diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000000..5ace4600a1f2 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2fc36f1b3cf2..ade68f9762f2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,6 +3,7 @@ name: Scala Merge CI on: push: branches: ['2.*.x'] + workflow_dispatch: defaults: run: @@ -18,25 +19,34 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest] - java: [8, 11, 17] + java-distribution: [temurin] + java: [8, 11, 17, 20] + # 21-ea will presumably be available from Temurin eventually, but for now: + include: + - os: ubuntu-latest + java-distribution: zulu + java: 21-ea + - os: windows-latest + java-distribution: zulu + java: 21-ea runs-on: ${{matrix.os}} steps: - run: git config --global core.autocrlf false - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup Java uses: actions/setup-java@v3 with: - distribution: temurin + distribution: ${{matrix.java-distribution}} java-version: ${{matrix.java}} cache: sbt - name: Build run: | - sbt setupPublishCore generateBuildCharacterPropertiesFile headerCheck publishLocal + sbt -Dsbt.scala.version=2.12.18-M2 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 + sbt -Dsbt.scala.version=2.12.18-M2 -Dstarr.version=$STARR setupValidateTest test:compile info testAll diff --git a/.idea/icon.png b/.idea/icon.png new file mode 100644 index 000000000000..8280fd4bfc3f Binary files /dev/null and b/.idea/icon.png differ diff --git a/.mailmap b/.mailmap index 815b48ad3c20..6bb681d917ad 100644 --- a/.mailmap +++ b/.mailmap @@ -24,16 +24,19 @@ Christopher Vogt Damien Obristi Daniel C. Sobral Daniel C. Sobral +Daniel Esik Daniel Lorch Darcy Shen Diego E. Alonso Blas Diego E. Alonso Blas +Eric Huang Erik Stenman Eugene Burmako Eugene Burmako Eugene Vigdorchik François Garillot Geoff Reedy +Gilad Hoch Harrison Houghton Ilya Sergei Ingo Maier @@ -45,6 +48,8 @@ Josh Suereth Josh Suereth Julien Eberle Kenji Yoshida <6b656e6a69@gmail.com> +Liang Yan <35164941+liang3zy22@users.noreply.github.com> +Liang Yan Luc Bourlier Luc Bourlier Luc Bourlier @@ -90,3 +95,6 @@ Vincent Cremet Vladimir Nikolaev Vojin Jovanovic Vojin Jovanovic +Zhang Zhipeng +jxnu-liguobin +philwalk diff --git a/.scala-steward.conf b/.scala-steward.conf index 3b05785b80cb..17ba56cee95c 100644 --- a/.scala-steward.conf +++ b/.scala-steward.conf @@ -2,17 +2,22 @@ # 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"} ] +updates.ignore = [ -# Ant support is deprecated, so leave the version where it is -updates.ignore = [ { groupId = "org.apache.ant"} ] + # 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 + { groupId = "com.fasterxml.jackson.core" }, + { groupId = "com.fasterxml.jackson.dataformat" }, + { groupId = "org.slf4j" }, + { groupId = "org.eclipse.jgit" }, + { groupId = "org.openjdk.jol" }, -# OSGi stuff is fragile and we suspect it is little-used, -# so let's prefer stability -updates.ignore = [ { groupId = "biz.aQute.bnd"} ] + # Ant support is deprecated, so leave the version where it is + { groupId = "org.apache.ant" }, + + # OSGi stuff is fragile and we suspect it is little-used, + # so let's prefer stability + { groupId = "biz.aQute.bnd" } + +] diff --git a/.travis.yml b/.travis.yml index 7da7ef852558..bfdc8a069387 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,9 +27,9 @@ templates: # this has no effect on travis, it's just a place to put our template name: build, publishLocal, build again script: - set -e - - sbt setupPublishCore generateBuildCharacterPropertiesFile headerCheck publishLocal + - sbt -Dsbt.scala.version=2.12.18-M2 setupPublishCore generateBuildCharacterPropertiesFile headerCheck publishLocal - STARR=$(sed -n 's/^maven\.version\.number=//p' buildcharacter.properties) && echo $STARR - - sbt -Dstarr.version=$STARR setupValidateTest compile + - sbt -Dsbt.scala.version=2.12.18-M2 -Dstarr.version=$STARR setupValidateTest compile workspaces: create: name: bootstrapped @@ -53,7 +53,7 @@ templates: # this has no effect on travis, it's just a place to put our template script: - set -e - STARR=$(sed -n 's/^maven\.version\.number=//p' buildcharacter.properties) && echo $STARR - - sbt -Dstarr.version=$STARR setupValidateTest Test/compile testAll1 + - sbt -Dsbt.scala.version=2.12.18-M2 -Dstarr.version=$STARR setupValidateTest Test/compile testAll1 test2: &test2 stage: test @@ -63,7 +63,7 @@ templates: # this has no effect on travis, it's just a place to put our template script: - set -e - STARR=$(sed -n 's/^maven\.version\.number=//p' buildcharacter.properties) && echo $STARR - - sbt -Dstarr.version=$STARR setupValidateTest testAll2 + - sbt -Dsbt.scala.version=2.12.18-M2 -Dstarr.version=$STARR setupValidateTest testAll2 jobs: include: @@ -113,7 +113,7 @@ jobs: script: - set -e - STARR=$(sed -n 's/^maven\.version\.number=//p' buildcharacter.properties) && echo $STARR - - sbt -Dscala.build.compileWithDotty=true library/compile + - sbt -Dsbt.scala.version=2.12.18-M2 -Dscala.build.compileWithDotty=true library/compile - name: build benchmarks if: type = pull_request OR repo != scala/scala @@ -122,7 +122,7 @@ jobs: script: - set -e - STARR=$(sed -n 's/^maven\.version\.number=//p' buildcharacter.properties) && echo $STARR - - sbt bench/Jmh/compile + - sbt -Dsbt.scala.version=2.12.18-M2 bench/Jmh/compile - stage: build if: type = pull_request OR type = push @@ -134,7 +134,7 @@ jobs: - ruby -v - gem install bundler - bundler --version - - bundle install + - bundle install --path vendor/bundle # 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 @@ -158,8 +158,8 @@ env: - 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 + - secure: "PbDzgRGivsDM/1P18dIAZiZnK8yG+fxU/9Ho6DkAd8pvsu7S08MPks+ekM0uSVeKxYj7Npzd3XTe4weEXM7Jtljy3CRHoPasI0TF/6ZVOb7H+MMP1cg9K1xrZXKfEk2RABCbMxKtrEv9BDa/lVtjCCEKWAIPz38Z6q2mKk417Ps=" # SONA_USER, token username for publishing to Sonatype + - secure: "D/V5nrAJsAc6t5ZMoeSt37ViIsJyRmagA286M3zWn/uZhgk4mbgYfzu6rDbYeUTBB9jX8YHKPtzUrxqcnlpkV8z6USAbDhzYSLL/QqcLnTjKZZ3KvPEimNQIXX8Nb1KIrlXNQ/xTE8u+GNvQLDdxa60QqlzvA3tt5vnVl3GatFE=" # SONA_PASS, token password for publishing to Sonatype # caching for sdkman / sbt / ivy / coursier imported from scala-dev cache: diff --git a/Gemfile b/Gemfile index d37ec91782f6..b248ccc91835 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,10 @@ # To build the spec on Travis CI source "https://rubygems.org" -gem "jekyll", "3.6.3" +gem "jekyll", "3.9.3" gem "rouge" -# gem 's3_website' -gem "redcarpet", "3.5.1" +gem "redcarpet", "3.6.0" + +# we use redcarpet not kramdown, but current jekyll complains +# if this isn't present?! +gem 'kramdown-parser-gfm' diff --git a/NOTICE b/NOTICE index 611c177829c1..5bceee7b366a 100644 --- a/NOTICE +++ b/NOTICE @@ -1,6 +1,6 @@ Scala -Copyright (c) 2002-2022 EPFL -Copyright (c) 2011-2022 Lightbend, Inc. +Copyright (c) 2002-2023 EPFL +Copyright (c) 2011-2023 Lightbend, Inc. Scala includes software developed at LAMP/EPFL (https://lamp.epfl.ch/) and diff --git a/README.md b/README.md index 2eaf532d97d6..ff367a04dc32 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ This is the home of the [Scala 2](https://www.scala-lang.org) standard library, compiler, and language spec. +If you want to visit the Scala 3 repository, go to the [lampepfl/dotty](https://github.com/lampepfl/dotty). + # How to contribute Issues and bug reports for Scala 2 are located in [scala/bug](https://github.com/scala/bug). That tracker is also where new contributors may find issues to work on: [good first issues](https://github.com/scala/bug/labels/good%20first%20issue), [help wanted](https://github.com/scala/bug/labels/help%20wanted). @@ -38,6 +40,7 @@ If you need some help with your PR at any time, please feel free to @-mention an | [`@retronym`](https://github.com/retronym) | 2.12.x branch, compiler performance, weird compiler bugs, lambdas | | [`@SethTisue`](https://github.com/SethTisue) | getting started, build, CI, community build, Jenkins, docs, library, REPL | | [`@dwijnand`](https://github.com/dwijnand) | pattern matcher, MiMa, partest | + | [`@som-snytt`](https://github.com/som-snytt) | warnings/lints/errors, REPL, compiler options, compiler internals, partest | | [`@Ichoran`](https://github.com/Ichoran) | collections library, performance | | [`@viktorklang`](https://github.com/viktorklang) | concurrency, futures | | [`@sjrd`](https://github.com/sjrd) | interactions with Scala.js | diff --git a/build.sbt b/build.sbt index 9d497effc816..e2739024c148 100644 --- a/build.sbt +++ b/build.sbt @@ -37,7 +37,7 @@ import scala.build._, VersionUtil._ // Non-Scala dependencies: val junitDep = "junit" % "junit" % "4.13.2" val junitInterfaceDep = "com.github.sbt" % "junit-interface" % "0.13.3" % Test -val scalacheckDep = "org.scalacheck" %% "scalacheck" % "1.15.4" % Test +val scalacheckDep = "org.scalacheck" %% "scalacheck" % "1.17.0" % 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") @@ -73,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.10" +Global / baseVersion := "2.13.11" Global / baseVersionSuffix := "SNAPSHOT" ThisBuild / organization := "org.scala-lang" ThisBuild / homepage := Some(url("https://www.scala-lang.org")) @@ -135,6 +135,12 @@ lazy val commonSettings = instanceSettings ++ clearSourceAndResourceDirectories compileOrder := CompileOrder.JavaThenScala, projectFolder := thisProject.value.id, // overridden in configureAsSubproject Compile / javacOptions ++= Seq("-g", "-source", "1.8", "-target", "1.8", "-Xlint:unchecked"), + Compile / javacOptions ++= ( + if (scala.util.Properties.isJavaAtLeast("20")) + Seq("-Xlint:-options") // allow `-source 1.8` and `-target 1.8` + else + Seq()), + 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), diff --git a/doc/LICENSE.md b/doc/LICENSE.md index 1cb1641772c7..0e13c1126947 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-2022 EPFL +Copyright (c) 2002-2023 EPFL -Copyright (c) 2011-2022 Lightbend, Inc. +Copyright (c) 2011-2023 Lightbend, Inc. All rights reserved. diff --git a/doc/License.rtf b/doc/License.rtf index f54f532da402..57ed3c3e4280 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-2022 EPFL\ -Copyright (c) 2011-2022 Lightbend, Inc.\ +\f0\b0\fs28 \cf2 Copyright (c) 2002-2023 EPFL\ +Copyright (c) 2011-2023 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/project/DottySupport.scala b/project/DottySupport.scala index 5fb86c005d87..929b5ac73ddc 100644 --- a/project/DottySupport.scala +++ b/project/DottySupport.scala @@ -5,14 +5,14 @@ import sbt.Keys._ import java.io.File import sbt.librarymanagement.{ - ivy, DependencyResolution, ScalaModuleInfo, UpdateConfiguration, UnresolvedWarningConfiguration + DependencyResolution, ScalaModuleInfo, UpdateConfiguration, UnresolvedWarningConfiguration } /** * Settings to support validation of TastyUnpickler against the release of dotty with the matching TASTy version */ object TastySupport { - val supportedTASTyRelease = "3.2.0" // TASTy version 28.2-0 + val supportedTASTyRelease = "3.3.1-RC1" // TASTy version 28.4-1 val scala3Compiler = "org.scala-lang" % "scala3-compiler_3" % supportedTASTyRelease val scala3Library = "org.scala-lang" % "scala3-library_3" % supportedTASTyRelease @@ -26,9 +26,9 @@ object TastySupport { * Dotty in .travis.yml. */ object DottySupport { - val dottyVersion = "3.2.0" + val dottyVersion = "3.3.1-RC1" val compileWithDotty: Boolean = - Option(System.getProperty("scala.build.compileWithDotty")).map(_.toBoolean).getOrElse(false) + Option(System.getProperty("scala.build.compileWithDotty")).exists(_.toBoolean) lazy val commonSettings = Seq( Compile / scalacOptions ++= Seq( "-language:implicitConversions" // Avoid a million warnings @@ -41,7 +41,7 @@ object DottySupport { // 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 withoutFatalWarnings = old.filterNot(opt => opt == "-Werror" || opt.startsWith("-Wconf")) val (beforeSourcepath, "-sourcepath" :: oldSourcepath :: afterSourcePath) = withoutFatalWarnings.span(_ != "-sourcepath") @@ -68,10 +68,8 @@ object DottySupport { Compile / sourceGenerators += Def.task { object DottyLibrarySourceFilter extends FileFilter { def accept(file: File): Boolean = { - val name = file.name - val path = file.getCanonicalPath - file.isFile && - (path.endsWith(".scala") || path.endsWith(".java")) + val name = file.getName + file.isFile && (name.endsWith(".scala") || name.endsWith(".java")) } } diff --git a/project/GenerateFunctionConverters.scala b/project/GenerateFunctionConverters.scala index d1fbd334420e..52e02adfbb41 100644 --- a/project/GenerateFunctionConverters.scala +++ b/project/GenerateFunctionConverters.scala @@ -341,7 +341,7 @@ object GenerateFunctionConverters { def sourceFile(subPack: String, body: String): String = s"""$copyright | - |${packaging}${subPack} + |$packaging$subPack | |$body |""".stripMargin diff --git a/project/JitWatch.scala b/project/JitWatch.scala index d14c43765102..08b2c03eba0d 100644 --- a/project/JitWatch.scala +++ b/project/JitWatch.scala @@ -58,7 +58,7 @@ object JitWatchFilePlugin extends AutoPlugin { // Download and add transitive sources from the classpath val classiferArtifacts: Seq[(ModuleID, Artifact, File)] = updateClassifiers.value.configurations.flatMap(_.details.flatMap(_.modules.flatMap(report => report.artifacts.map(x => (report.module, x._1, x._2))))) - val sourceClassiferArtifacts = classiferArtifacts.filter(tuple => tuple._2.classifier == Some("sources") && dependencyModuleIds.contains(tuple._1)) + val sourceClassiferArtifacts = classiferArtifacts.filter(tuple => tuple._2.classifier.contains("sources") && dependencyModuleIds.contains(tuple._1)) val externalSources = sourceClassiferArtifacts.map(_._3) val internalAndExternalSources = sourceDirectories.value ++ (javaHomeSrc +: (transitiveSourceDirectories ++ transitiveSourceDirectories2).distinct) ++ externalSources diff --git a/project/MimaFilters.scala b/project/MimaFilters.scala index bc5b70009729..f2cf65569fc3 100644 --- a/project/MimaFilters.scala +++ b/project/MimaFilters.scala @@ -13,7 +13,7 @@ object MimaFilters extends AutoPlugin { import autoImport._ override val globalSettings = Seq( - mimaReferenceVersion := Some("2.13.9"), + mimaReferenceVersion := Some("2.13.10"), ) val mimaFilters: Seq[ProblemFilter] = Seq[ProblemFilter]( @@ -31,6 +31,23 @@ object MimaFilters extends AutoPlugin { ProblemFilters.exclude[DirectMissingMethodProblem]("scala.Predef#ArrayCharSequence.isEmpty"), ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.ArrayCharSequence.isEmpty"), + // KEEP: make use of CompletionStage#handle to get a better performance than CompletionStage#whenComplete. + ProblemFilters.exclude[MissingTypesProblem]("scala.concurrent.impl.FutureConvertersImpl$P"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.concurrent.impl.FutureConvertersImpl#P.andThen"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.concurrent.impl.FutureConvertersImpl#P.apply"), + ProblemFilters.exclude[IncompatibleMethTypeProblem]("scala.concurrent.impl.FutureConvertersImpl#P.andThen"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.concurrent.impl.FutureConvertersImpl#P.accept"), + ProblemFilters.exclude[IncompatibleMethTypeProblem]("scala.concurrent.impl.FutureConvertersImpl#P.andThen"), + + // private class used by methods + ProblemFilters.exclude[MissingClassProblem]("scala.collection.IterableOnceOps$Maximized"), + + // private object used by methods + ProblemFilters.exclude[MissingClassProblem]("scala.math.Numeric$BigDecimalIsConflicted$"), + + // PR 10235 + ProblemFilters.exclude[MissingClassProblem]("scala.collection.mutable.LinkedHashMap$LinkedHashMapIterator"), + ProblemFilters.exclude[MissingClassProblem]("scala.collection.mutable.LinkedHashSet$LinkedHashSetIterator"), ) override val buildSettings = Seq( diff --git a/project/Osgi.scala b/project/Osgi.scala index 4dade3aa03a4..e745872e76de 100644 --- a/project/Osgi.scala +++ b/project/Osgi.scala @@ -3,7 +3,7 @@ package scala.build import aQute.bnd.osgi.Builder import aQute.bnd.osgi.Constants._ import java.util.jar.Attributes -import sbt._ +import sbt.{License => _, _} import sbt.Keys._ import collection.JavaConverters._ import VersionUtil.versionProperties @@ -85,7 +85,7 @@ object Osgi { def resourceDirectoryRef(f: File) = (if (f.getName endsWith ".jar") "@" else "") + f.getAbsolutePath val includeRes = resourceDirectories.filter(_.exists).map(resourceDirectoryRef).mkString(",") - if (!includeRes.isEmpty) builder.setProperty(INCLUDERESOURCE, includeRes) + if (includeRes.nonEmpty) builder.setProperty(INCLUDERESOURCE, includeRes) builder.getProperties.asScala.foreach { case (k, v) => log.debug(s"bnd: $k: $v") } // builder.build is not thread-safe because it uses a static SimpleDateFormat. This ensures // that all calls to builder.build are serialized. diff --git a/project/ParserUtil.scala b/project/ParserUtil.scala index 311544d108c7..ea921031c89c 100644 --- a/project/ParserUtil.scala +++ b/project/ParserUtil.scala @@ -23,8 +23,8 @@ object ParserUtil { val preFile = if (prefixIsAbsolute) prefixFile else new File(base, prefix) val basePrefix = if (prefixIsAbsolute) "" else ensureSuffix(base.getPath, "/") def relativize(p: String) = p.stripPrefix(basePrefix) - def pathOf(f: File) = if (f.isDirectory() && !fileFilter.accept(f)) ensureSuffix(f.getPath, "/") else f.getPath - val finder = if (preFile.isDirectory()) { + def pathOf(f: File) = if (f.isDirectory && !fileFilter.accept(f)) ensureSuffix(f.getPath, "/") else f.getPath + val finder = if (preFile.isDirectory) { preFile.glob(childFilter) } else if (preFile.exists()) { PathFinder(preFile).filter(fileFilter.accept) diff --git a/project/PartestTestListener.scala b/project/PartestTestListener.scala index 83d1e82aefd8..f7df4ab3f148 100644 --- a/project/PartestTestListener.scala +++ b/project/PartestTestListener.scala @@ -1,7 +1,6 @@ package scala.build import java.io.{File, PrintWriter, StringWriter} -import java.util.concurrent.TimeUnit import sbt.testing.{SuiteSelector, TestSelector} import sbt.{JUnitXmlTestsListener, TestEvent, TestResult, TestsListener, _} @@ -47,7 +46,7 @@ class PartestTestListener(target: File) extends TestsListener { e.fullyQualifiedName() } - for ((group, events) <- event.detail.groupBy(groupOf(_))) { + for ((group, events) <- event.detail.groupBy(groupOf)) { val statii = events.map(_.status()) val errorCount = statii.count(errorStatus.contains) val failCount = statii.count(failStatus.contains) @@ -95,7 +94,7 @@ class PartestTestListener(target: File) extends TestsListener { }} val partestTestReports = target / "test-reports" / "partest" - val xmlFile = (partestTestReports / (group + ".xml")) + val xmlFile = partestTestReports / (group + ".xml") xmlFile.getParentFile.mkdirs() scala.xml.XML.save(xmlFile.getAbsolutePath, xml, "UTF-8", true, null) } diff --git a/project/PartestUtil.scala b/project/PartestUtil.scala index dfe8819a7a6f..672b70a60fa7 100644 --- a/project/PartestUtil.scala +++ b/project/PartestUtil.scala @@ -10,7 +10,7 @@ object PartestUtil { val srcDir = testBase / srcPath // mirror of partest.nest.PathSettings#srcDir private val testCaseFile = GlobFilter("*.scala") | GlobFilter("*.java") | GlobFilter("*.res") - private val testCaseDir = new SimpleFileFilter(f => f.isDirectory() && f.listFiles().nonEmpty && !(f.getParentFile / (f.getName + ".res")).exists()) + private val testCaseDir = new SimpleFileFilter(f => f.isDirectory && f.listFiles().nonEmpty && !(f.getParentFile / (f.getName + ".res")).exists()) private val testCaseFilter = testCaseFile || testCaseDir private val testCaseFinder = srcDir * AllPassFilter * testCaseFilter @@ -36,7 +36,8 @@ object PartestUtil { val knownUnaryOptions = List( "--pos", "--neg", "--run", "--jvm", "--res", "--ant", "--scalap", "--specialized", "--instrumented", "--presentation", "--failed", "--update-check", "--no-exec", - "--show-diff", "--show-log", "--verbose", "--terse", "--debug", "--version", "--help") + "--show-diff", "--show-log", "--verbose", "--terse", "--debug", "--realeasy", "--branch", "--version", + "--help") val srcPathOption = "--srcpath" val compilerPathOption = "--compilerpath" val grepOption = "--grep" diff --git a/project/SavedLogs.scala b/project/SavedLogs.scala index 27e1277da670..4ec335f4b2bf 100644 --- a/project/SavedLogs.scala +++ b/project/SavedLogs.scala @@ -1,10 +1,10 @@ package scala.build -import java.io.{ByteArrayOutputStream, PrintStream, StringWriter} +import java.io.{ByteArrayOutputStream, PrintStream} import sbt._ import Keys._ -import sbt.internal.util.{ConsoleAppender, StringEvent } +import sbt.internal.util.ConsoleAppender import scala.collection.mutable /** Save MiMa logs so they don't get lost in lots of debug output */ diff --git a/project/ScalaOptionParser.scala b/project/ScalaOptionParser.scala index 89d11246b11f..aaa769ddbea6 100644 --- a/project/ScalaOptionParser.scala +++ b/project/ScalaOptionParser.scala @@ -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 19).map(_.toString).flatMap(v => v :: s"jvm-1.$v" :: s"jvm-$v" :: s"1.$v" :: Nil).toList + private val targetSettingNames = (8 to 21).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 125b4836839f..171cd6037c12 100644 --- a/project/ScaladocSettings.scala +++ b/project/ScaladocSettings.scala @@ -7,7 +7,7 @@ object ScaladocSettings { // when this changes, the integrity check in HtmlFactory.scala also needs updating val webjarResources = Seq( - "org.webjars" % "jquery" % "3.6.0" + "org.webjars" % "jquery" % "3.6.4" ) def extractResourcesFromWebjar = Def.task { diff --git a/project/VersionUtil.scala b/project/VersionUtil.scala index b9f21ad180c8..f177278ee5db 100644 --- a/project/VersionUtil.scala +++ b/project/VersionUtil.scala @@ -7,8 +7,7 @@ import java.util.{Date, Locale, Properties, TimeZone} 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 java.time.format.DateTimeFormatter.ISO_DATE_TIME import scala.collection.JavaConverters._ import BuildSettings.autoImport._ @@ -30,7 +29,7 @@ object VersionUtil { ) lazy val generatePropertiesFileSettings = Seq[Setting[_]]( - copyrightString := "Copyright 2002-2022, LAMP/EPFL and Lightbend, Inc.", + copyrightString := "Copyright 2002-2023, LAMP/EPFL and Lightbend, Inc.", shellBannerString := """ | ________ ___ / / ___ | / __/ __// _ | / / / _ | @@ -69,8 +68,8 @@ object VersionUtil { val (dateObj, sha) = { try { // Use JGit to get the commit date and SHA - import org.eclipse.jgit.storage.file.FileRepositoryBuilder import org.eclipse.jgit.revwalk.RevWalk + import org.eclipse.jgit.storage.file.FileRepositoryBuilder val db = new FileRepositoryBuilder().findGitDir.build val head = db.resolve("HEAD") if (head eq null) { @@ -79,9 +78,7 @@ object VersionUtil { // Workaround lack of git worktree support in JGit https://bugs.eclipse.org/bugs/show_bug.cgi?id=477475 val sha = List("git", "rev-parse", "HEAD").!!.trim val commitDateIso = List("git", "log", "-1", "--format=%cI", "HEAD").!!.trim - val date = java.util.Date.from(DateTimeFormatter.ISO_DATE_TIME.parse(commitDateIso, new TemporalQuery[Instant] { - override def queryFrom(temporal: TemporalAccessor): Instant = Instant.from(temporal) - })) + val date = Date.from(ISO_DATE_TIME.parse(commitDateIso, Instant.from _)) (date, sha.substring(0, 7)) } catch { case ex: Exception => @@ -130,7 +127,7 @@ object VersionUtil { val (base, suffix) = { val (b, s) = (baseVersion.value, baseVersionSuffix.value) if(s == "SPLIT") { - val split = """([\w+\.]+)(-[\w+\.-]+)??""".r + val split = """([\w+.]+)(-[\w+.-]+)??""".r val split(b2, sOrNull) = b (b2, Option(sOrNull).map(_.drop(1)).getOrElse("")) } else (b, s) diff --git a/project/build.properties b/project/build.properties index 22af2628c413..72413de151e1 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.7.1 +sbt.version=1.8.3 diff --git a/project/genprod.scala b/project/genprod.scala index 1d37ee74cddf..565281847084 100644 --- a/project/genprod.scala +++ b/project/genprod.scala @@ -217,7 +217,7 @@ class Function(val i: Int) extends Group("Function") with Arity { * object Main extends App {%s} * }}}""" - def toStr() = "\"" + ("" format i) + "\"" + def toStr = "\"" + ("" format i) + "\"" def apply() = { {header} {companionObject} @@ -392,7 +392,7 @@ class Product(val i: Int) extends Group("Product") with Arity { * * @param n number of the projection to be returned * @return same as `._(n+1)`, for example `productElement(0)` is the same as `._1`. - * @throws IndexOutOfBoundsException if the `n` is out of range(n < 0 || n >= ${i}). + * @throws IndexOutOfBoundsException if the `n` is out of range(n < 0 || n >= $i). */ """ diff --git a/project/plugins.sbt b/project/plugins.sbt index 5499c38f2373..52c782e832bd 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -17,7 +17,7 @@ buildInfoKeys := Seq[BuildInfoKey](buildClasspath) buildInfoPackage := "scalabuild" -addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.0") +addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.2") libraryDependencies ++= Seq( "org.eclipse.jgit" % "org.eclipse.jgit" % "4.11.9.201909030838-r", @@ -29,6 +29,6 @@ Global / concurrentRestrictions := Seq( Tags.limitAll(1) // workaround for https://github.com/sbt/sbt/issues/2970 ) -addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.7.0") +addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.9.0") -addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.3") +addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.4") diff --git a/project/project/plugins.sbt b/project/project/plugins.sbt index b8bfe1262e80..71982a81514a 100644 --- a/project/project/plugins.sbt +++ b/project/project/plugins.sbt @@ -1 +1 @@ -addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.10.0") +addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.11.0") diff --git a/scripts/common b/scripts/common index e00731fcc01b..2dd91be08b08 100644 --- a/scripts/common +++ b/scripts/common @@ -13,7 +13,7 @@ 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 $SBT_VERSION" +SBT_CMD="$SBT_CMD -Dsbt.scala.version=2.12.18-M2 -sbt-version $SBT_VERSION" # repo to publish builds integrationRepoUrl=${integrationRepoUrl-"https://scala-ci.typesafe.com/artifactory/scala-integration/"} diff --git a/spec/01-lexical-syntax.md b/spec/01-lexical-syntax.md index 921384b40e20..36f6daaa989d 100644 --- a/spec/01-lexical-syntax.md +++ b/spec/01-lexical-syntax.md @@ -8,6 +8,11 @@ chapter: 1 Scala source code consists of Unicode text. +The nine [Bidirectional explicit formatting](https://www.unicode.org/reports/tr9/#Bidirectional_Character_Types) +characters `\u202a - \u202e` and `\u2066 - \u2069` (inclusive) are forbidden +from appearing in source files. Note that they can be represented using +unicode escapes in string and character literals. + 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_. diff --git a/spec/02-identifiers-names-and-scopes.md b/spec/02-identifiers-names-and-scopes.md index b8bde8cfd1a9..213c2bee96df 100644 --- a/spec/02-identifiers-names-and-scopes.md +++ b/spec/02-identifiers-names-and-scopes.md @@ -7,23 +7,29 @@ chapter: 2 # Identifiers, Names and Scopes Names in Scala identify types, values, methods, and classes which are -collectively called _entities_. Names are introduced by local +collectively called _entities_. Names are introduced by [definitions and declarations](04-basic-declarations-and-definitions.html#basic-declarations-and-definitions), [inheritance](05-classes-and-objects.html#class-members), [import clauses](04-basic-declarations-and-definitions.html#import-clauses), or [package clauses](09-top-level-definitions.html#packagings) which are collectively called _bindings_. -Bindings of different kinds have precedence defined on them: +Bindings of each kind are assigned a precedence which determines +whether one binding can shadow another: + 1. Definitions and declarations that are local, inherited, or made available by a package clause and also defined in the same compilation unit as the reference to them, have the highest precedence. 1. Explicit imports have the next highest precedence. 1. Wildcard imports have the next highest precedence. -1. Definitions made available by a package clause, but not also defined in the - same compilation unit as the reference to them, as well as imports which - are supplied by the compiler but not explicitly written in source code, +1. Bindings made available by a package clause, + but not also defined in the same compilation unit as the reference to them, + as well as bindings supplied by the compiler but not explicitly written in source code, have the lowest precedence. There are two different name spaces, one for [types](03-types.html#types) @@ -72,8 +78,8 @@ In particular, imported names have higher precedence than names, defined in othe that might otherwise be visible because they are defined in either the current package or an enclosing package. -Note that a package definition is taken as lowest precedence, since packages -are open and can be defined across arbitrary compilation units. +Note that a binding introduced by a packaging is taken as lowest precedence, +since packages are open and can be defined across arbitrary compilation units. ```scala package util { @@ -85,42 +91,37 @@ package util { } ``` -The compiler supplies imports in a preamble to every source file. This preamble -conceptually has the following form, where braces indicate nested scopes: +The compiler supplies bindings from well-known packages and objects, called "root contexts". +The standard locations for these bindings are: -```scala -import java.lang._ -{ - import scala._ - { - import Predef._ - { /* source */ } - } -} -``` +1. The object `scala.Predef`. +1. The package `scala`. +1. The package `java.lang`. -These imports are taken as lowest precedence, so that they are always shadowed +These bindings are taken as lowest precedence, so that they are always shadowed by user code, which may contain competing imports and definitions. -They also increase the nesting depth as shown, so that later imports -shadow earlier ones. -As a convenience, multiple bindings of a type identifier to the same -underlying type is permitted. This is possible when import clauses introduce -a binding of a member type alias with the same binding precedence, typically -through wildcard imports. This allows redundant type aliases to be imported -without introducing an ambiguity. +A binding is available from a root context if it would also be available +using an ordinary import clause. In particular, ordinary access restrictions apply. + +A binding from an earlier root context shadows a binding of the same name from a later one. +For example, `scala.Predef.String` shadows `java.lang.String`, for which it is a type alias. + +Multiple binding of a type identifier to the same underlying type is permitted. +This is possible when import clauses introduce a binding of a member type alias +with the same binding precedence, typically through wildcard imports. +This allows redundant type aliases to be imported without introducing an ambiguity. ```scala object X { type T = annotation.tailrec } object Y { type T = annotation.tailrec } object Z { - import X._, Y._, annotation.{tailrec => T} // OK, all T mean tailrec - @T def f: Int = { f ; 42 } // error, f is not tail recursive + import X._, Y._ // OK, both T mean tailrec + @T def f: Int = { f ; 42 } // the annotation worked: error, f is not tail recursive } ``` -Similarly, imported aliases of names introduced by package statements are -allowed, even though the names are strictly ambiguous: +Similarly, imported aliases of names introduced by package statements are permitted: ```scala // c.scala @@ -128,16 +129,9 @@ package p { class C } // xy.scala import p._ -package p { class X extends C } -package q { class Y extends C } +package p { class X extends C } // not ambiguous (compiles without the import) +package q { class Y extends C } // requires the import ``` - -The reference to `C` in the definition of `X` is strictly ambiguous -because `C` is available by virtue of the package clause in -a different file, and can't shadow the imported name. But because -the references are the same, the definition is taken as though it -did shadow the import. - ###### Example Assume the following two definitions of objects named `X` in packages `p` and `q` diff --git a/spec/05-classes-and-objects.md b/spec/05-classes-and-objects.md index 5c3a74e608a2..c83de4a2836d 100644 --- a/spec/05-classes-and-objects.md +++ b/spec/05-classes-and-objects.md @@ -332,13 +332,13 @@ Furthermore, the following restrictions on modifiers apply to ´M´ and - ´M'´ must not be labeled `final`. - ´M´ must not be [`private`](#modifiers). -- If ´M´ is labeled `private[´C´]` for some enclosing class or package ´C´, - then ´M'´ must be labeled `private[´C'´]` for some class or package ´C'´ where - ´C'´ equals ´C´ or ´C'´ is contained in ´C´. - - - If ´M´ is labeled `protected`, then ´M'´ must also be labeled `protected`. +- If ´M´ is labeled `private[´C´]` (respectively `protected[´C´]`) + for some enclosing class or package ´C´, + then ´M'´ must be labeled `private[´C'´]` (or, respectively, `protected[´C'´]`) + for some class or package ´C'´ where + ´C'´ equals ´C´ or the companion of ´C´, or ´C'´ is contained in ´C´. - If ´M'´ is not an abstract member, then ´M´ must be labeled `override`. Furthermore, one of two possibilities must hold: - either ´M´ is defined in a subclass of the class where is ´M'´ is defined, diff --git a/spec/06-expressions.md b/spec/06-expressions.md index f574d7ad2469..cb6614baef4c 100644 --- a/spec/06-expressions.md +++ b/spec/06-expressions.md @@ -416,9 +416,10 @@ Otherwise, `´U´` is the expected type at the call site. If the expected type i ###### Note -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*`. +On the Java platform version 11 and later, a method is signature polymorphic if it is native, +a member of `java.lang.invoke.MethodHandle` or `java.lang.invoke.VarHandle`, and has a single +repeated parameter of type `java.lang.Object*`. (These requirements also work for Java 8, +which had fewer such methods.) ## Method Values @@ -1181,8 +1182,8 @@ for `try { try { ´b´ } catch ´e_1´ } finally ´e_2´`. ## Anonymous Functions ```ebnf -Expr ::= (Bindings | [‘implicit’] id | ‘_’) ‘=>’ Expr -ResultExpr ::= (Bindings | ([‘implicit’] id | ‘_’) ‘:’ CompoundType) ‘=>’ Block +Expr ::= (Bindings | [‘implicit’] (id | ‘_’)) ‘=>’ Expr +ResultExpr ::= (Bindings | [‘implicit’] (id | ‘_’) [‘:’ CompoundType]) ‘=>’ Block Bindings ::= ‘(’ Binding {‘,’ Binding} ‘)’ Binding ::= (id | ‘_’) [‘:’ Type] ``` @@ -1526,7 +1527,9 @@ question: given - A parameterized method ´m´ of type `(´p_1:T_1, \ldots , p_n:T_n´)´U´` is _as specific as_ some other member ´m'´ of type ´S´ if ´m'´ is [applicable](#function-applications) - to arguments `(´p_1 , \ldots , p_n´)` of types ´T_1 , \ldots , T_n´. + to arguments `(´p_1 , \ldots , p_n´)` of types ´T_1 , \ldots , T_last´; + if ´T_n´ denotes a repeated parameter (it has shape ´T*´), and so does ´m'´'s last parameter, + ´T_last´ is taken as ´T´, otherwise ´T_n´ is used directly. - A polymorphic method of type `[´a_1´ >: ´L_1´ <: ´U_1 , \ldots , a_n´ >: ´L_n´ <: ´U_n´]´T´` is as specific as some other member of type ´S´ if ´T´ is as specific as ´S´ under the assumption that for ´i = 1 , \ldots , n´ each ´a_i´ is an abstract type name diff --git a/spec/13-syntax-summary.md b/spec/13-syntax-summary.md index 6ece538e2ff1..6e225e177729 100644 --- a/spec/13-syntax-summary.md +++ b/spec/13-syntax-summary.md @@ -8,6 +8,11 @@ chapter: 13 The following descriptions of Scala tokens uses literal characters `‘c’` when referring to the ASCII fragment `\u0000` – `\u007F`. +The nine [Bidirectional explicit formatting](https://www.unicode.org/reports/tr9/#Bidirectional_Character_Types) +characters `\u202a - \u202e` and `\u2066 - \u2069` (inclusive) are forbidden +from appearing in source files. Note that they can be represented using +unicode escapes in string and character literals. + ## Lexical Syntax The lexical syntax of Scala is given by the following grammar in EBNF form: @@ -139,7 +144,7 @@ grammar: | ‘:’ Annotation {Annotation} | ‘:’ ‘_’ ‘*’ - Expr ::= (Bindings | [‘implicit’] id | ‘_’) ‘=>’ Expr + Expr ::= (Bindings | [‘implicit’] (id | ‘_’)) ‘=>’ Expr | Expr1 Expr1 ::= ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[semi] ‘else’ Expr] | ‘while’ ‘(’ Expr ‘)’ {nl} Expr @@ -183,7 +188,7 @@ grammar: | Expr1 | ResultExpr ::= Expr1 - | (Bindings | ([‘implicit’] id | ‘_’) ‘:’ CompoundType) ‘=>’ Block + | (Bindings | [‘implicit’] (id | ‘_’) [‘:’ CompoundType]) ‘=>’ Block Enumerators ::= Generator {semi Generator} Generator ::= [‘case’] Pattern1 ‘<-’ Expr {[semi] Guard | semi Pattern1 ‘=’ Expr} diff --git a/src/compiler/scala/reflect/macros/contexts/Evals.scala b/src/compiler/scala/reflect/macros/contexts/Evals.scala index fbff3a2fb8fe..d781dfce5198 100644 --- a/src/compiler/scala/reflect/macros/contexts/Evals.scala +++ b/src/compiler/scala/reflect/macros/contexts/Evals.scala @@ -24,8 +24,14 @@ trait Evals { private lazy val evalImporter = ru.internal.createImporter(universe).asInstanceOf[ru.Importer { val from: universe.type }] def eval[T](expr: Expr[T]): T = { + def specialK(x: Any) = + x match { + case _: global.TypeRef => true + case _: global.Symbol => true + case _ => false + } expr.tree match { - case global.Literal(global.Constant(value)) => + case global.Literal(global.Constant(value)) if !specialK(value) => value.asInstanceOf[T] case _ => val imported = evalImporter.importTree(expr.tree) diff --git a/src/compiler/scala/reflect/reify/Errors.scala b/src/compiler/scala/reflect/reify/Errors.scala index 012eca623c19..215633708961 100644 --- a/src/compiler/scala/reflect/reify/Errors.scala +++ b/src/compiler/scala/reflect/reify/Errors.scala @@ -10,10 +10,12 @@ * additional information regarding copyright ownership. */ -package scala.reflect.reify +package scala.reflect +package reify -import scala.reflect.macros.ReificationException -import scala.reflect.macros.UnexpectedReificationException +import internal.util.StringContextStripMarginOps +import macros.ReificationException +import macros.UnexpectedReificationException trait Errors { self: Reifier => diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala index d24ab4b6c3d4..fe971938438e 100644 --- a/src/compiler/scala/tools/nsc/CompilationUnits.scala +++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala @@ -14,8 +14,7 @@ package scala.tools.nsc import scala.annotation.nowarn import scala.reflect.internal.util.{FreshNameCreator, NoSourceFile, SourceFile} -import scala.collection.mutable -import scala.collection.mutable.{LinkedHashSet, ListBuffer} +import scala.collection.mutable, mutable.ArrayDeque trait CompilationUnits { global: Global => @@ -127,7 +126,9 @@ trait CompilationUnits { global: Global => val transformed = new mutable.AnyRefMap[Tree, Tree] /** things to check at end of compilation unit */ - val toCheck = new ListBuffer[() => Unit] + val toCheck = ArrayDeque.empty[CompilationUnit.ToCheck] + private[nsc] def addPostUnitCheck(check: CompilationUnit.ToCheckAfterUnit): Unit = toCheck.append(check) + private[nsc] def addPostTyperCheck(check: CompilationUnit.ToCheckAfterTyper): Unit = toCheck.append(check) /** The features that were already checked for this unit */ var checkedFeatures = Set[Symbol]() @@ -142,11 +143,17 @@ trait CompilationUnits { global: Global => def targetPos: Position = NoPosition /** For sbt compatibility (https://github.com/scala/scala/pull/4588) */ - val icode: LinkedHashSet[icodes.IClass] = new LinkedHashSet + val icode: mutable.LinkedHashSet[icodes.IClass] = new mutable.LinkedHashSet /** Is this about a .java source file? */ val isJava: Boolean = source.isJava override def toString() = source.toString() } + + object CompilationUnit { + sealed trait ToCheck { def apply(): Unit } + trait ToCheckAfterUnit extends ToCheck + trait ToCheckAfterTyper extends ToCheck + } } diff --git a/src/compiler/scala/tools/nsc/CompilerCommand.scala b/src/compiler/scala/tools/nsc/CompilerCommand.scala index dc606d5a15a0..9e2405ee6e66 100644 --- a/src/compiler/scala/tools/nsc/CompilerCommand.scala +++ b/src/compiler/scala/tools/nsc/CompilerCommand.scala @@ -126,11 +126,12 @@ class CompilerCommand(arguments: List[String], val settings: Settings) { def expandArg(arg: String): List[String] = { import java.nio.file.{Files, Paths} import scala.jdk.CollectionConverters._ - def stripComment(s: String) = s.takeWhile(_ != '#').trim() // arg can be "" but not " " + def stripComment(s: String) = s.takeWhile(_ != '#').trim() val file = Paths.get(arg.stripPrefix("@")) if (!Files.exists(file)) throw new java.io.FileNotFoundException(s"argument file $file could not be found") - Files.readAllLines(file).asScala.filter(!_.startsWith("#")).map(stripComment).toList + val lines = Files.readAllLines(file).asScala.map(stripComment).filterNot(_.isEmpty).toList + lines.flatMap(settings.splitParams) } // override this if you don't want arguments processed here diff --git a/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala b/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala index 1a1b9301404a..a6af42fe5e7a 100644 --- a/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala +++ b/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala @@ -45,7 +45,7 @@ extends CompilerCommand(args, settings) { val f = io.File(target) if (!f.hasExtension("class", "jar", "zip") && f.canRead) AsScript else { - Console.err.println("No such file or class on classpath: " + target) + settings.errorFn("No such file or class on classpath: " + target) Error } } diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 926496d913fb..d8575d20914e 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -135,9 +135,22 @@ class Global(var currentSettings: Settings, reporter0: Reporter) type ThisPlatform = JavaPlatform { val global: Global.this.type } lazy val platform: ThisPlatform = new GlobalPlatform - /* A hook for the REPL to add a classpath entry containing products of previous runs to inliner's bytecode repository*/ - // Fixes scala/bug#8779 - def optimizerClassPath(base: ClassPath): ClassPath = base + + /* Create a class path for the backend, based on the given class path. + * Used to make classes available to the inliner's bytecode repository. + * + * In particular, if ct.sym is used for compilation, replace it with jrt. + * + * See ReplGlobal, which appends a classpath entry containing products of previous runs. (Fixes scala/bug#8779.) + */ + def optimizerClassPath(base: ClassPath): ClassPath = + base match { + case AggregateClassPath(entries) if entries.head.isInstanceOf[CtSymClassPath] => + JrtClassPath(release = None, closeableRegistry) + .map(jrt => AggregateClassPath(entries.drop(1).prepended(jrt))) + .getOrElse(base) + case _ => base + } def classPath: ClassPath = platform.classPath @@ -1275,26 +1288,26 @@ class Global(var currentSettings: Settings, reporter0: Reporter) // doesn't select a unique phase, that might be surprising too. def checkPhaseSettings(including: Boolean, specs: Seq[String]*) = { def isRange(s: String) = s.forall(c => c.isDigit || c == '-') - def isSpecial(s: String) = (s == "_" || isRange(s)) + def isMulti(s: String) = s == "_" || s == "all" || isRange(s) || s.startsWith("~") val tester = new ss.PhasesSetting("fake","fake") for (p <- specs.flatten.to(Set)) { tester.value = List(p) val count = if (including) first.iterator.count(tester.containsPhase(_)) - else phaseDescriptors.count(pd => tester.contains(pd.phaseName)) + else phaseDescriptors.count(pd => tester.contains(pd.phaseName) || tester.contains(s"~${pd.phaseName}")) if (count == 0) runReporting.warning(NoPosition, s"'$p' specifies no phase", WarningCategory.Other, site = "") - if (count > 1 && !isSpecial(p)) runReporting.warning(NoPosition, s"'$p' selects $count phases", WarningCategory.Other, site = "") - if (!including && isSpecial(p)) globalError(s"-Yskip and -Ystop values must name phases: '$p'") + if (count > 1 && !isMulti(p)) runReporting.warning(NoPosition, s"'$p' selects $count phases", WarningCategory.Other, site = "") + if (!including && isMulti(p)) globalError(s"-Yskip and -Ystop values must name phases: '$p'") tester.clear() } } // phases that are excluded; for historical reasons, these settings only select by phase name val exclusions = List(ss.stopBefore, ss.stopAfter, ss.skip) val inclusions = ss.visibleSettings collect { - case s: ss.PhasesSetting if !(exclusions contains s) => s.value + case s: ss.PhasesSetting if !exclusions.contains(s) => s.value } checkPhaseSettings(including = true, inclusions.toSeq: _*) - checkPhaseSettings(including = false, exclusions map (_.value): _*) + checkPhaseSettings(including = false, exclusions.map(_.value): _*) // Report the overhead of statistics measurements per every run if (settings.areStatisticsEnabled) @@ -1479,7 +1492,9 @@ class Global(var currentSettings: Settings, reporter0: Reporter) private def printArgs(sources: List[SourceFile]): Unit = settings.printArgs.valueSetByUser foreach { value => - val argsFile = (settings.recreateArgs ::: sources.map(_.file.absolute.toString())).mkString("", "\n", "\n") + def quote(s: String) = if (s.charAt(0) != '"' && s.contains(' ')) "\"" + s + "\"" else s + val allArgs = settings.recreateArgs ::: sources.map(_.file.absolute.toString()) + val argsFile = allArgs.map(quote).mkString("", "\n", "\n") value match { case "-" => reporter.echo(argsFile) @@ -1536,7 +1551,7 @@ class Global(var currentSettings: Settings, reporter0: Reporter) informTime(globalPhase.description, phaseTimer.nanos) // progress update - if ((settings.Xprint containsPhase globalPhase) || settings.printLate.value && runIsAt(cleanupPhase)) { + if (settings.Xprint.containsPhase(globalPhase) || settings.printLate.value && runIsAt(cleanupPhase)) { // print trees if (settings.Xshowtrees.value || settings.XshowtreesCompact.value || settings.XshowtreesStringified.value) nodePrinters.printAll() else printAllUnits() @@ -1712,10 +1727,8 @@ class Global(var currentSettings: Settings, reporter0: Reporter) } // class Run def printAllUnits(): Unit = { - print("[[syntax trees at end of %25s]]".format(phase)) - exitingPhase(phase)(currentRun.units foreach { unit => - nodePrinters showUnit unit - }) + print(f"[[syntax trees at end of $phase%25s]]") + exitingPhase(phase)(currentRun.units.foreach(nodePrinters.showUnit(_))) } /** We resolve the class/object ambiguity by passing a type/term name. diff --git a/src/compiler/scala/tools/nsc/PipelineMain.scala b/src/compiler/scala/tools/nsc/PipelineMain.scala index 06adc05f8cc6..0aa58520bcdc 100644 --- a/src/compiler/scala/tools/nsc/PipelineMain.scala +++ b/src/compiler/scala/tools/nsc/PipelineMain.scala @@ -77,7 +77,7 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe } } - implicit val executor = ExecutionContext.fromExecutor(new java.util.concurrent.ForkJoinPool(parallelism), t => handler.uncaughtException(Thread.currentThread(), t)) + implicit val executor: ExecutionContext = ExecutionContext.fromExecutor(new java.util.concurrent.ForkJoinPool(parallelism), t => handler.uncaughtException(Thread.currentThread(), t)) def changeExtension(p: Path, newExtension: String): Path = { val fileName = p.getFileName.toString val changedFileName = fileName.lastIndexOf('.') match { @@ -325,6 +325,7 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe trace.append("""{"traceEvents": [""") val sb = new mutable.StringBuilder(trace) + @annotation.nowarn("cat=deprecation") def durationEvent(name: String, cat: String, t: Timer): String = { s"""{"name": "$name", "cat": "$cat", "ph": "X", "ts": ${(t.startMicros).toLong}, "dur": ${(t.durationMicros).toLong}, "pid": 0, "tid": ${t.thread.getId}}""" } diff --git a/src/compiler/scala/tools/nsc/Reporting.scala b/src/compiler/scala/tools/nsc/Reporting.scala index fc1abc6d6691..eadd5e9c4d8a 100644 --- a/src/compiler/scala/tools/nsc/Reporting.scala +++ b/src/compiler/scala/tools/nsc/Reporting.scala @@ -204,7 +204,7 @@ trait Reporting extends internal.Reporting { self: ast.Positions with Compilatio def deprecationWarning(pos: Position, origin: Symbol, site: Symbol): Unit = { val version = origin.deprecationVersion.getOrElse("") val since = if (version.isEmpty) version else s" (since $version)" - val message = origin.deprecationMessage.map(": " + _).getOrElse("") + val message = origin.deprecationMessage.filter(!_.isEmpty).map(": " + _).getOrElse("") deprecationWarning(pos, origin, site, s"$origin${origin.locationString} is deprecated$since$message", version) } @@ -341,7 +341,7 @@ object Reporting { private val insertDash = "(?=[A-Z][a-z])".r val all: mutable.Map[String, WarningCategory] = mutable.Map.empty - private def add(c: WarningCategory): Unit = all += ((c.name, c)) + private def add(c: WarningCategory): Unit = all.put(c.name, c).ensuring(_.isEmpty, s"lint '${c.name}' added twice") object Deprecation extends WarningCategory; add(Deprecation) @@ -362,6 +362,7 @@ object Reporting { object OtherDebug extends Other; add(OtherDebug) object OtherNullaryOverride extends Other; add(OtherNullaryOverride) object OtherNonCooperativeEquals extends Other; add(OtherNonCooperativeEquals) + object OtherImplicitType extends Other; add(OtherImplicitType) sealed trait WFlag extends WarningCategory { override def summaryCategory: WarningCategory = WFlag } object WFlag extends WFlag { override def includes(o: WarningCategory): Boolean = o.isInstanceOf[WFlag] }; add(WFlag) @@ -407,6 +408,9 @@ object Reporting { object LintUnitSpecialization extends Lint; add(LintUnitSpecialization) object LintMultiargInfix extends Lint; add(LintMultiargInfix) object LintPerformance extends Lint; add(LintPerformance) + object LintIntDivToFloat extends Lint; add(LintIntDivToFloat) + object LintUniversalMethods extends Lint; add(LintUniversalMethods) + object LintNumericMethods extends Lint; add(LintNumericMethods) sealed trait Feature extends WarningCategory { override def summaryCategory: WarningCategory = Feature } object Feature extends Feature { override def includes(o: WarningCategory): Boolean = o.isInstanceOf[Feature] }; add(Feature) diff --git a/src/compiler/scala/tools/nsc/ast/parser/CommonTokens.scala b/src/compiler/scala/tools/nsc/ast/parser/CommonTokens.scala index 090c517054f7..a3b858c34fbc 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/CommonTokens.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/CommonTokens.scala @@ -51,7 +51,7 @@ abstract class CommonTokens { // J: PUBLIC = 42 final val PROTECTED = 43 final val PRIVATE = 44 - // S: SEALED = 45 + final val SEALED = 45 // J: contextual keyword final val ABSTRACT = 46 // J: DEFAULT = 47 // J: STATIC = 48 diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index f809182abdcb..127b75e9e665 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -477,7 +477,7 @@ self => /* --------------- PLACEHOLDERS ------------------------------------------- */ - /** The implicit parameters introduced by `_` in the current expression. + /** The parameters introduced by `_` "placeholder syntax" in the current expression. * Parameters appear in reverse order. */ var placeholderParams: List[ValDef] = Nil @@ -529,8 +529,8 @@ self => @tailrec final def isWildcard(t: Tree): Boolean = t match { - case Ident(name1) => !placeholderParams.isEmpty && name1 == placeholderParams.head.name - case Typed(t1, _) => isWildcard(t1) + case Ident(name1) => !placeholderParams.isEmpty && name1 == placeholderParams.head.name + case Typed(t1, _) => isWildcard(t1) case Annotated(t1, _) => isWildcard(t1) case _ => false } @@ -777,14 +777,21 @@ self => /** Convert tree to formal parameter list. */ def convertToParams(tree: Tree): List[ValDef] = tree match { - case Parens(ts) => ts map convertToParam - case _ => List(convertToParam(tree)) + case Parens(ts) => ts.map(convertToParam) + case Typed(Ident(_), _) => + val msg = "parentheses are required around the parameter of a lambda" + val wrn = sm"""|$msg + |Use '-Wconf:msg=lambda-parens:s' to silence this warning.""" + if (currentRun.isScala3) + deprecationWarning(tree.pos.point, wrn, "2.13.11") + List(convertToParam(tree)) + case _ => List(convertToParam(tree)) } /** Convert tree to formal parameter. */ def convertToParam(tree: Tree): ValDef = atPos(tree.pos) { def removeAsPlaceholder(name: Name): Unit = { - placeholderParams = placeholderParams filter (_.name != name) + placeholderParams = placeholderParams.filter(_.name != name) } def errorParam = makeParam(nme.ERROR, errorTypeTree setPos o2p(tree.pos.end)) def propagateNoWarnAttachment(from: Tree, to: ValDef): to.type = @@ -986,10 +993,15 @@ self => def finishBinaryOp(isExpr: Boolean, opinfo: OpInfo, rhs: Tree): Tree = { import opinfo._ + if (targs.nonEmpty) + if (currentRun.isScala3) + syntaxError(offset, "type application is not allowed for infix operators") + else + deprecationWarning(offset, "type application will be disallowed for infix operators", "2.13.11") val operatorPos: Position = Position.range(rhs.pos.source, offset, offset, offset + operator.length) val pos = lhs.pos.union(rhs.pos).union(operatorPos).withEnd(in.lastOffset).withPoint(offset) - atPos(pos)(makeBinop(isExpr, lhs, operator, rhs, operatorPos, opinfo.targs)) + atPos(pos)(makeBinop(isExpr, lhs, operator, rhs, operatorPos, targs)) } def reduceExprStack(base: List[OpInfo], top: Tree): Tree = reduceStack(isExpr = true, base, top) @@ -1388,7 +1400,7 @@ self => * }}} */ def stableId(): Tree = - path(thisOK = false, typeOK = false) + path(thisOK = true, typeOK = false) /** {{{ * QualId ::= Id {`.` Id} @@ -1706,54 +1718,52 @@ self => case IMPLICIT => implicitClosure(in.skipToken(), location) case _ => - def parseOther = { + def parseOther: Tree = { var t = postfixExpr() - if (in.token == EQUALS) { - t match { - case Ident(_) | Select(_, _) | Apply(_, _) => - t = atPos(t.pos.start, in.skipToken()) { gen.mkAssign(t, expr()) } - case _ => - } - } else if (in.token == COLON) { - t = stripParens(t) - val colonPos = in.skipToken() - if (in.token == USCORE) { - //todo: need to handle case where USCORE is a wildcard in a type - val uscorePos = in.skipToken() - if (isIdent && in.name == nme.STAR) { - in.nextToken() - t = atPos(t.pos.start, colonPos) { - Typed(t, atPos(uscorePos) { Ident(tpnme.WILDCARD_STAR) }) - } - } else { - syntaxErrorOrIncomplete("`*` expected", skipIt = true) + in.token match { + case EQUALS => + t match { + case Ident(_) | Select(_, _) | Apply(_, _) => + t = atPos(t.pos.start, in.skipToken()) { gen.mkAssign(t, expr()) } + case _ => } - } else if (isAnnotation) { - t = annotations(skipNewLines = false).foldLeft(t)(makeAnnotated) - } else { - t = atPos(t.pos.start, colonPos) { - val tpt = typeOrInfixType(location) - if (isWildcard(t)) - (placeholderParams: @unchecked) match { - case (vd @ ValDef(mods, name, _, _)) :: rest => - placeholderParams = treeCopy.ValDef(vd, mods, name, tpt.duplicate, EmptyTree) :: rest + case COLON => + t = stripParens(t) + val colonPos = in.skipToken() + if (in.token == USCORE) { + //todo: need to handle case where USCORE is a wildcard in a type + val uscorePos = in.skipToken() + if (isIdent && in.name == nme.STAR) { + in.nextToken() + t = atPos(t.pos.start, colonPos) { + Typed(t, atPos(uscorePos) { Ident(tpnme.WILDCARD_STAR) }) } - // this does not correspond to syntax, but is necessary to - // accept closures. We might restrict closures to be between {...} only. - Typed(t, tpt) + } + else syntaxErrorOrIncomplete("`*` expected", skipIt = true) } - } - } else if (in.token == MATCH) { - t = atPos(t.pos.start, in.skipToken())(Match(stripParens(t), inBracesOrNil(caseClauses()))) + else if (isAnnotation) + t = annotations(skipNewLines = false).foldLeft(t)(makeAnnotated) + else + t = atPos(t.pos.start, colonPos) { + val tpt = typeOrInfixType(location) + // for placeholder syntax `(_: Int) + 1`; function literal `(_: Int) => 42` uses `t` below + if (isWildcard(t)) + (placeholderParams: @unchecked) match { + case (vd @ ValDef(mods, name, _, _)) :: rest => + placeholderParams = treeCopy.ValDef(vd, mods, name, tpt.duplicate, EmptyTree) :: rest + } + // this does not correspond to syntax, but is necessary to accept closures. See below & convertToParam. + Typed(t, tpt) + } + case MATCH => + t = atPos(t.pos.start, in.skipToken())(Match(stripParens(t), inBracesOrNil(caseClauses()))) + case _ => } // disambiguate between self types "x: Int =>" and orphan function literals "(x: Int) => ???" // "(this: Int) =>" is parsed as an erroneous function literal but emits special guidance on // what's probably intended. def lhsIsTypedParamList() = t match { - case Parens(List(Typed(This(_), _))) => { - reporter.error(t.pos, "self-type annotation may not be in parentheses") - false - } + case Parens(List(Typed(This(_), _))) => reporter.error(t.pos, "self-type annotation may not be in parentheses"); false case Parens(xs) => xs.forall(isTypedParam) case _ => false } @@ -1774,15 +1784,15 @@ self => * Expr ::= implicit Id `=>` Expr * }}} */ - def implicitClosure(start: Offset, location: Location): Tree = { val param0 = convertToParam { atPos(in.offset) { - Ident(ident()) match { - case expr if in.token == COLON => - in.nextToken() ; Typed(expr, typeOrInfixType(location)) - case expr => expr + val p = stripParens(postfixExpr()) //if (in.token == USCORE) freshPlaceholder() else Ident(ident()) + if (in.token == COLON) { + in.nextToken() + Typed(p, typeOrInfixType(location)) } + else p } } val param = copyValDef(param0)(mods = param0.mods | Flags.IMPLICIT) @@ -2098,7 +2108,7 @@ self => if (in.token == SUBTYPE || in.token == SUPERTYPE) wildcardType(start, scala3Wildcard) else atPos(start) { Bind(tpnme.WILDCARD, EmptyTree) } } else { - typ() match { + this.typ() match { case Ident(name: TypeName) if nme.isVariableName(name) => atPos(start) { Bind(name, EmptyTree) } case t => t @@ -2246,10 +2256,8 @@ self => * * XXX: Hook for IDE */ - def simplePattern(): Tree = ( - // simple diagnostics for this entry point - simplePattern(() => syntaxErrorOrIncompleteAnd("illegal start of simple pattern", skipIt = true)(errorPatternTree)) - ) + def simplePattern(): Tree = + simplePattern(() => syntaxErrorOrIncompleteAnd("illegal start of simple pattern", skipIt = true)(errorPatternTree)) // simple diagnostics for this entry point def simplePattern(onError: () => Tree): Tree = { val start = in.offset in.token match { @@ -2289,7 +2297,7 @@ self => } /** The implementation of the context sensitive methods for parsing outside of patterns. */ final val outPattern = new PatternContextSensitive { - def argType(): Tree = typ() + def argType(): Tree = this.typ() def functionArgType(): Tree = paramType(repeatedParameterOK = false, useStartAsPosition = true) } /** The implementation for parsing inside of patterns at points where sequences are allowed. */ @@ -3504,7 +3512,7 @@ self => else if (isDefIntro || isLocalModifier || isAnnotation) { if (in.token == IMPLICIT) { val start = in.skipToken() - if (isIdent) stats += implicitClosure(start, InBlock) + if (isIdent || in.token == USCORE) stats += implicitClosure(start, InBlock) else stats ++= localDef(Flags.IMPLICIT) } else { stats ++= localDef(0) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala b/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala index 56dbf3db7494..c846cc55ec81 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala @@ -28,7 +28,6 @@ object Tokens extends CommonTokens { /** modifiers */ final val IMPLICIT = 40 final val OVERRIDE = 41 - final val SEALED = 45 final val LAZY = 55 final val MACRO = 57 diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala index a2b2a21b365c..b56711f8d7ac 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala @@ -236,9 +236,11 @@ abstract class BCodeIdiomatic { asm.Type.getMethodDescriptor(StringRef.toASMType, argTypes:_*), new asm.Handle( asm.Opcodes.H_INVOKESTATIC, - "java/lang/invoke/StringConcatFactory", + jliStringConcatFactoryRef.internalName, "makeConcatWithConstants", - "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;", + List(jliMethodHandlesLookupRef, StringRef, jliMethodTypeRef, StringRef, ArrayBType(ObjectRef)) + .map(_.descriptor) + .mkString("(", "", s")${jliCallSiteRef.descriptor}"), false ), (recipe +: constants):_* diff --git a/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala index 7f337b087bb2..98d293f855c9 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala @@ -210,6 +210,9 @@ abstract class CoreBTypesFromSymbols[G <: Global] extends CoreBTypes { def jliLambdaMetafactoryRef : ClassBType = _jliLambdaMetafactoryRef.get private[this] lazy val _jliLambdaMetafactoryRef : LazyVar[ClassBType] = runLazy(classBTypeFromSymbol(requiredClass[java.lang.invoke.LambdaMetafactory])) + def jliStringConcatFactoryRef : ClassBType = _jliStringConcatFactoryRef.get + private[this] lazy val _jliStringConcatFactoryRef : LazyVar[ClassBType] = runLazy(classBTypeFromSymbol(getRequiredClass("java.lang.invoke.StringConcatFactory"))) + def srBoxesRunTimeRef : ClassBType = _srBoxesRunTimeRef.get private[this] lazy val _srBoxesRunTimeRef : LazyVar[ClassBType] = runLazy(classBTypeFromSymbol(requiredClass[scala.runtime.BoxesRunTime])) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/PostProcessorFrontendAccess.scala b/src/compiler/scala/tools/nsc/backend/jvm/PostProcessorFrontendAccess.scala index 6fd591720842..cce594a7fbe4 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/PostProcessorFrontendAccess.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/PostProcessorFrontendAccess.scala @@ -16,11 +16,11 @@ package backend.jvm import scala.collection.mutable.Clearable import scala.reflect.internal.util.{JavaClearable, Position, Statistics} import scala.reflect.io.AbstractFile +import scala.tools.nsc.Reporting.WarningCategory import scala.tools.nsc.backend.jvm.BTypes.InternalName +import scala.util.chaining._ import java.util.{Collection => JCollection, Map => JMap} -import scala.tools.nsc.Reporting.WarningCategory - /** * Functionality needed in the post-processor whose implementation depends on the compiler * frontend. All methods are synchronized. @@ -186,7 +186,13 @@ object PostProcessorFrontendAccess { @inline def debug: Boolean = s.isDebug - val target: String = s.targetValue + val target: String = s.targetValue.tap { value => + s.releaseValue.foreach { release => + if (value.toInt < release.toInt) + directBackendReporting.warning(NoPosition, + s"target platform version $value is older than the release version $release") + } + } private val singleOutDir = s.outputDirs.getSingleOutput // the call to `outputDirFor` should be frontendSynch'd, but we assume that the setting is not mutated during the backend diff --git a/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala index 05444337a241..75af78a8fc58 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala @@ -83,6 +83,8 @@ abstract class BackendUtils extends PerRunInit { case "17" => asm.Opcodes.V17 case "18" => asm.Opcodes.V18 case "19" => asm.Opcodes.V19 + case "20" => asm.Opcodes.V20 + case "21" => asm.Opcodes.V21 // to be continued... }) diff --git a/src/compiler/scala/tools/nsc/classpath/DirectoryClassPath.scala b/src/compiler/scala/tools/nsc/classpath/DirectoryClassPath.scala index 2f4cd0941477..4dd8743c5f4e 100644 --- a/src/compiler/scala/tools/nsc/classpath/DirectoryClassPath.scala +++ b/src/compiler/scala/tools/nsc/classpath/DirectoryClassPath.scala @@ -13,7 +13,7 @@ package scala.tools.nsc.classpath import java.io.{Closeable, File} -import java.net.URL +import java.net.{URI, URL} import scala.reflect.io.{AbstractFile, PlainFile, PlainNioFile} import scala.tools.nsc.util.{ClassPath, ClassRepresentation, EfficientClassPath} @@ -209,7 +209,7 @@ final class JrtClassPath(fs: java.nio.file.FileSystem) extends ClassPath with No if (inPackage.isRoot) ClassPathEntries(packages(inPackage), Nil) else ClassPathEntries(packages(inPackage), classes(inPackage)) - def asURLs: Seq[URL] = Seq(new URL("jrt:/")) + def asURLs: Seq[URL] = Seq(new URI("jrt:/").toURL) // We don't yet have a scheme to represent the JDK modules in our `-classpath`. // java models them as entries in the new "module path", we'll probably need to follow this. def asClassPathStrings: Seq[String] = Nil diff --git a/src/compiler/scala/tools/nsc/classpath/VirtualDirectoryClassPath.scala b/src/compiler/scala/tools/nsc/classpath/VirtualDirectoryClassPath.scala index f8d626841473..ac187762c095 100644 --- a/src/compiler/scala/tools/nsc/classpath/VirtualDirectoryClassPath.scala +++ b/src/compiler/scala/tools/nsc/classpath/VirtualDirectoryClassPath.scala @@ -15,7 +15,7 @@ package scala.tools.nsc.classpath import scala.tools.nsc.util.ClassRepresentation import scala.reflect.io.{AbstractFile, VirtualDirectory} import FileUtils._ -import java.net.URL +import java.net.{URI, URL} import scala.reflect.internal.util.AbstractFileClassLoader import scala.tools.nsc.util.ClassPath @@ -35,7 +35,7 @@ case class VirtualDirectoryClassPath(dir: VirtualDirectory) extends ClassPath wi def isPackage(f: AbstractFile): Boolean = f.isPackage // mimic the behavior of the old nsc.util.DirectoryClassPath - def asURLs: Seq[URL] = Seq(new URL("file://_VIRTUAL_/" + dir.name)) + def asURLs: Seq[URL] = Seq(new URI("file://_VIRTUAL_/" + dir.name).toURL) def asClassPathStrings: Seq[String] = Seq(dir.path) override def findClass(className: String): Option[ClassRepresentation] = findClassFile(className) map ClassFileEntryImpl diff --git a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala index 2049693a81f3..e77576c804ae 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala @@ -16,14 +16,15 @@ package scala.tools.nsc package javac -import scala.collection.mutable.ListBuffer import symtab.Flags import JavaTokens._ -import scala.annotation.tailrec +import scala.annotation._ +import scala.collection.mutable +import scala.collection.mutable.ListBuffer import scala.language.implicitConversions -import scala.reflect.internal.util.Position -import scala.reflect.internal.util.ListOfNil +import scala.reflect.internal.util.{ListOfNil, Position} import scala.tools.nsc.Reporting.WarningCategory +import scala.util.chaining._ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { val global : Global @@ -45,6 +46,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { abstract class JavaParser extends ParserCommon { val in: JavaScanner + def unit: CompilationUnit def freshName(prefix : String): Name protected implicit def i2p(offset : Int) : Position @@ -493,11 +495,39 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { case SYNCHRONIZED => in.nextToken() case _ => - val privateWithin: TypeName = - if (isPackageAccess && !inInterface) thisPackageName - else tpnme.EMPTY - - return Modifiers(flags, privateWithin) withAnnotations annots + val unsealed = 0L // no flag for UNSEALED + def consume(added: FlagSet): false = { in.nextToken(); flags |= added; false } + def lookingAhead(s: String): Boolean = { + import scala.reflect.internal.Chars._ + var i = 0 + val n = s.length + val lookahead = in.in.lookahead + while (i < n && lookahead.ch != SU) { + if (lookahead.ch != s.charAt(i)) return false + lookahead.next() + i += 1 + } + i == n && Character.isWhitespace(lookahead.ch) + } + val done = (in.token != IDENTIFIER) || ( + in.name match { + case nme.javaRestrictedIdentifiers.SEALED => consume(Flags.SEALED) + case nme.javaRestrictedIdentifiers.UNSEALED => consume(unsealed) + case nme.javaRestrictedIdentifiers.NON => + !lookingAhead("-sealed") || { + in.nextToken() + in.nextToken() + consume(unsealed) + } + case _ => true + } + ) + if (done) { + val privateWithin: TypeName = + if (isPackageAccess && !inInterface) thisPackageName + else tpnme.EMPTY + return Modifiers(flags, privateWithin) withAnnotations annots + } } } abort("should not be here") @@ -802,7 +832,15 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { List() } + def permitsOpt() = + if (in.token == IDENTIFIER && in.name == nme.javaRestrictedIdentifiers.PERMITS) { + in.nextToken() + repsep(() => typ(), COMMA) + } + else Nil + def classDecl(mods: Modifiers): List[Tree] = { + if (mods.hasFlag(SEALED)) patmat.javaClassesByUnit(unit.source) = mutable.Set.empty accept(CLASS) val pos = in.currentPos val name = identForType() @@ -815,9 +853,11 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { javaLangObject() } val interfaces = interfacesOpt() + val permits = permitsOpt() val (statics, body) = typeBody(CLASS) addCompanionObject(statics, atPos(pos) { ClassDef(mods, name, tparams, makeTemplate(superclass :: interfaces, body)) + .tap(cd => if (permits.nonEmpty) cd.updateAttachment(PermittedSubclasses(permits))) }) } @@ -867,6 +907,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { } def interfaceDecl(mods: Modifiers): List[Tree] = { + if (mods.hasFlag(SEALED)) patmat.javaClassesByUnit(unit.source) = mutable.Set.empty accept(INTERFACE) val pos = in.currentPos val name = identForType() @@ -878,11 +919,13 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { } else { List(javaLangObject()) } + val permits = permitsOpt() val (statics, body) = typeBody(INTERFACE) addCompanionObject(statics, atPos(pos) { ClassDef(mods | Flags.TRAIT | Flags.INTERFACE | Flags.ABSTRACT, name, tparams, makeTemplate(parents, body)) + .tap(cd => if (permits.nonEmpty) cd.updateAttachment(PermittedSubclasses(permits))) }) } @@ -905,7 +948,6 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { } else if (in.token == SEMI) { in.nextToken() } else { - // See "14.3. Local Class and Interface Declarations" adaptRecordIdentifier() if (in.token == ENUM || in.token == RECORD || definesInterface(in.token)) diff --git a/src/compiler/scala/tools/nsc/javac/JavaTokens.scala b/src/compiler/scala/tools/nsc/javac/JavaTokens.scala index a124d1b90aaa..66fcdf7c069c 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaTokens.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaTokens.scala @@ -38,6 +38,7 @@ object JavaTokens extends ast.parser.CommonTokens { final val NATIVE = 53 final val STRICTFP = 54 final val THROWS = 56 + final val UNSEALED = 57 // contextual keyword /** templates */ final val INTERFACE = 66 diff --git a/src/compiler/scala/tools/nsc/package.scala b/src/compiler/scala/tools/nsc/package.scala index 46cd59b63625..10c2a9ac9af7 100644 --- a/src/compiler/scala/tools/nsc/package.scala +++ b/src/compiler/scala/tools/nsc/package.scala @@ -12,6 +12,8 @@ package scala.tools +import scala.reflect.internal.util.StringContextStripMarginOps + package object nsc { type Mode = scala.reflect.internal.Mode val Mode = scala.reflect.internal.Mode @@ -32,4 +34,8 @@ package object nsc { @deprecated("Use scala.reflect.internal.util.ListOfNil", "2.11.0") lazy val ListOfNil = scala.reflect.internal.util.ListOfNil + + /** Adds the `sm` interpolator to a [[scala.StringContext]]. + */ + implicit val `strip margin`: StringContext => StringContextStripMarginOps = StringContextStripMarginOps } diff --git a/src/compiler/scala/tools/nsc/profile/ExtendedThreadMxBean.java b/src/compiler/scala/tools/nsc/profile/ExtendedThreadMxBean.java index 1d5cf4bc3e4e..f580e16ba496 100644 --- a/src/compiler/scala/tools/nsc/profile/ExtendedThreadMxBean.java +++ b/src/compiler/scala/tools/nsc/profile/ExtendedThreadMxBean.java @@ -260,13 +260,14 @@ public SunThreadMxBean(ThreadMXBean underlying) { super(underlying); this.real = underlying; try { - getThreadUserTimeMethod = real.getClass().getMethod("getThreadUserTime", long[].class); - isThreadAllocatedMemoryEnabledMethod = real.getClass().getMethod("isThreadAllocatedMemoryEnabled"); - setThreadAllocatedMemoryEnabledMethod = real.getClass().getMethod("setThreadAllocatedMemoryEnabled", Boolean.TYPE); - getThreadAllocatedBytesMethod1 = real.getClass().getMethod("getThreadAllocatedBytes", Long.TYPE); - getThreadAllocatedBytesMethod2 = real.getClass().getMethod("getThreadAllocatedBytes", long[].class); - isThreadAllocatedMemorySupportedMethod = real.getClass().getMethod("isThreadAllocatedMemorySupported"); - getThreadCpuTimeMethod = real.getClass().getMethod("getThreadCpuTime", long[].class); + Class cls = Class.forName("com.sun.management.ThreadMXBean"); + getThreadUserTimeMethod = cls.getMethod("getThreadUserTime", long[].class); + isThreadAllocatedMemoryEnabledMethod = cls.getMethod("isThreadAllocatedMemoryEnabled"); + setThreadAllocatedMemoryEnabledMethod = cls.getMethod("setThreadAllocatedMemoryEnabled", Boolean.TYPE); + getThreadAllocatedBytesMethod1 = cls.getMethod("getThreadAllocatedBytes", Long.TYPE); + getThreadAllocatedBytesMethod2 = cls.getMethod("getThreadAllocatedBytes", long[].class); + isThreadAllocatedMemorySupportedMethod = cls.getMethod("isThreadAllocatedMemorySupported"); + getThreadCpuTimeMethod = cls.getMethod("getThreadCpuTime", long[].class); getThreadUserTimeMethod.setAccessible(true); isThreadAllocatedMemoryEnabledMethod.setAccessible(true); diff --git a/src/compiler/scala/tools/nsc/profile/Profiler.scala b/src/compiler/scala/tools/nsc/profile/Profiler.scala index 9a50c0101940..9b9e67e1a965 100644 --- a/src/compiler/scala/tools/nsc/profile/Profiler.scala +++ b/src/compiler/scala/tools/nsc/profile/Profiler.scala @@ -129,6 +129,7 @@ private [profile] object RealProfiler { private val idGen = new AtomicInteger() lazy val allPlugins = ServiceLoader.load(classOf[ProfilerPlugin]).iterator.asScala.toList + @annotation.nowarn("cat=deprecation") private[profile] def snapThread(idleTimeNanos: Long): ProfileSnap = { val current = Thread.currentThread() val allocatedBytes = threadMx.getThreadAllocatedBytes(Thread.currentThread().getId) @@ -403,6 +404,7 @@ class StreamProfileReporter(out:PrintWriter) extends ProfileReporter { override def reportForeground(profiler: RealProfiler, threadRange: ProfileRange): Unit = { reportCommon(EventType.MAIN, profiler, threadRange) } + @annotation.nowarn("cat=deprecation") private def reportCommon(tpe:EventType.value, profiler: RealProfiler, threadRange: ProfileRange): Unit = { out.println(s"$tpe,${threadRange.start.snapTimeNanos},${threadRange.end.snapTimeNanos},${profiler.id},${threadRange.phase.id},${threadRange.phase.name},${threadRange.purpose},${threadRange.taskCount},${threadRange.thread.getId},${threadRange.thread.getName},${threadRange.runNs},${threadRange.idleNs},${threadRange.cpuNs},${threadRange.userNs},${threadRange.allocatedBytes},${threadRange.end.heapBytes} ") } diff --git a/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala b/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala index 0d47fc6bb988..9dafc2426d70 100644 --- a/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala +++ b/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala @@ -27,9 +27,10 @@ class ConsoleReporter(val settings: Settings, val reader: BufferedReader, val wr override def finish(): Unit = { import reflect.internal.util.StringOps.countElementsAsString if (!settings.nowarn.value && hasWarnings) - echo(countElementsAsString(warningCount, WARNING.toString.toLowerCase)) + writer.println(countElementsAsString(warningCount, WARNING.toString.toLowerCase)) if (hasErrors) - echo(countElementsAsString(errorCount, ERROR.toString.toLowerCase)) + writer.println(countElementsAsString(errorCount, ERROR.toString.toLowerCase)) + writer.flush() super.finish() } } diff --git a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala index 314b5393138f..aa81abb28041 100644 --- a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala @@ -945,14 +945,19 @@ class MutableSettings(val errorFn: String => Unit, val pathFactory: PathFactory) def clear(): Unit = (v = Nil) - // we slightly abuse the usual meaning of "contains" here by returning - // true if our phase list contains "_", regardless of the incoming argument + /* True if the named phase is selected. + * + * A setting value "_" or "all" selects all phases by name. + */ def contains(phName: String) = doAllPhases || containsName(phName) - def containsName(phName: String) = stringValues exists (phName startsWith _) + /* True if the given phase name matches the selection, possibly as prefixed "~name". */ + def containsName(phName: String) = stringValues.exists(phName.startsWith(_)) def containsId(phaseId: Int) = phaseIdTest(phaseId) - def containsPhase(ph: Phase) = contains(ph.name) || containsId(ph.id) + /* True if the phase is selected by name or "all", or by id, or by prefixed "~name". */ + def containsPhase(ph: Phase) = contains(ph.name) || containsId(ph.id) || containsName(s"~${ph.name}") || + ph.next != null && containsName(s"~${ph.next.name}") // null if called during construction - def doAllPhases = stringValues.contains("_") + def doAllPhases = stringValues.exists(s => s == "_" || s == "all") def unparse: List[String] = value.map(v => s"$name:$v") withHelpSyntax( diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 6a3a812f21d8..e551cfec3c30 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -499,7 +499,7 @@ trait ScalaSettings extends StandardScalaSettings with Warnings { _: MutableSett val showPhases = BooleanSetting("-Vphases", "Print a synopsis of compiler phases.") .withAbbreviation("-Xshow-phases") val Yposdebug = BooleanSetting("-Vpos", "Trace position validation.") withAbbreviation "-Ypos-debug" - val Xprint = PhasesSetting("-Vprint", "Print out program after") + val Xprint = PhasesSetting("-Vprint", "Print out program after (or ~phase for before and after)", "typer") .withAbbreviation("-Xprint") val Xprintpos = BooleanSetting("-Vprint-pos", "Print tree positions, as offsets.") .withAbbreviation("-Xprint-pos") diff --git a/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala index 10e9ab81ff22..4b798e0eb52d 100644 --- a/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala @@ -73,7 +73,7 @@ trait StandardScalaSettings { _: MutableSettings => // .withAbbreviation("-Xtarget") // .withAbbreviation("-Xunchecked-java-output-version") .withDeprecationMessage("Use -release instead to compile against the correct platform API.") - def targetValue: String = releaseValue.getOrElse(target.value) + def targetValue: String = target.valueSetByUser.orElse(releaseValue).getOrElse(target.value) val unchecked = BooleanSetting ("-unchecked", "Enable additional warnings where generated code depends on assumptions. See also -Wconf.") withAbbreviation "--unchecked" withPostSetHook { s => if (s.value) Wconf.tryToSet(List(s"cat=unchecked:w")) else Wconf.tryToSet(List(s"cat=unchecked:s")) @@ -103,7 +103,7 @@ object StandardScalaSettings { val MaxTargetVersion = ScalaVersion(javaSpecVersion) match { case SpecificScalaVersion(1, minor, _, _) => minor case SpecificScalaVersion(major, _, _, _) => major - case _ => 19 + case _ => 21 } private val AllTargetVersions = (MinTargetVersion to MaxTargetVersion).map(_.toString).to(List) diff --git a/src/compiler/scala/tools/nsc/settings/Warnings.scala b/src/compiler/scala/tools/nsc/settings/Warnings.scala index b5d12f83683a..1b2413a7d4c2 100644 --- a/src/compiler/scala/tools/nsc/settings/Warnings.scala +++ b/src/compiler/scala/tools/nsc/settings/Warnings.scala @@ -205,14 +205,17 @@ trait Warnings { val Serial = LintWarning("serial", "@SerialVersionUID on traits and non-serializable classes.") val ValPattern = LintWarning("valpattern", "Enable pattern checks in val definitions.") val EtaZero = LintWarning("eta-zero", "Usage `f` of parameterless `def f()` resulted in eta-expansion, not empty application `f()`.") - val EtaSam = LintWarning("eta-sam", "The Java-defined target interface for eta-expansion was not annotated @FunctionalInterface.") + val EtaSam = LintWarning("eta-sam", "A method reference was eta-expanded but the expected SAM type was not annotated @FunctionalInterface.") val Deprecation = LintWarning("deprecation", "Enable -deprecation and also check @deprecated annotations.") val ByNameImplicit = LintWarning("byname-implicit", "Block adapted by implicit with by-name parameter.") val RecurseWithDefault = LintWarning("recurse-with-default", "Recursive call used default argument.") val UnitSpecialization = LintWarning("unit-special", "Warn for specialization of Unit in parameter position.") val MultiargInfix = LintWarning("multiarg-infix", "Infix operator was defined or used with multiarg operand.") val ImplicitRecursion = LintWarning("implicit-recursion", "Implicit resolves to an enclosing definition.") - val UniversalMethods = LintWarning("universal-methods", "Require arg to is/asInstanceOf.") + val UniversalMethods = LintWarning("universal-methods", "Require arg to is/asInstanceOf. No Unit receiver.") + val NumericMethods = LintWarning("numeric-methods", "Dubious usages, such as `42.isNaN`.") + val ArgDiscard = LintWarning("arg-discard", "-Wvalue-discard for adapted arguments.") + val IntDivToFloat = LintWarning("int-div-to-float", "Warn when an integer division is converted (widened) to floating point: `(someInt / 2): Double`.") def allLintWarnings = values.toSeq.asInstanceOf[Seq[LintWarning]] } @@ -246,6 +249,9 @@ trait Warnings { def multiargInfix = lint contains MultiargInfix def lintImplicitRecursion = lint.contains(ImplicitRecursion) || (warnSelfImplicit.value: @nowarn("cat=deprecation")) def lintUniversalMethods = lint.contains(UniversalMethods) + def lintNumericMethods = lint.contains(NumericMethods) + def lintArgDiscard = lint.contains(ArgDiscard) + def lintIntDivToFloat = lint.contains(IntDivToFloat) // The Xlint warning group. val lint = MultiChoiceSetting( diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index ac20d4f16e0d..616e8298698d 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -826,6 +826,7 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { case tpnme.DeprecatedATTR => in.skip(attrLen) + sym.addAnnotation(JavaDeprecatedAttr) if (sym == clazz) staticModule.addAnnotation(JavaDeprecatedAttr) @@ -839,7 +840,10 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { val paramNameAccess = new Array[Int](paramCount) var i = 0 while (i < paramCount) { - paramNames(i) = pool.getExternalName(u2()) + paramNames(i) = u2() match { + case 0 => null // may occur on JDK 21+, as per scala/bug#12783 + case index => pool.getExternalName(index) + } paramNameAccess(i) = u2() i += 1 } @@ -852,10 +856,11 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { in.skip(attrLen) case tpnme.RuntimeAnnotationATTR => - val numAnnots = u2() + val numAnnots = u2() val annots = new ListBuffer[AnnotationInfo] - for (n <- 0 until numAnnots; annot <- parseAnnotation(u2())) - annots += annot + numAnnots times { + parseAnnotation(u2()).foreach(annots.addOne) + } /* `sym.withAnnotations(annots)`, like `sym.addAnnotation(annot)`, prepends, * so if we parsed in classfile order we would wind up with the annotations * in reverse order in `sym.annotations`. Instead we just read them out the @@ -904,6 +909,17 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { } in.skip(attrLen) + case tpnme.PermittedSubclassesATTR => + sym.setFlag(SEALED) + val numberOfClasses = u2() + numberOfClasses times { + val k = pool.getClassSymbol(u2()) + completer match { + case ctc: ClassTypeCompleter => ctc.permittedSubclasses ::= k // sym.addChild(k) + case _ => + } + } + case _ => in.skip(attrLen) } @@ -1345,6 +1361,7 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { sym setInfo createFromClonedSymbols(alias.initialize.typeParams, alias.tpe)(typeFun) } } + // on JDK 21+, `names` may include nulls, as per scala/bug#12783 private class ParamNames(val names: Array[NameOrString], val access: Array[Int]) { assert(names.length == access.length, "Require as many names as access") def length = names.length @@ -1356,6 +1373,7 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { var exceptions: List[NameOrString] = Nil } private final class ClassTypeCompleter(name: Name, jflags: JavaAccFlags, parent: NameOrString, ifaces: List[NameOrString]) extends JavaTypeCompleter { + var permittedSubclasses: List[symbolTable.Symbol] = Nil override def complete(sym: symbolTable.Symbol): Unit = { val info = if (sig != null) sigToType(sym, sig) else { val superTpe = if (parent == null) definitions.AnyClass.tpe_* else getClassSymbol(parent.value).tpe_* @@ -1364,6 +1382,9 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { ClassInfoType(superTpe1 :: ifacesTypes, instanceScope, clazz) } sym.setInfo(info) + for (k <- permittedSubclasses) + if (k.parentSymbols.contains(sym)) + sym.addChild(k) } } @@ -1445,8 +1466,10 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { case (i, param) => val isSynthetic = (paramNames.access(i) & ACC_SYNTHETIC) != 0 if (!isSynthetic) { - param.name = paramNames.names(i).name.toTermName.encode param.resetFlag(SYNTHETIC) + val nameOrString = paramNames.names(i) + if (nameOrString != null) + param.name = nameOrString.name.toTermName.encode } } // there's not anything we can do, but it's slightly worrisome @@ -1493,7 +1516,13 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { if (flags.isStatic) staticScope else instanceScope } object ClassfileParser { - private implicit class GoodTimes(val n: Int) extends AnyVal { - def times(body: => Unit) = (1 to n).foreach(_ => body) + private implicit class GoodTimes(private val n: Int) extends AnyVal { + def times(body: => Unit): Unit = { + var i = n + while (i > 0) { + body + i -= 1 + } + } } } diff --git a/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala b/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala index 864c4cedd5fc..6d38ff105bad 100644 --- a/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala +++ b/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala @@ -1148,6 +1148,9 @@ class TreeUnpickler[Tasty <: TastyUniverse]( tpd.Apply(fn, until(end)(readTerm()(argsCtx))) } case TYPEAPPLY => tpd.TypeApply(readTerm(), until(end)(readTpt())) + case APPLYsigpoly => + // this is skipped if it appears in parents, so only affects forced annotation trees + signaturePolymorphicIsUnsupported case TYPED => tpd.Typed(readTerm(), readTpt()) case IF => if (nextByte === INLINE) unsupportedTermTreeError("inline conditional expression") @@ -1241,6 +1244,9 @@ class TreeUnpickler[Tasty <: TastyUniverse]( */ private def abortMacroHole[T]: T = abortWith(msg = "Scala 3 macro hole in pickled TASTy") + private def signaturePolymorphicIsUnsupported[T](implicit ctx: Context): T = + unsupportedTermTreeError("signature polymorphic application") + private def metaprogrammingIsUnsupported[T](implicit ctx: Context): T = unsupportedError("Scala 3 metaprogramming features") diff --git a/src/compiler/scala/tools/nsc/transform/AccessorSynthesis.scala b/src/compiler/scala/tools/nsc/transform/AccessorSynthesis.scala index 8f59b22ccf38..17f2027dbb37 100644 --- a/src/compiler/scala/tools/nsc/transform/AccessorSynthesis.scala +++ b/src/compiler/scala/tools/nsc/transform/AccessorSynthesis.scala @@ -357,7 +357,7 @@ trait AccessorSynthesis extends Transform with ast.TreeDSL { else rhs private def mkCheckedAccessorRhs(retVal: Tree, pos: Position, bitmap: BitmapInfo): Tree = { - val msg = s"Uninitialized field: ${clazz.sourceFile}: ${pos.line}" + val msg = s"Uninitialized field: ${clazz.sourceFile.name}: ${pos.line}" val result = IF(mkTest(bitmap, equalToZero = false)). THEN(retVal). diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala index 96c0bd21b29d..60758c58c54a 100644 --- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala +++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala @@ -398,85 +398,94 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL { } private def transformStringSwitch(sw: Match): Tree = { import CODE._ - // these assumptions about the shape of the tree are justified by the codegen in MatchOptimization - val Match(Typed(selTree, _), cases) = sw: @unchecked - def selArg = selTree match { - case x: Ident => REF(x.symbol) - case x: Literal => x - case x => throw new MatchError(x) - } - val newSel = selTree match { - case x: Ident => atPos(x.symbol.pos)(IF (x.symbol OBJ_EQ NULL) THEN ZERO ELSE selArg.OBJ_##) - case x: Literal => atPos(x.pos) (if (x.value.value == null) ZERO else selArg.OBJ_##) - case x => throw new MatchError(x) - } - val restpe = sw.tpe - val resUnit = restpe =:= UnitTpe - val swPos = sw.pos.focus - - /* From this: - * string match { case "AaAa" => 1 case "BBBB" | "c" => 2 case _ => 3 } - * Generate this: - * string.## match { - * case 2031744 => - * if ("AaAa" equals string) goto matchEnd (1) - * else if ("BBBB" equals string) goto case2 - * else goto defaultCase - * case 99 => - * if ("c" equals string) goto case2 - * else goto defaultCase - * case _ => goto defaultCase - * } - * case2: goto matchEnd (2) - * defaultCase: goto matchEnd (3) // or `goto matchEnd (throw new MatchError(string))` if no default was given - * matchEnd(res: Int): res - * Extra labels are added for alternative patterns branches, since multiple branches in the - * resulting switch may need to correspond to a single case body. - */ + // tree shape assumption justified by the codegen in MatchOptimization + val Match(Typed(selTree0, _), cases) = sw: @unchecked + // usually `selTree0` is an `Ident` or `Literal`, but Xasync may transform the scrutinee local into a state + // machine field (scala/bug#12686). `evalOnce` introduces another local if needed (not for Ident / Literal). + gen.evalOnce(selTree0, currentOwner, unit) { selTree => + def selArg = selTree() match { + case x: Ident => REF(x.symbol) + case x: Literal => x + case x => throw new MatchError(x) + } - val labels = mutable.ListBuffer.empty[LabelDef] - var defaultCaseBody = Throw(New(MatchErrorClass.tpe_*, selArg)): Tree + val newSel = selTree() match { + case x: Ident => atPos(x.symbol.pos)(IF(x.symbol OBJ_EQ NULL) THEN ZERO ELSE selArg.OBJ_##) + case x: Literal => atPos(x.pos)(if (x.value.value == null) ZERO else selArg.OBJ_##) + case x => throw new MatchError(x) + } + val restpe = sw.tpe + val resUnit = restpe =:= UnitTpe + val swPos = sw.pos.focus + + /* From this: + * string match { case "AaAa" => 1 case "BBBB" | "c" => 2 case _ => 3 } + * Generate this: + * string.## match { + * case 2031744 => + * if ("AaAa" equals string) goto matchEnd (1) + * else if ("BBBB" equals string) goto case2 + * else goto defaultCase + * case 99 => + * if ("c" equals string) goto case2 + * else goto defaultCase + * case _ => goto defaultCase + * } + * case2: goto matchEnd (2) + * defaultCase: goto matchEnd (3) // or `goto matchEnd (throw new MatchError(string))` if no default was given + * matchEnd(res: Int): res + * Extra labels are added for alternative patterns branches, since multiple branches in the + * resulting switch may need to correspond to a single case body. + */ - def LABEL(name: String) = currentOwner.newLabel(unit.freshTermName(name), swPos).setFlag(SYNTH_CASE_FLAGS) - def newCase() = LABEL( "case").setInfo(MethodType(Nil, restpe)) - val defaultCase = LABEL("defaultCase").setInfo(MethodType(Nil, restpe)) - val matchEnd = LABEL("matchEnd").tap { lab => - // genbcode isn't thrilled about seeing labels with Unit arguments, so `success`'s type is one of - // `${sw.tpe} => ${sw.tpe}` or `() => Unit` depending. - lab.setInfo(MethodType(if (resUnit) Nil else List(lab.newSyntheticValueParam(restpe)), restpe)) - } - def goto(sym: Symbol, params: Tree*) = REF(sym) APPLY (params: _*) - def gotoEnd(body: Tree) = if (resUnit) BLOCK(body, goto(matchEnd)) else goto(matchEnd, body) - - val casesByHash = cases.flatMap { - case cd@CaseDef(StringsPattern(strs), _, body) => - val jump = newCase() // always create a label so when its used it matches the source case (e.g. `case4()`) - strs match { - case str :: Nil => List((str, gotoEnd(body), cd.pat.pos)) - case _ => - labels += LabelDef(jump, Nil, gotoEnd(body)) - strs.map((_, goto(jump), cd.pat.pos)) - } - case cd if isDefaultCase(cd) => defaultCaseBody = gotoEnd(cd.body); None - case cd => globalError(s"unhandled in switch: $cd"); None - }.groupBy(_._1.##) - - val newCases = casesByHash.toList.sortBy(_._1).map { - case (hash, cases) => - val newBody = cases.foldRight(atPos(swPos)(goto(defaultCase): Tree)) { - case ((null, rhs, pos), next) => atPos(pos)(IF (NULL OBJ_EQ selArg) THEN rhs ELSE next) - case ((str, rhs, pos), next) => atPos(pos)(IF (LIT(str) OBJ_== selArg) THEN rhs ELSE next) - } - CASE(LIT(hash)) ==> newBody - } + val labels = mutable.ListBuffer.empty[LabelDef] + var defaultCaseBody = Throw(New(MatchErrorClass.tpe_*, selArg)): Tree - labels += LabelDef(defaultCase, Nil, defaultCaseBody) - labels += LabelDef(matchEnd, matchEnd.info.params, matchEnd.info.params.headOption.fold(UNIT: Tree)(REF)) + def LABEL(name: String) = currentOwner.newLabel(unit.freshTermName(name), swPos).setFlag(SYNTH_CASE_FLAGS) - val stats = Match(newSel, newCases :+ (DEFAULT ==> goto(defaultCase))) :: labels.toList + def newCase() = LABEL("case").setInfo(MethodType(Nil, restpe)) - val res = Block(stats: _*) - typedWithPos(sw.pos)(res) + val defaultCase = LABEL("defaultCase").setInfo(MethodType(Nil, restpe)) + val matchEnd = LABEL("matchEnd").tap { lab => + // genbcode isn't thrilled about seeing labels with Unit arguments, so `success`'s type is one of + // `${sw.tpe} => ${sw.tpe}` or `() => Unit` depending. + lab.setInfo(MethodType(if (resUnit) Nil else List(lab.newSyntheticValueParam(restpe)), restpe)) + } + + def goto(sym: Symbol, params: Tree*) = REF(sym) APPLY (params: _*) + + def gotoEnd(body: Tree) = if (resUnit) BLOCK(body, goto(matchEnd)) else goto(matchEnd, body) + + val casesByHash = cases.flatMap { + case cd@CaseDef(StringsPattern(strs), _, body) => + val jump = newCase() // always create a label so when its used it matches the source case (e.g. `case4()`) + strs match { + case str :: Nil => List((str, gotoEnd(body), cd.pat.pos)) + case _ => + labels += LabelDef(jump, Nil, gotoEnd(body)) + strs.map((_, goto(jump), cd.pat.pos)) + } + case cd if isDefaultCase(cd) => defaultCaseBody = gotoEnd(cd.body); None + case cd => globalError(s"unhandled in switch: $cd"); None + }.groupBy(_._1.##) + + val newCases = casesByHash.toList.sortBy(_._1).map { + case (hash, cases) => + val newBody = cases.foldRight(atPos(swPos)(goto(defaultCase): Tree)) { + case ((null, rhs, pos), next) => atPos(pos)(IF(NULL OBJ_EQ selArg) THEN rhs ELSE next) + case ((str, rhs, pos), next) => atPos(pos)(IF(LIT(str) OBJ_== selArg) THEN rhs ELSE next) + } + CASE(LIT(hash)) ==> newBody + } + + labels += LabelDef(defaultCase, Nil, defaultCaseBody) + labels += LabelDef(matchEnd, matchEnd.info.params, matchEnd.info.params.headOption.fold(UNIT: Tree)(REF)) + + val stats = Match(newSel, newCases :+ (DEFAULT ==> goto(defaultCase))) :: labels.toList + + val res = Block(stats: _*) + typedWithPos(sw.pos)(res) + } } // transform scrutinee of all matches to switchable types (ints, strings) diff --git a/src/compiler/scala/tools/nsc/transform/Fields.scala b/src/compiler/scala/tools/nsc/transform/Fields.scala index c4f1ba58e071..1ffef3ed8e6b 100644 --- a/src/compiler/scala/tools/nsc/transform/Fields.scala +++ b/src/compiler/scala/tools/nsc/transform/Fields.scala @@ -364,8 +364,8 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor if (newDecls.nonEmpty) { val allDecls = newScope - origDecls foreach allDecls.enter - newDecls foreach allDecls.enter + origDecls.foreach(allDecls.enter(_)) + newDecls .foreach(allDecls.enter(_)) ClassInfoType(parents, allDecls, clazz) } else tp diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index 98b8e0cbbad1..92a5b15b12cf 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -357,6 +357,8 @@ abstract class Mixin extends Transform with ast.TreeDSL with AccessorSynthesis { case alias1 => registerRequiredDirectInterface(alias1, clazz, parent => s"Unable to implement a super accessor required by trait ${mixinClass.name} unless $parent is directly extended by $clazz.") + if (alias1.isValue && !alias1.isMethod || alias1.isAccessor) + reporter.error(clazz.pos, s"parent $mixinClass has a super call to method ${mixinMember.alias.fullNameString}, which binds to the value ${alias1.fullNameString}. Super calls can only target methods.") superAccessor.asInstanceOf[TermSymbol] setAlias alias1 } } diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 8d4d9f8417c4..d0a367ed16a6 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -506,8 +506,15 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { else if (!args.isEmpty) enteringTyper { foreach2(sym.typeParams, args) { (tp, arg) => - if (tp.isSpecialized) + if (tp.isSpecialized) { specializedTypeVarsBuffer(arg, result) + } else if (sym == ValueOfClass) { // scala/bug#11489, we only update it for ValueOf + arg.typeSymbol.annotations.foreach { + case lzai: LazyAnnotationInfo if lzai.symbol == SpecializedClass => + specializedTypeVarsBuffer(arg, result) + case _ => + } + } } } case PolyType(tparams, resTpe) => specializedTypeVarsBuffer(resTpe, result); tparams.foreach(sym => specializedTypeVarsBuffer(sym.info, result)) @@ -1125,7 +1132,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } } else None case (overridden, env) => - val om = specializedOverload(clazz, overridden, env) + val om = specializedOverload(clazz, overriding, env, overridden) clazz.info.decls.enter(om) foreachWithIndex(om.paramss) { (params, i) => foreachWithIndex(params) { (param, j) => @@ -1423,7 +1430,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { override def enterSyntheticSym(tree: Tree): Symbol = tree.symbol } - protected override def newBodyDuplicator(context: Context): SpecializeBodyDuplicator = + override protected def newBodyDuplicator(context: Context): SpecializeBodyDuplicator = new SpecializeBodyDuplicator(context) override def newNamer(context: Context): Namer = diff --git a/src/compiler/scala/tools/nsc/transform/async/AnfTransform.scala b/src/compiler/scala/tools/nsc/transform/async/AnfTransform.scala index 19924c2ffb70..9d5449e1f8b5 100644 --- a/src/compiler/scala/tools/nsc/transform/async/AnfTransform.scala +++ b/src/compiler/scala/tools/nsc/transform/async/AnfTransform.scala @@ -204,7 +204,10 @@ private[async] trait AnfTransform extends TransformUtils { if (isPatMatGeneratedJump(tree)) assignUnitType(tree) if (!needsResultVar || isUnitType(tree.tpe) || (tree.tpe =:= definitions.NothingTpe)) { - core(NoSymbol) + if (tree.tpe =:= definitions.BoxedUnitTpe) { + currentStats += assignUnitType(core(NoSymbol)) + literalBoxedUnit + } else core(NoSymbol) } else { val varDef = defineVar(nameSource(), tree.tpe, tree.pos) currentStats += varDef diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala index 67c6efba06a0..b5ea1d2f91d1 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala @@ -13,10 +13,11 @@ package scala package tools.nsc.transform.patmat -import scala.collection.mutable import scala.collection.immutable.ArraySeq +import scala.collection.{IterableOps, mutable} import scala.reflect.internal.util.Collections._ import scala.reflect.internal.util.HashSet +import scala.tools.nsc.transform.patmat.Logic.LogicLinkedHashSet trait Logic extends Debugging { import global._ @@ -113,24 +114,21 @@ trait Logic extends Debugging { def implications: List[(Sym, List[Sym], List[Sym])] } + // Using LogicLinkedHashSet (a custom mutable.LinkedHashSet subclass) to ensure deterministic exhaustivity + // messages. immutable.ListSet was too slow (concatenate cost? scala/bug#12499). + // would be nice to statically check whether a prop is equational or pure, // but that requires typing relations like And(x: Tx, y: Ty) : (if(Tx == PureProp && Ty == PureProp) PureProp else Prop) - final case class And(ops: Set[Prop]) extends Prop + final case class And(ops: LogicLinkedHashSet[Prop]) extends Prop object And { def apply(ps: Prop*) = create(ps) - def create(ps: Iterable[Prop]) = ps match { - case ps: Set[Prop] => new And(ps) - case _ => new And(ps.to(scala.collection.immutable.ListSet)) - } + def create(ps: Iterable[Prop]) = new And(ps.to(LogicLinkedHashSet)) } - final case class Or(ops: Set[Prop]) extends Prop + final case class Or(ops: LogicLinkedHashSet[Prop]) extends Prop object Or { def apply(ps: Prop*) = create(ps) - def create(ps: Iterable[Prop]) = ps match { - case ps: Set[Prop] => new Or(ps) - case _ => new Or(ps.to(scala.collection.immutable.ListSet)) - } + def create(ps: Iterable[Prop]) = new Or(ps.to(LogicLinkedHashSet)) } final case class Not(a: Prop) extends Prop @@ -197,7 +195,7 @@ trait Logic extends Debugging { */ def simplify(f: Prop): Prop = { - def hasImpureAtom(ops0: collection.Iterable[Prop]): Boolean = { + def hasImpureAtom(ops0: Iterable[Prop]): Boolean = { // HOT method, imperative rewrite of: // ops.combinations(2).exists { // case Seq(a, Not(b)) if a == b => true @@ -247,7 +245,7 @@ trait Logic extends Debugging { } } - def mapConserve[A <: AnyRef](s: Set[A])(f: A => A): Set[A] = { + def mapConserve[CC[X] <: IterableOps[X, CC, CC[X]], A <: AnyRef](s: CC[A])(f: A => A): CC[A] = { var changed = false val s1 = s.map {a => val a1 = f(a) @@ -284,10 +282,10 @@ trait Logic extends Debugging { | (_: AtMostOne) => p } - def simplifyAnd(ps: Set[Prop]): Prop = { + def simplifyAnd(ps: Iterable[Prop]): Prop = { // recurse for nested And (pulls all Ands up) // build up Set in order to remove duplicates - val props = mutable.LinkedHashSet.empty[Prop] + val props = LogicLinkedHashSet.empty[Prop] for (prop <- ps) { simplifyProp(prop) match { case True => // ignore `True` @@ -300,10 +298,10 @@ trait Logic extends Debugging { else /\(props) } - def simplifyOr(ps: Set[Prop]): Prop = { + def simplifyOr(ps: Iterable[Prop]): Prop = { // recurse for nested Or (pulls all Ors up) // build up Set in order to remove duplicates - val props = mutable.LinkedHashSet.empty[Prop] + val props = LogicLinkedHashSet.empty[Prop] for (prop <- ps) { simplifyProp(prop) match { case False => // ignore `False` @@ -344,7 +342,7 @@ trait Logic extends Debugging { } def gatherVariables(p: Prop): collection.Set[Var] = { - val vars = new mutable.LinkedHashSet[Var]() + val vars = new LogicLinkedHashSet[Var]() (new PropTraverser { override def applyVar(v: Var) = vars += v })(p) @@ -352,7 +350,7 @@ trait Logic extends Debugging { } def gatherSymbols(p: Prop): collection.Set[Sym] = { - val syms = new mutable.LinkedHashSet[Sym]() + val syms = new LogicLinkedHashSet[Sym]() (new PropTraverser { override def applySymbol(s: Sym) = syms += s })(p) @@ -410,7 +408,7 @@ trait Logic extends Debugging { def removeVarEq(props: List[Prop], modelNull: Boolean = false): (Prop, List[Prop]) = { val start = if (settings.areStatisticsEnabled) statistics.startTimer(statistics.patmatAnaVarEq) else null - val vars = new mutable.LinkedHashSet[Var] + val vars = new LogicLinkedHashSet[Var] object gatherEqualities extends PropTraverser { override def apply(p: Prop) = p match { @@ -517,6 +515,31 @@ trait Logic extends Debugging { } } +object Logic { + import scala.annotation.nowarn + import scala.collection.mutable.{Growable, GrowableBuilder, SetOps} + import scala.collection.{IterableFactory, IterableFactoryDefaults, StrictOptimizedIterableOps} + + // Local subclass because we can't override `addAll` in the collections (bin compat), see PR scala/scala#10361 + @nowarn("msg=inheritance from class LinkedHashSet") + class LogicLinkedHashSet[A] extends mutable.LinkedHashSet[A] + with SetOps[A, LogicLinkedHashSet, LogicLinkedHashSet[A]] + with StrictOptimizedIterableOps[A, LogicLinkedHashSet, LogicLinkedHashSet[A]] + with IterableFactoryDefaults[A, LogicLinkedHashSet] { + override def iterableFactory: IterableFactory[LogicLinkedHashSet] = LogicLinkedHashSet + override def addAll(xs: IterableOnce[A]): this.type = { + sizeHint(xs.knownSize) + super.addAll(xs) + } + } + + object LogicLinkedHashSet extends IterableFactory[LogicLinkedHashSet] { + override def from[A](source: IterableOnce[A]): LogicLinkedHashSet[A] = Growable.from(empty[A], source) + override def empty[A]: LogicLinkedHashSet[A] = new LogicLinkedHashSet[A] + override def newBuilder[A]: mutable.Builder[A, LogicLinkedHashSet[A]] = new GrowableBuilder(empty[A]) + } +} + trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis { trait TreesAndTypesDomain extends PropositionalLogic with CheckableTreeAndTypeAnalysis { type Type = global.Type @@ -734,8 +757,8 @@ trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis { } - import global.{ConstantType, SingletonType, Literal, Ident, singleType, TypeBounds, NoSymbol} import global.definitions._ + import global.{ConstantType, Ident, Literal, NoSymbol, SingletonType, TypeBounds, singleType} // all our variables range over types diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala index 99aafbee6a03..8c746cc78494 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala @@ -110,7 +110,7 @@ trait TreeAndTypeAnalysis extends Debugging { private def enumerateSealed(tp: Type, grouped: Boolean): List[List[Type]] = { val tpApprox = analyzer.approximateAbstracts(tp) - val pre = tpApprox.prefix + val pre = tp.prefix val sym = tp.typeSymbol def subclassesToSubtypes(syms: List[Symbol]): List[Type] = syms.flatMap { sym => @@ -120,7 +120,7 @@ trait TreeAndTypeAnalysis extends Debugging { // sealed trait X[T]; class XInt extends X[Int] // XInt not valid when enumerating X[String] // however, must also approximate abstract types - val memberType = nestedMemberType(sym, pre, tpApprox.typeSymbol.owner) + val memberType = nestedMemberType(sym, pre, tp.typeSymbol.owner) val subTp = appliedType(memberType, WildcardType.fillList(sym.typeParams.length)) val subTpApprox = analyzer.approximateAbstracts(subTp) if (subTpApprox <:< tpApprox) Some(checkableType(subTp)) else None @@ -129,8 +129,11 @@ trait TreeAndTypeAnalysis extends Debugging { def filterAndSortChildren(children: Set[Symbol]) = { // symbols which are both sealed and abstract need not be covered themselves, // because all of their children must be and they cannot otherwise be created. - children.toList.filter(x => !(x.isSealed || x.isPrivate) || !x.isAbstractClass || isPrimitiveValueClass(x)) - .sortBy(_.sealedSortName) + val children1 = children.toList.filterNot(child => child.isSealed && child.isAbstractClass).sortBy(_.sealedSortName) + children1.filterNot { child => + // remove private abstract children that are superclasses of other children, for example in t6159 drop X2 + child.isPrivate && child.isAbstractClass && children1.exists(sym => (sym ne child) && sym.isSubClass(child)) + } } @tailrec def groupChildren(wl: List[Symbol], acc: List[List[Symbol]]): List[List[Symbol]] = wl match { diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala index 1274ca468669..6c320fd01e1d 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala @@ -56,7 +56,7 @@ trait MatchOptimization extends MatchTreeMaking with MatchApproximation { val cond = test.prop def simplify(c: Prop): Set[Prop] = c match { - case And(ops) => ops flatMap simplify + case And(ops) => ops.flatMap(simplify).toSet case Or(ops) => Set(False) // TODO: make more precise case Not(Eq(Var(_), NullConst)) => Set.empty // not worth remembering case True => Set.empty // same diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala index 14f7f12e5027..1097b6646e5a 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala @@ -10,7 +10,8 @@ * additional information regarding copyright ownership. */ -package scala.tools.nsc.transform.patmat +package scala.tools.nsc +package transform.patmat /** Translate typed Trees that represent pattern matches into the patternmatching IR, defined by TreeMakers. */ @@ -67,11 +68,10 @@ trait MatchTranslation { def pos = tree.pos def tpe = binder.info.dealias // the type of the variable bound to the pattern def pt = unbound match { - case Star(tpt) => this glbWith seqType(tpt.tpe) + case Star(tpt) => seqType(tpt.tpe) case TypeBound(tpe) => tpe case tree => tree.tpe } - def glbWith(other: Type) = glb(tpe :: other :: Nil).normalize object SymbolAndTypeBound { def unapply(tree: Tree): Option[(Symbol, Type)] = tree match { @@ -93,7 +93,7 @@ trait MatchTranslation { private def bindingStep(sub: Symbol, subpattern: Tree) = step(SubstOnlyTreeMaker(sub, binder))(rebindTo(subpattern)) private def equalityTestStep() = step(EqualityTestTreeMaker(binder, tree, pos))() - private def typeTestStep(sub: Symbol, subPt: Type) = step(TypeTestTreeMaker(sub, binder, subPt, glbWith(subPt))(pos))() + private def typeTestStep(sub: Symbol, subPt: Type) = step(TypeTestTreeMaker(sub, binder, subPt, subPt)(pos))() private def alternativesStep(alts: List[Tree]) = step(AlternativesTreeMaker(binder, translatedAlts(alts), alts.head.pos))() private def translatedAlts(alts: List[Tree]) = alts map (alt => rebindTo(alt).translate()) private def noStep() = step()() diff --git a/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala b/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala index b9d562ff9756..958ffad37c3d 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala @@ -13,9 +13,10 @@ package scala.tools.nsc.transform.patmat import scala.annotation.tailrec +import scala.collection.mutable import scala.collection.mutable.ListBuffer import scala.reflect.internal.{Mode, Types} -import scala.reflect.internal.util.Statistics +import scala.reflect.internal.util.{SourceFile, Statistics} import scala.tools.nsc.Global import scala.tools.nsc.ast import scala.tools.nsc.transform.{Transform, TypingTransformers} @@ -58,6 +59,9 @@ trait PatternMatching extends Transform val phaseName: String = "patmat" + /** Symbols to force for determining children of sealed Java classes. */ + val javaClassesByUnit = perRunCaches.newMap[SourceFile, mutable.Set[Symbol]]() + def newTransformer(unit: CompilationUnit): AstTransformer = new MatchTransformer(unit) class MatchTransformer(unit: CompilationUnit) extends TypingTransformer(unit) { diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Solving.scala b/src/compiler/scala/tools/nsc/transform/patmat/Solving.scala index dd6a524549dc..28b60686c5c1 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/Solving.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/Solving.scala @@ -166,7 +166,7 @@ trait Solving extends Logic { } } - def and(bv: Set[Lit]): Lit = { + def and(bv: collection.Set[Lit]): Lit = { if (bv.isEmpty) { // this case can actually happen because `removeVarEq` could add no constraints constTrue @@ -178,14 +178,14 @@ trait Solving extends Logic { // op1 /\ op2 /\ ... /\ opx <==> // (o -> op1) /\ (o -> op2) ... (o -> opx) /\ (!op1 \/ !op2 \/... \/ !opx \/ o) // (!o \/ op1) /\ (!o \/ op2) ... (!o \/ opx) /\ (!op1 \/ !op2 \/... \/ !opx \/ o) - val new_bv = bv - constTrue // ignore `True` + val new_bv = bv.toSet - constTrue // ignore `True` val o = newLiteral() // auxiliary Tseitin variable new_bv.foreach(op => addClauseProcessed(clause(op, -o))) o } } - def or(bv: Set[Lit]): Lit = { + def or(bv: collection.Set[Lit]): Lit = { if (bv.isEmpty) { constFalse } else if (bv.size == 1) { @@ -196,7 +196,7 @@ trait Solving extends Logic { // op1 \/ op2 \/ ... \/ opx <==> // (op1 -> o) /\ (op2 -> o) ... (opx -> o) /\ (op1 \/ op2 \/... \/ opx \/ !o) // (!op1 \/ o) /\ (!op2 \/ o) ... (!opx \/ o) /\ (op1 \/ op2 \/... \/ opx \/ !o) - val new_bv = bv - constFalse // ignore `False` + val new_bv = bv.toSet - constFalse // ignore `False` val o = newLiteral() // auxiliary Tseitin variable addClauseProcessed(new_bv + (-o)) o diff --git a/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala b/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala index 4fc3c1fdddd8..cee889f74e4b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala @@ -77,11 +77,11 @@ trait Adaptations { ) } @inline def msg(what: String): String = s"adaptation of an empty argument list by inserting () $what" - @inline def noAdaptation = { + @inline def noAdaptation: false = { context.error(t.pos, adaptWarningMessage(msg("has been removed"), showAdaptation = false)) false // drop adaptation } - @inline def deprecatedAdaptation = { + @inline def deprecatedAdaptation: true = { val twist = if (isLeakyTarget) "leaky (Object-receiving) target makes this especially dangerous" else "this is unlikely to be what you want" @@ -89,8 +89,16 @@ trait Adaptations { context.deprecationWarning(t.pos, t.symbol, adaptWarningMessage(text), "2.11.0") true // keep adaptation } - @inline def warnAdaptation = { - if (settings.warnAdaptedArgs && !isInfix) context.warning(t.pos, adaptWarningMessage( + @inline def warnAdaptation: true = { + def discardedArgs = t match { + case Apply(_, stat @ Block(Apply(TypeApply(Select(adapter, _), _), adapted) :: Nil, expr) :: Nil) => + isTupleSymbol(adapter.symbol.companion) && expr.tpe == UnitTpe && adapted == args + case _ => false + } + if (settings.lintArgDiscard && discardedArgs) context.warning(t.pos, adaptWarningMessage( + s"adapted the argument list to expected Unit type: arguments will be discarded"), + WarningCategory.LintAdaptedArgs) + else if (settings.warnAdaptedArgs && !isInfix) context.warning(t.pos, adaptWarningMessage( s"adapted the argument list to the expected ${args.size}-tuple: add additional parens instead"), WarningCategory.LintAdaptedArgs) true // keep adaptation diff --git a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala index 80dab26b77e8..1161a3cb404b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala @@ -13,6 +13,8 @@ package scala.tools.nsc package typechecker +import scala.collection.mutable.ArrayDeque + /** Defines the sub-components for the namer, packageobjects, and typer phases. */ trait Analyzer extends AnyRef @@ -98,15 +100,20 @@ trait Analyzer extends AnyRef // Lacking a better fix, we clear it here (before the phase is created, meaning for each // compiler run). This is good enough for the resident compiler, which was the most affected. undoLog.clear() + private val toCheckAfterTyper = ArrayDeque.empty[CompilationUnit.ToCheckAfterTyper] + def addCheckAfterTyper(check: CompilationUnit.ToCheckAfterTyper): Unit = toCheckAfterTyper.append(check) override def run(): Unit = { val start = if (settings.areStatisticsEnabled) statistics.startTimer(statistics.typerNanos) else null global.echoPhaseSummary(this) val units = currentRun.units + while (units.hasNext) { applyPhase(units.next()) undoLog.clear() } finishComputeParamAlias() + try while (toCheckAfterTyper.nonEmpty) toCheckAfterTyper.removeHead().apply() + finally toCheckAfterTyper.clearAndShrink() // defensive measure in case the bookkeeping in deferred macro expansion is buggy clearDelayed() if (settings.areStatisticsEnabled) statistics.stopTimer(statistics.typerNanos, start) @@ -117,7 +124,12 @@ trait Analyzer extends AnyRef unit.body = typer.typed(unit.body) // interactive typed may finish by throwing a `TyperResult` if (!settings.Youtline.value) { - for (workItem <- unit.toCheck) workItem() + while (unit.toCheck.nonEmpty) { + unit.toCheck.removeHead() match { + case now: CompilationUnit.ToCheckAfterUnit => now() + case later: CompilationUnit.ToCheckAfterTyper => addCheckAfterTyper(later) + } + } if (!settings.isScaladoc && settings.warnUnusedImport) warnUnusedImports(unit) if (!settings.isScaladoc && settings.warnUnused.isSetByUser) @@ -126,7 +138,7 @@ trait Analyzer extends AnyRef } finally { runReporting.reportSuspendedMessages(unit) - unit.toCheck.clear() + unit.toCheck.clearAndShrink() } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala b/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala index 2557867ea966..9fd8343ea7ce 100644 --- a/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala +++ b/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala @@ -54,7 +54,8 @@ trait AnalyzerPlugins { self: Analyzer with splain.SplainData => /** * Let analyzer plugins change the types assigned to definitions. For definitions that have * an annotated type, the assigned type is obtained by typing that type tree. Otherwise, the - * type is inferred by typing the definition's righthand side. + * type is inferred by typing the definition's righthand side, or from the overridden + * member under `-Xsource:3`. * * In order to know if the type was inferred, you can query the `wasEmpty` field in the `tpt` * TypeTree of the definition (for DefDef and ValDef). diff --git a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala index 481531a5951d..5374867eafe1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala @@ -370,8 +370,10 @@ trait Checkable { def isSealedOrFinal(sym: Symbol) = sym.isSealed || sym.isFinal val Xsym = X.typeSymbol val Psym = P.typeSymbol - if (isSealedOrFinal(Xsym) && isSealedOrFinal(Psym) && (currentRun.compiles(Xsym) || currentRun.compiles(Psym))) - context.unit.toCheck += (() => recheckFruitless()) + if (isSealedOrFinal(Xsym) && isSealedOrFinal(Psym) && (currentRun.compiles(Xsym) || currentRun.compiles(Psym))) { + debuglog(s"deferred recheckFruitless($X, $P)") + context.unit.addPostTyperCheck(() => recheckFruitless()) + } } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 2521ce0b4c40..800fef31e99d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -21,6 +21,7 @@ import scala.annotation.tailrec import scala.reflect.runtime.ReflectionUtils import scala.reflect.macros.runtime.AbortMacroException import scala.util.control.{ControlThrowable, NonFatal} +import scala.tools.nsc.Reporting.WarningCategory import scala.tools.nsc.util.stackTraceString import scala.reflect.io.NoAbstractFile import scala.reflect.internal.util.NoSourceFile @@ -107,11 +108,9 @@ trait ContextErrors extends splain.SplainErrors { def issueTypeError(err: AbsTypeError)(implicit context: Context): Unit = { context.issue(err) } + // OPT: avoid error string creation for errors that won't see the light of day def typeErrorMsg(context: Context, found: Type, req: Type) = - if (context.openImplicits.nonEmpty && !settings.Vimplicits.value) - // OPT: avoid error string creation for errors that won't see the light of day, but predicate - // this on -Xsource:2.13 for bug compatibility with https://github.com/scala/scala/pull/7147#issuecomment-418233611 - "type mismatch" + if (!context.openImplicits.isEmpty && !settings.Vimplicits.value) "type mismatch" else "type mismatch" + foundReqMsg(found, req) } @@ -153,7 +152,7 @@ trait ContextErrors extends splain.SplainErrors { MacroIncompatibleEngineError("macro cannot be expanded, because it was compiled by an incompatible macro engine", internalMessage) /** The implicit not found message from the annotation, and whether it's a supplement message or not. */ - def NoImplicitFoundAnnotation(tree: Tree, param: Symbol): (Boolean, String) = { + def NoImplicitFoundAnnotation(tree: Tree, param: Symbol): (Boolean, String) = param match { case ImplicitNotFoundMsg(msg) => (false, msg.formatParameterMessage(tree)) case _ => @@ -167,7 +166,6 @@ trait ContextErrors extends splain.SplainErrors { true -> supplement } } - } def NoImplicitFoundError(tree: Tree, param: Symbol)(implicit context: Context): Unit = { val (isSupplement, annotationMsg) = NoImplicitFoundAnnotation(tree, param) @@ -186,6 +184,24 @@ trait ContextErrors extends splain.SplainErrors { issueNormalTypeError(tree, if (errMsg.isEmpty) defaultErrMsg else errMsg) } + private def InferredImplicitErrorImpl(tree: Tree, inferred: Type, cx: Context, isTyper: Boolean): Unit = { + def err(): Unit = { + val msg = + s"Implicit definition ${if (currentRun.isScala3) "must" else "should"} have explicit type${ + if (!inferred.isErroneous) s" (inferred $inferred)" else "" + }" + if (currentRun.isScala3) ErrorUtils.issueNormalTypeError(tree, msg)(cx) + else cx.warning(tree.pos, msg, WarningCategory.OtherImplicitType) + } + val sym = tree.symbol + // Defer warning field of class until typing getter (which is marked implicit) + if (sym.isImplicit) { + if (!sym.isLocalToBlock) err() + } + else if (!isTyper && sym.isField && !sym.isLocalToBlock) + sym.updateAttachment(FieldTypeInferred) + } + trait TyperContextErrors { self: Typer => @@ -391,50 +407,70 @@ trait ContextErrors extends splain.SplainErrors { //typedSelect def NotAMemberError(sel: Tree, qual: Tree, name: Name, cx: Context) = { - import util.{ EditDistance, StringUtil } + import util.EditDistance, util.StringUtil.oxford def errMsg: String = { + val editThreshold = 3 + val maxSuggestions = 4 + val owner = qual.tpe.typeSymbol val target = qual.tpe.widen def targetKindString = if (owner.isTypeParameterOrSkolem) "type parameter " else "" def nameString = decodeWithKind(name, owner) /* Illuminating some common situations and errors a bit further. */ def addendum = { - def orEmpty(cond: Boolean)(s: => String) = if (cond) s else "" + @inline def orEmpty(cond: Boolean)(s: => String) = if (cond) s else "" val companionSymbol: Symbol = { if (name.isTermName && owner.isPackageClass) target.member(name.toTypeName) else NoSymbol } val companion = orEmpty(companionSymbol != NoSymbol)(s"note: $companionSymbol exists, but it has no companion object.") - // find out all the names available under target within 2 edit distances - lazy val alternatives: List[String] = { - val editThreshold = 2 + // find out all the names available under target within ~2 edit distances + lazy val alternatives: List[(Int, String)] = { val x = name.decode - if (context.openImplicits.nonEmpty || (x.size < 2) || x.endsWith("=")) Nil + // effectively suppress comparison ops, but if they say <= and there is >=, offer it + def isEncodedComparison(n: Name) = n match { + case nme.EQ | nme.NE | nme.LT | nme.GT | nme.LE | nme.GE => true + case _ => false + } + val nameIsComparison = isEncodedComparison(name) + if (context.openImplicits.nonEmpty || x.length < 2) Nil else { target.members.iterator - .filter(sym => (sym.isTerm == name.isTermName) && + .filter(sym => sym.isTerm == name.isTermName && !sym.isConstructor && !nme.isLocalName(sym.name) && + isEncodedComparison(sym.name) == nameIsComparison && + sym.name != nme.EQ && sym.name != nme.NE && cx.isAccessible(sym, target)) .map(_.name.decode) - .filter(n => (n.length > 2) && - (math.abs(n.length - x.length) <= editThreshold) && - (n != x) && - !n.contains("$") && - EditDistance.levenshtein(n, x) <= editThreshold) - .distinct.toList + .filter { n => + math.abs(n.length - x.length) <= editThreshold && + n != x && + !n.contains("$") + } + .map(n => (EditDistance.levenshtein(n, x), n)) + .filter { case (d, n) => + val nset = n.endsWith("_=") + val xset = x.endsWith("_=") + val (n1, x1) = if (nset && xset) (n.dropRight(2), x.dropRight(2)) else (n, x) + def contained = x1.forall(c => n1.indexOf(c) >= 0) + !(nset ^ xset) && d <= editThreshold && (d <= n1.length/2 && d <= x1.length/2 || contained) + } + .toList.sorted } } - val altStr: String = { - val maxSuggestions = 4 - orEmpty(companionSymbol == NoSymbol) { - alternatives match { - case Nil => "" - case xs => s"did you mean ${StringUtil.oxford(xs.sorted.take(maxSuggestions), "or")}?" - } + val altStr: String = + orEmpty(companionSymbol == NoSymbol && alternatives.nonEmpty) { + val d0 = alternatives.head._1 + val (best0, rest0) = alternatives.span(_._1 == d0) + val best = best0.map(_._2).distinct + val rest = rest0.map(_._2).distinct + val more = (maxSuggestions - best.length) max 0 + val add1 = orEmpty(more > 0 && rest.nonEmpty)(s" or perhaps ${oxford(rest.take(more), "or")}?") + val add2 = orEmpty(best.length > maxSuggestions || rest.length > more)(" or...?") + s"did you mean ${oxford(best.take(maxSuggestions), "or")}?$add1$add2" } - } val semicolon = orEmpty(linePrecedes(qual, sel))(s"possible cause: maybe a semicolon is missing before `$nameString`?") val notAnyRef = orEmpty(ObjectClass.info.member(name).exists)(notAnyRefMessage(target)) val javaRules = orEmpty(owner.isJavaDefined && owner.isClass && !owner.hasPackageFlag) { @@ -673,11 +709,10 @@ trait ContextErrors extends splain.SplainErrors { def NotEnoughArgsError(tree: Tree, fun: Tree, missing: List[Symbol]) = { val notEnoughArgumentsMsg = { val suffix = if (missing.isEmpty) "" else { - val keep = missing take 3 map (_.name) + val keep = missing.take(3).map(_.name) val ess = if (missing.tail.isEmpty) "" else "s" - f".%nUnspecified value parameter$ess ${ - keep.mkString("", ", ", if ((missing drop 3).nonEmpty) "..." else ".") - }" + val dots = if (missing.drop(3).nonEmpty) "..." else "." + keep.mkString(s".\nUnspecified value parameter$ess ", ", ", dots) } s"not enough arguments for ${ treeSymTypeMsg(fun) }$suffix" } @@ -834,11 +869,6 @@ trait ContextErrors extends splain.SplainErrors { setError(tree) } - def DependentMethodTpeConversionToFunctionError(tree: Tree, tp: Type): Tree = { - issueNormalTypeError(tree, "method with dependent type " + tp + " cannot be converted to function value") - setError(tree) - } - // cases where we do not necessarily return trees //checkStarPatOK @@ -855,7 +885,7 @@ trait ContextErrors extends splain.SplainErrors { // def stabilize def NotAValueError(tree: Tree, sym: Symbol) = { - issueNormalTypeError(tree, sym.kindString + " " + sym.fullName + " is not a value") + issueNormalTypeError(tree, s"${sym.kindString} ${sym.fullName} is not a value") setError(tree) } @@ -1049,6 +1079,8 @@ trait ContextErrors extends splain.SplainErrors { def MacroAnnotationTopLevelModuleBadExpansion(ann: Tree) = issueNormalTypeError(ann, "top-level object can only expand into an eponymous object") + def InferredImplicitError(tree: Tree, inferred: Type, cx: Context): Unit = + InferredImplicitErrorImpl(tree, inferred, cx, isTyper = true) } /** This file will be the death of me. */ @@ -1077,7 +1109,7 @@ trait ContextErrors extends splain.SplainErrors { object InferErrorGen { - implicit val contextInferErrorGen = getContext + implicit val contextInferErrorGen: Context = getContext object PolyAlternativeErrorKind extends Enumeration { type ErrorType = Value @@ -1282,7 +1314,7 @@ trait ContextErrors extends splain.SplainErrors { object NamerErrorGen { - implicit val contextNamerErrorGen = context + implicit val contextNamerErrorGen: Context = context object SymValidateErrors extends Enumeration { val ImplicitConstr, ImplicitNotTermOrClass, ImplicitAtToplevel, @@ -1400,6 +1432,9 @@ trait ContextErrors extends splain.SplainErrors { issueNormalTypeError(tree, name.decode + " " + msg) } } + + def InferredImplicitError(tree: Tree, inferred: Type, cx: Context): Unit = + InferredImplicitErrorImpl(tree, inferred, cx, isTyper = false) } trait ImplicitsContextErrors { diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 381b468aaa8b..64b902efe302 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -51,13 +51,6 @@ trait Contexts { self: Analyzer => } private lazy val NoJavaMemberFound = (NoType, NoSymbol) - def ambiguousImports(imp1: ImportInfo, imp2: ImportInfo) = - LookupAmbiguous(s"it is imported twice in the same scope by\n$imp1\nand $imp2") - def ambiguousDefnAndImport(owner: Symbol, imp: ImportInfo) = - LookupAmbiguous(s"it is both defined in $owner and imported subsequently by \n$imp") - def ambiguousDefinitions(owner: Symbol, other: Symbol) = - LookupAmbiguous(s"it is both defined in $owner and available as ${other.fullLocationString}") - private lazy val startContext = NoContext.make( Template(List(), noSelfType, List()) setSymbol global.NoSymbol setType global.NoType, rootMirror.RootClass, @@ -590,7 +583,7 @@ trait Contexts { self: Analyzer => inSilentMode { try { set(disable = ImplicitsEnabled | EnrichmentEnabled) // restored by inSilentMode - tryOnce(false) + tryOnce(isLastTry = false) reporter.hasErrors } catch { case ex: CyclicReference => throw ex @@ -602,7 +595,7 @@ trait Contexts { self: Analyzer => // do last try if try with implicits enabled failed // (or if it was not attempted because they were disabled) if (doLastTry) - tryOnce(true) + tryOnce(isLastTry = true) } } @@ -896,7 +889,9 @@ trait Contexts { self: Analyzer => private def isSubClassOrCompanion(sub: Symbol, base: Symbol) = sub.isNonBottomSubClass(base) || (sub.isModuleClass && sub.linkedClassOfClass.isNonBottomSubClass(base)) || - (base.isJavaDefined && base.isModuleClass && sub.isNonBottomSubClass(base.linkedClassOfClass)) + (base.isJavaDefined && base.isModuleClass && ( + sub.isNonBottomSubClass(base.linkedClassOfClass) || + sub.isModuleClass && sub.linkedClassOfClass.isNonBottomSubClass(base.linkedClassOfClass))) /** Return the closest enclosing context that defines a subclass of `clazz` * or a companion object thereof, or `NoContext` if no such context exists. @@ -1249,30 +1244,13 @@ trait Contexts { self: Analyzer => || unit.exists && s.sourceFile != unit.source.file) ) - - /** Does the import just import the defined symbol? - * - * `import p._ ; package p { S }` where `p.S` is defined elsewhere. - * `S` is both made available in `p` and imported, an ambiguity. - * (The import is not used and is extraneous, but normally a definition - * in `p` would shadow and result in maybe a warning, not an error.) - * - * Don't attempt to interfere with correctness everywhere. - * `object X { def f = ??? ; def g = { import X.f ; f } }` - * - * This method doesn't use the ImportInfo, `imp1`. - */ - private[Contexts] def reconcileAmbiguousImportAndDef(name: Name, impSym: Symbol, defSym: Symbol): Boolean = { - val res = impSym == defSym - if (res) log(s"Suppressing ambiguous import, taking $defSym for $name") - res - } - /** If the given import is permitted, fetch the symbol and filter for accessibility. */ - private[Contexts] def importedAccessibleSymbol(imp: ImportInfo, sym: => Symbol): Symbol = + private[Contexts] def importedAccessibleSymbol(imp: ImportInfo, sym: => Symbol): Symbol = { if (isExcludedRootImport(imp)) NoSymbol - else sym.filter(isAccessible(_, imp.qual.tpe, superAccess = false)) + else sym.filter(s => s.exists && isAccessible(s, imp.qual.tpe, superAccess = false)) + // `exists` above completes SymbolLoaders, which sets the symbol's access flags (scala/bug#12736) + } private def isExcludedRootImport(imp: ImportInfo): Boolean = imp.isRootImport && excludedRootImportsCached.get(unit).exists(_.contains(imp.qual.symbol)) @@ -1379,6 +1357,40 @@ trait Contexts { self: Analyzer => private[this] var pre: Type = _ // the prefix type of defSym, if a class member private[this] var cx: Context = _ // the context under consideration private[this] var symbolDepth: Int = _ // the depth of the directly found symbol + private[this] var foundInPrefix: Boolean = _ // the symbol was found in pre + private[this] var foundInSuper: Boolean = _ // the symbol was found super of context class (inherited) + + def ambiguousImports(imp1: ImportInfo, imp2: ImportInfo) = + LookupAmbiguous(s"it is imported twice in the same scope by\n$imp1\nand $imp2") + def ambiguousDefnAndImport(owner: Symbol, imp: ImportInfo) = + LookupAmbiguous(s"it is both defined in $owner and imported subsequently by \n$imp") + def ambiguousDefinitions(outer: Symbol, inherited: Symbol, foundInSuper: Boolean, currentClass: Symbol) = + if (foundInSuper) { + if (inherited.isImplicit) None + else { + val outer1 = outer.alternatives.head + val inherited1 = inherited.alternatives.head + val classDesc = if (currentClass.isAnonymousClass) "anonymous class" else currentClass.toString + val parent = currentClass.parentSymbols.find(_.isNonBottomSubClass(inherited1.owner)).getOrElse(NoSymbol) + val inherit = if (parent.exists && parent != inherited1.owner) s", inherited through parent $parent" else "" + val message = + sm"""|it is both defined in the enclosing ${outer1.owner} and inherited in the enclosing $classDesc as $inherited1 (defined in ${inherited1.ownsString}$inherit) + |In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. + |Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.${outer1.name}`.""" + // For now (2.13.11), warn under Xsource:3. We'll consider warning by default (and erring in Xsource:3) in 2.13.12 + if (currentRun.isScala3) { + // passing the message to `typedIdent` as attachment, we don't have the position here to report the warning + inherited.updateAttachment(LookupAmbiguityWarning( + sm"""|reference to ${outer1.name} is ambiguous; + |$message + |Or use `-Wconf:msg=legacy-binding:s` to silence this warning.""")) + // Some(LookupAmbiguous(message)) // to make it an error in 2.13.12 + None + } else + None + } + } else + Some(LookupAmbiguous(s"it is both defined in ${outer.owner} and available as ${inherited.fullLocationString}")) def apply(thisContext: Context, name: Name)(qualifies: Symbol => Boolean): NameLookup = { lookupError = null @@ -1387,6 +1399,8 @@ trait Contexts { self: Analyzer => pre = NoPrefix cx = thisContext symbolDepth = -1 + foundInPrefix = false + foundInSuper = false def finish(qual: Tree, sym: Symbol): NameLookup = ( if (lookupError ne null) lookupError @@ -1403,8 +1417,8 @@ trait Contexts { self: Analyzer => finish(qual, sym) } - def lookupInPrefix(name: Name): Symbol = { - if (thisContext.unit.isJava) { + def lookupInPrefix(name: Name): Symbol = + if (thisContext.unit.isJava) thisContext.javaFindMember(pre, name, qualifies) match { case (_, NoSymbol) => NoSymbol @@ -1412,17 +1426,16 @@ trait Contexts { self: Analyzer => pre = pre1 sym } - } else { + else pre.member(name).filter(qualifies) - } - } + def accessibleInPrefix(s: Symbol) = thisContext.isAccessible(s, pre, superAccess = false) def searchPrefix = { cx = cx.enclClass val found0 = lookupInPrefix(name) - val found1 = found0 filter accessibleInPrefix + val found1 = found0.filter(accessibleInPrefix) if (found0.exists && !found1.exists && inaccessible == null) inaccessible = LookupInaccessible(found0, analyzer.lastAccessCheckDetails) @@ -1469,16 +1482,26 @@ trait Contexts { self: Analyzer => } // cx.scope eq null arises during FixInvalidSyms in Duplicators - def nextDefinition(): Unit = + def nextDefinition(lastDef: Symbol, lastPre: Type): Unit = { + var inPrefix = false + defSym = NoSymbol while (defSym == NoSymbol && (cx ne NoContext) && (cx.scope ne null)) { pre = cx.enclClass.prefix - defSym = lookupInScope(cx.owner, cx.enclClass.prefix, cx.scope) match { - case NoSymbol => searchPrefix - case found => found + defSym = lookupInScope(cx.owner, pre, cx.scope) match { + case NoSymbol => inPrefix = true; searchPrefix + case found => inPrefix = false; found } if (!defSym.exists) cx = cx.outer // push further outward } - nextDefinition() + if ((defSym.isAliasType || lastDef.isAliasType) && pre.memberType(defSym) =:= lastPre.memberType(lastDef)) + defSym = NoSymbol + if (defSym.isStable && lastDef.isStable && + (lastPre.memberType(lastDef).termSymbol == defSym || pre.memberType(defSym).termSymbol == lastDef)) + defSym = NoSymbol + foundInPrefix = inPrefix && defSym.exists + foundInSuper = foundInPrefix && defSym.alternatives.forall(_.owner != cx.owner) + } + nextDefinition(NoSymbol, NoPrefix) if (symbolDepth < 0) symbolDepth = cx.depth @@ -1488,6 +1511,9 @@ trait Contexts { self: Analyzer => val importCursor = new ImportCursor(thisContext, name) import importCursor.{imp1, imp2} + // The symbol resolved by the given import for `name`, paired with the selector that was used. + // If `requireExplicit`, then only "named" or "specific" selectors are considered. + // In addition, the symbol must be accessible (in the current context) and satisfy the `qualifies` predicate. def lookupImport(imp: ImportInfo, requireExplicit: Boolean): (ImportSelector, Symbol) = { val pair @ (sel, sym) = imp.importedSelectedSymbol(name, requireExplicit) if (sym == NoSymbol) pair @@ -1507,12 +1533,17 @@ trait Contexts { self: Analyzer => * * Scala: Bindings of different kinds have a defined precedence order: * - * 1) Definitions and declarations that are local, inherited, or made available by - * a package clause and also defined in the same compilation unit as the reference, have highest precedence. + * 1) Definitions and declarations in lexical scope that are not top-level have the highest precedence. + * 1b) Definitions and declarations that are either inherited, or made + * available by a package clause and also defined in the same compilation unit + * as the reference to them, have the next highest precedence. + * (Only in -Xsource:3, same precedence as 1 with a warning in Scala 2.) * 2) Explicit imports have next highest precedence. * 3) Wildcard imports have next highest precedence. - * 4) Definitions made available by a package clause, but not also defined in the same compilation unit - * as the reference, have lowest precedence. Also "root" imports added implicitly. + * 4) Bindings made available by a package clause, + * but not also defined in the same compilation unit as the reference to them, + * as well as bindings supplied by the compiler but not explicitly written in source code, + * have the lowest precedence. */ def foreignDefined = defSym.exists && thisContext.isPackageOwnedInDifferentUnit(defSym) // SI-2458 @@ -1543,6 +1574,12 @@ trait Contexts { self: Analyzer => advanceCursorToNextImport() val preferDef: Boolean = defSym.exists && (!impSym.exists || { + // Does the import just import the defined symbol? + def reconcileAmbiguousImportAndDef: Boolean = { + val res = impSym == defSym + if (res) log(s"Suppressing ambiguous import, taking $defSym for $name") + res + } // 4) root imported symbols have same (lowest) precedence as package-owned symbols in different compilation units. if (imp1.depth < symbolDepth && imp1.isRootImport && foreignDefined) true @@ -1555,36 +1592,47 @@ trait Contexts { self: Analyzer => // Defined symbols take precedence over erroneous imports. else if (impSym.isError || impSym.name == nme.CONSTRUCTOR) true - // Try to reconcile them before giving up, at least if the def is not visible - else if (foreignDefined && thisContext.reconcileAmbiguousImportAndDef(name, impSym, defSym)) + // Try to reconcile them before giving up + else if (foreignDefined && reconcileAmbiguousImportAndDef) true // Otherwise they are irreconcilably ambiguous else return ambiguousDefnAndImport(defSym.alternatives.head.owner, imp1) }) - // If the defSym is at 4, and there is a def at 1 in scope due to packaging, then the reference is ambiguous. - if (foreignDefined && !defSym.hasPackageFlag && !thisContext.unit.isJava) { + // If the defSym is at 4, and there is a def at 1b in scope due to packaging, then the reference is ambiguous. + // Also if defSym is at 1b inherited, the reference can be rendered ambiguous by a def at 1a in scope. + val possiblyAmbiguousDefinition = + foundInSuper && cx.owner.isClass || + foreignDefined && !defSym.hasPackageFlag + if (possiblyAmbiguousDefinition && !thisContext.unit.isJava) { val defSym0 = defSym val pre0 = pre val cx0 = cx + val depth0 = symbolDepth + val wasFoundInSuper = foundInSuper + val foundCompetingSymbol: () => Boolean = + if (foreignDefined) () => !foreignDefined + else () => !defSym.owner.isPackageObjectOrClass && !foundInSuper && !foreignDefined while ((cx ne NoContext) && cx.depth >= symbolDepth) cx = cx.outer + if (wasFoundInSuper) + while ((cx ne NoContext) && (cx.owner eq cx0.owner)) cx = cx.outer var done = false while (!done) { - defSym = NoSymbol - nextDefinition() - done = (cx eq NoContext) || defSym.exists && !foreignDefined + nextDefinition(defSym0, pre0) + done = (cx eq NoContext) || defSym.exists && foundCompetingSymbol() if (!done && (cx ne NoContext)) cx = cx.outer } if (defSym.exists && (defSym ne defSym0)) { val ambiguity = - if (preferDef) ambiguousDefinitions(owner = defSym.owner, defSym0) - else ambiguousDefnAndImport(owner = defSym.owner, imp1) - return ambiguity + if (preferDef) ambiguousDefinitions(defSym, defSym0, wasFoundInSuper, cx0.enclClass.owner) + else Some(ambiguousDefnAndImport(owner = defSym.owner, imp1)) + if (ambiguity.nonEmpty) return ambiguity.get } defSym = defSym0 pre = pre0 cx = cx0 + symbolDepth = depth0 } if (preferDef) impSym = NoSymbol else defSym = NoSymbol diff --git a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala index 4d18d7b86953..4021cd9e4ba4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala @@ -58,7 +58,7 @@ abstract class Duplicators extends Analyzer { private var envSubstitution: SubstTypeMap = _ private class SubstSkolemsTypeMap(from: List[Symbol], to: List[Type]) extends SubstTypeMap(from, to) { - protected override def matches(sym1: Symbol, sym2: Symbol) = + override protected def matches(sym1: Symbol, sym2: Symbol) = if (sym2.isTypeSkolem) sym2.deSkolemize eq sym1 else sym1 eq sym2 } diff --git a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala index d48ca53570c8..439aedac1c26 100644 --- a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala +++ b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala @@ -24,7 +24,7 @@ import symtab.Flags._ trait EtaExpansion { self: Analyzer => import global._ - /** Expand partial method application `p.f(es_1)...(es_n)`. Does not support dependent method types (yet). + /** Expand partial method application `p.f(es_1)...(es_n)`. * * We expand this to the following block, which evaluates * the target of the application and its supplied arguments if needed (they are not stable), diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 389a77902a9b..3f937ecdadb8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -741,23 +741,14 @@ trait Implicits extends splain.SplainData { as = as.tail } } else { - while (ps.nonEmpty && as.nonEmpty) { + while (!(ps.isEmpty || as.isEmpty)) { if (!(as.head <:< ps.head.tpe)) return false ps = ps.tail as = as.tail } } - ps.isEmpty && as.nonEmpty && { - val lastArg = as.head - as.tail.isEmpty && { - lastArg match { - case RefinedType(_, syms) if !syms.isEmpty => - syms.reverseIterator.exists(x => context.isAccessible(restpe.nonPrivateMember(x.name), restpe)) - case _ => true - } - } && loop(restpe, lastArg) - } + ps.isEmpty && !as.isEmpty && as.tail.isEmpty && loop(restpe, as.head) } } @@ -766,7 +757,14 @@ trait Implicits extends splain.SplainData { case NullaryMethodType(restpe) => loop(restpe, pt) case PolyType(_, restpe) => loop(restpe, pt) case ExistentialType(_, qtpe) => if (fast) loop(qtpe, pt) else methodToExpressionTp(tp) <:< pt // is !fast case needed?? - case _ => if (fast) isPlausiblySubType(tp, pt) else tp <:< pt + case _ => (if (fast) isPlausiblySubType(tp, pt) else tp <:< pt) && { + pt match { + case RefinedType(_, syms) if !syms.isEmpty => + syms.reverseIterator.exists(x => context.isAccessible(tp.nonPrivateMember(x.name), tp)) + case _ => + true + } + } } loop(tp0, pt0) } @@ -1267,7 +1265,7 @@ trait Implicits extends splain.SplainData { if (invalidImplicits.nonEmpty) setAddendum(pos, () => - s"\n Note: implicit ${invalidImplicits.head} is not applicable here because it comes after the application point and it lacks an explicit result type" + s"\n Note: implicit ${invalidImplicits.head} is not applicable here because it comes after the application point and it lacks an explicit result type.${if (invalidImplicits.head.isModule) " An object can be written as a lazy val with an explicit type." else ""}" ) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 7c08012aad1c..c5e9b5444bd3 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -286,7 +286,7 @@ trait Infer extends Checkable { ErrorUtils.issueTypeError(error)(context) ErrorType } - def accessible = sym filter (alt => context.isAccessible(alt, pre, site.isInstanceOf[Super])) match { + def accessible = sym.filter(context.isAccessible(_, pre, site.isInstanceOf[Super])) match { case NoSymbol if sym.isJavaDefined && context.unit.isJava => sym // don't try to second guess Java; see #4402 case sym1 => sym1 } @@ -1383,12 +1383,14 @@ trait Infer extends Checkable { * matches prototype `pt`, if it exists. * If several alternatives match `pt`, take parameterless one. * If no alternative matches `pt`, take the parameterless one anyway. + * (There may be more than one parameterless alternative, in particular, + * badly overloaded default args or case class elements. These are detected elsewhere.) */ def inferExprAlternative(tree: Tree, pt: Type): Tree = { val c = context class InferTwice(pre: Type, alts: List[Symbol]) extends c.TryTwice { def tryOnce(isSecondTry: Boolean): Unit = { - val alts0 = alts filter (alt => isWeaklyCompatible(pre memberType alt, pt)) + val alts0 = alts.filter(alt => isWeaklyCompatible(pre.memberType(alt), pt)) val alts1 = if (alts0.isEmpty) alts else alts0 val bests = bestAlternatives(alts1) { (sym1, sym2) => val tp1 = memberTypeForSpecificity(pre, sym1, tree) @@ -1399,22 +1401,35 @@ trait Infer extends Checkable { || isStrictlyMoreSpecific(tp1, tp2, sym1, sym2) ) } - // todo: missing test case for bests.isEmpty + def finish(s: Symbol): Unit = tree.setSymbol(s).setType(pre.memberType(s)) + def paramlessOr(error: => Unit): Unit = { + val paramless = + if (isSecondTry) + alts.find { alt => val ps = alt.info.paramss; ps.isEmpty || ps.tail.isEmpty && ps.head.isEmpty } + else None + paramless match { + case Some(alt) => finish(alt) + case None => error + } + } bests match { - case best :: Nil => tree setSymbol best setType (pre memberType best) + case best :: Nil => + finish(best) case best :: competing :: _ if alts0.nonEmpty => - // scala/bug#6912 Don't give up and leave an OverloadedType on the tree. - // Originally I wrote this as `if (secondTry) ... `, but `tryTwice` won't attempt the second try - // unless an error is issued. We're not issuing an error, in the assumption that it would be - // spurious in light of the erroneous expected type - if (pt.isErroneous) setError(tree) - else AmbiguousExprAlternativeError(tree, pre, best, competing, pt, isSecondTry) - case _ => if (bests.isEmpty || alts0.isEmpty) NoBestExprAlternativeError(tree, pt, isSecondTry) + // If erroneous expected type, don't issue spurious error and don't `tryTwice` again with implicits. + // scala/bug#6912 except it does not loop + paramlessOr { + if (pt.isErroneous) setError(tree) + else AmbiguousExprAlternativeError(tree, pre, best, competing, pt, isSecondTry) + } + case _ if bests.isEmpty || alts0.isEmpty => + paramlessOr(NoBestExprAlternativeError(tree, pt, isSecondTry)) + case _ => } } } tree.tpe match { - case OverloadedType(pre, alts) => (new InferTwice(pre, alts)).apply() ; tree + case OverloadedType(pre, alts) => (new InferTwice(pre, alts)).apply(); tree case _ => tree } } @@ -1512,11 +1527,12 @@ trait Infer extends Checkable { val applicable = overloadsToConsiderBySpecificity(alts filter isAltApplicable(pt), argtpes, varargsStar) // println(s"bestForExpectedType($argtpes, $pt): $alts -app-> ${alts filter isAltApplicable(pt)} -arity-> $applicable") val ranked = bestAlternatives(applicable)(rankAlternatives) + def finish(s: Symbol): Unit = tree.setSymbol(s).setType(pre.memberType(s)) ranked match { case best :: competing :: _ => AmbiguousMethodAlternativeError(tree, pre, best, competing, argtpes, pt, isLastTry) // ambiguous - case best :: Nil => tree setSymbol best setType (pre memberType best) // success - case Nil if pt.isWildcard => NoBestMethodAlternativeError(tree, argtpes, pt, isLastTry) // failed - case Nil => bestForExpectedType(WildcardType, isLastTry) // failed, but retry with WildcardType + case best :: nil => finish(best) + case nil if pt.isWildcard => NoBestMethodAlternativeError(tree, argtpes, pt, isLastTry) // failed + case nil => bestForExpectedType(WildcardType, isLastTry) // failed, but retry with WildcardType } } diff --git a/src/compiler/scala/tools/nsc/typechecker/MacroAnnotationNamers.scala b/src/compiler/scala/tools/nsc/typechecker/MacroAnnotationNamers.scala index de3a5facd6f3..1a4ab24e748c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MacroAnnotationNamers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MacroAnnotationNamers.scala @@ -386,7 +386,7 @@ trait MacroAnnotationNamers { self: Analyzer => sym.setInfo(new MaybeExpandeeCompleter(tree) { override def kind = s"maybeExpandeeCompleter for ${sym.accurateKindString} ${sym.rawname}#${sym.id}" override def maybeExpand(): Unit = { - val companion = if (tree.isInstanceOf[ClassDef]) patchedCompanionSymbolOf(sym, context) else NoSymbol + val companion = if (this.tree.isInstanceOf[ClassDef]) patchedCompanionSymbolOf(sym, context) else NoSymbol def maybeExpand(annotation: Tree, annottee: Tree, maybeExpandee: Tree): Option[List[Tree]] = if (context.macrosEnabled) { // TODO: when is this bit flipped -- can we pull this check out farther? @@ -395,7 +395,7 @@ trait MacroAnnotationNamers { self: Analyzer => if (mann.isClass && mann.hasFlag(MACRO)) { assert(!currentRun.compiles(mann), mann) val annm = prepareAnnotationMacro(annotation, mann, sym, annottee, maybeExpandee) - expandAnnotationMacro(tree, annm) + expandAnnotationMacro(this.tree, annm) // if we encounter an error, we just return None, so that other macro annotations can proceed // this is unlike macroExpand1 when any error in an expandee blocks expansions // there it's necessary in order not to exacerbate typer errors @@ -424,7 +424,7 @@ trait MacroAnnotationNamers { self: Analyzer => enterSyms(expanded) // TODO: we can't reliably expand into imports, because they won't be accounted by definitions below us case None => markNotExpandable(sym) - finishSymbolNotExpandee(tree) + finishSymbolNotExpandee(this.tree) } // take care of the companion if it's no longer needed diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 89b110ab2ece..6323357a5c5b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -18,6 +18,7 @@ import scala.collection.mutable import symtab.Flags._ import scala.reflect.internal.util.ListOfNil import scala.tools.nsc.Reporting.WarningCategory +import scala.util.chaining._ /** This trait declares methods to create symbols and to enter them into scopes. * @@ -426,29 +427,27 @@ trait Namers extends MethodSynthesis { /** Given a ClassDef or ModuleDef, verifies there isn't a companion which * has been defined in a separate file. */ - @nowarn("cat=lint-nonlocal-return") def validateCompanionDefs(tree: ImplDef): Unit = { - val sym = tree.symbol orElse { return } - val ctx = if (context.owner.isPackageObjectClass) context.outer else context - val module = if (sym.isModule) sym else ctx.scope lookupModule tree.name - val clazz = if (sym.isClass) sym else ctx.scope lookupClass tree.name - val fails = ( - module.isModule - && clazz.isClass - && !module.isSynthetic - && !clazz.isSynthetic - && (clazz.sourceFile ne null) - && (module.sourceFile ne null) - && !(module isCoDefinedWith clazz) - && module.exists - && clazz.exists - && (currentRun.compiles(clazz) == currentRun.compiles(module)) - ) - if (fails) { - reporter.error(tree.pos, ( - s"Companions '$clazz' and '$module' must be defined in same file:\n" - + s" Found in ${clazz.sourceFile.canonicalPath} and ${module.sourceFile.canonicalPath}") + val sym = tree.symbol + if (sym != NoSymbol) { + val ctx = if (context.owner.isPackageObjectClass) context.outer else context + val module = if (sym.isModule) sym else ctx.scope.lookupModule(tree.name) + val clazz = if (sym.isClass) sym else ctx.scope.lookupClass(tree.name) + val fails = ( + module.isModule + && clazz.isClass + && !module.isSynthetic + && !clazz.isSynthetic + && (clazz.sourceFile ne null) + && (module.sourceFile ne null) + && !module.isCoDefinedWith(clazz) + && module.exists + && clazz.exists + && currentRun.compiles(clazz) == currentRun.compiles(module) ) + if (fails) reporter.error(tree.pos, + sm"""|Companions '$clazz' and '$module' must be defined in same file: + | Found in ${clazz.sourceFile.canonicalPath} and ${module.sourceFile.canonicalPath}""") } } @@ -796,6 +795,8 @@ trait Namers extends MethodSynthesis { tree.symbol = enterClassSymbol(tree) tree.symbol setInfo completerOf(tree) + if (tree.symbol.isJava) patmat.javaClassesByUnit.get(tree.symbol.pos.source).foreach(_.addOne(tree.symbol)) + if (mods.isCase) { val m = ensureCompanionObject(tree, caseModuleDef) m.moduleClass.updateAttachment(new ClassForCaseCompanionAttachment(tree)) @@ -1120,15 +1121,36 @@ trait Namers extends MethodSynthesis { /** Computes the type of the body in a ValDef or DefDef, and * assigns the type to the tpt's node. Returns the type. + * + * Under `-Xsource:3`, use `pt`, the type of the overridden member. + * But preserve the precise type of a whitebox macro. + * For `def f = macro g`, here we see `def f = xp(g)` the expansion, + * not the `isMacro` case: `openMacros` will be nonEmpty. + * For `def m = f`, retrieve the typed RHS and check if it is an expansion; + * in that case, check if the expandee `f` is whitebox and preserve + * the precise type if it is. The user must provide an explicit type + * to "opt out" of the inferred narrow type; in Scala 3, they would + * inline the def to "opt in". */ private def assignTypeToTree(tree: ValOrDefDef, defnTyper: Typer, pt: Type): Type = { val rhsTpe = tree match { - case ddef: DefDef if tree.symbol.isTermMacro => defnTyper.computeMacroDefType(ddef, pt) + case ddef: DefDef if tree.symbol.isTermMacro => defnTyper.computeMacroDefType(ddef, pt) // unreached, see methodSig case _ => defnTyper.computeType(tree.rhs, pt) } tree.tpt.defineType { - if (currentRun.isScala3 && !pt.isWildcard && pt != NoType && !pt.isErroneous && openMacros.isEmpty) pt + val inferOverridden = currentRun.isScala3 && + !pt.isWildcard && pt != NoType && !pt.isErroneous && + openMacros.isEmpty && { + context.unit.transformed.get(tree.rhs) match { + case Some(t) if t.hasAttachment[MacroExpansionAttachment] => + val xp = macroExpandee(t) + xp.symbol == null || isBlackbox(xp.symbol) + case _ => true + } + } + if (inferOverridden) pt else dropIllegalStarTypes(widenIfNecessary(tree.symbol, rhsTpe, pt)) + .tap(InferredImplicitError(tree, _, context)) }.setPos(tree.pos.focus) tree.tpt.tpe } @@ -1160,23 +1182,29 @@ trait Namers extends MethodSynthesis { private def templateSig(templ: Template): Type = { val clazz = context.owner - val parentTrees = typer.typedParentTypes(templ) - val pending = mutable.ListBuffer[AbsTypeError]() parentTrees foreach { tpt => val ptpe = tpt.tpe - if (!ptpe.isError) { + if (!ptpe.isError && !phase.erasedTypes) { val psym = ptpe.typeSymbol - val sameSourceFile = context.unit.source.file == psym.sourceFile - - if (psym.isSealed && !phase.erasedTypes) - if (sameSourceFile) - psym addChild context.owner + if (psym.isSealed) { + val sameSourceFile = context.unit.source.file == psym.sourceFile + val okChild = + if (psym.isJava) + psym.attachments.get[PermittedSubclassSymbols] match { + case Some(permitted) => permitted.permits.exists(_ == clazz) + case _ => sameSourceFile + } + else + sameSourceFile + if (okChild) + psym.addChild(clazz) else pending += ParentSealedInheritanceError(tpt, psym) - if (psym.isLocalToBlock && psym.isClass && !phase.erasedTypes) - psym addChild context.owner + } + if (psym.isLocalToBlock && psym.isClass) + psym.addChild(clazz) } } pending.foreach(ErrorUtils.issueTypeError) @@ -1194,13 +1222,12 @@ trait Namers extends MethodSynthesis { // add apply and unapply methods to companion objects of case classes, // unless they exist already; here, "clazz" is the module class - if (clazz.isModuleClass) { + if (clazz.isModuleClass) clazz.attachments.get[ClassForCaseCompanionAttachment] foreach { cma => val cdef = cma.caseClass assert(cdef.mods.isCase, "expected case class: "+ cdef) addApplyUnapply(cdef, templateNamer) } - } // add the copy method to case classes; this needs to be done here, not in SyntheticMethods, because // the namer phase must traverse this copy method to create default getters for its parameters. @@ -1210,7 +1237,7 @@ trait Namers extends MethodSynthesis { val modClass = companionSymbolOf(clazz, context).moduleClass modClass.attachments.get[ClassForCaseCompanionAttachment] foreach { cma => val cdef = cma.caseClass - def hasCopy = (decls containsName nme.copy) || parents.exists(_.member(nme.copy).exists) + def hasCopy = decls.containsName(nme.copy) || parents.exists { p => val ov = p.member(nme.copy); ov.exists && !ov.isDeferred } // scala/bug#5956 needs (cdef.symbol == clazz): there can be multiple class symbols with the same name if (cdef.symbol == clazz && !hasCopy) @@ -1246,6 +1273,11 @@ trait Namers extends MethodSynthesis { val res = GenPolyType(tparams0, resultType) val pluginsTp = pluginsTypeSig(res, typer, cdef, WildcardType) + cdef.getAndRemoveAttachment[PermittedSubclasses].foreach { permitted => + clazz.updateAttachment[PermittedSubclassSymbols] { + PermittedSubclassSymbols(permitted.permits.map(typer.typed(_, Mode.NOmode).symbol)) + } + } // Already assign the type to the class symbol (monoTypeCompleter will do it again). // Allows isDerivedValueClass to look at the info. @@ -1368,10 +1400,11 @@ trait Namers extends MethodSynthesis { deskolemizedPolySig(vparamSymss, resTpGiven) // Must be lazy about the schema to avoid cycles in neg/t5093.scala - val overridden = + def computeOverridden(immediate: Boolean) = if (!canOverride) NoSymbol - else safeNextOverriddenSymbolLazySchema(meth, methodSigApproxUnknownArgs _) + else safeNextOverriddenSymbolLazySchema(meth, methodSigApproxUnknownArgs _, immediate) + val overridden = computeOverridden(immediate = false) /* * If `meth` doesn't have an explicit return type, extract the return type from the method * overridden by `meth` (if there's an unique one). This type is later used as the expected @@ -1380,7 +1413,6 @@ trait Namers extends MethodSynthesis { * * If the result type is missing, assign a MethodType to `meth` that's constructed using this return type. * This allows omitting the result type for recursive methods. - * */ val resTpFromOverride = if (!inferResTp || overridden == NoSymbol || overridden.isOverloaded) resTpGiven @@ -1439,7 +1471,7 @@ trait Namers extends MethodSynthesis { val resTp = { // When return type is inferred, we don't just use resTpFromOverride -- it must be packed and widened. - // Here, C.f has type String: + // Here, C.f has type String (unless -Xsource:3): // trait T { def f: Object }; class C extends T { def f = "" } // using resTpFromOverride as expected type allows for the following (C.f has type A): // trait T { def f: A }; class C extends T { implicit def b2a(t: B): A = ???; def f = new B } @@ -1454,21 +1486,15 @@ trait Namers extends MethodSynthesis { // Add a () parameter section if this overrides some method with () parameters val vparamSymssOrEmptyParamsFromOverride = { - // must check `.info.isInstanceOf[MethodType]`, not `.isMethod`! - // Note that matching MethodType of NullaryMethodType must be nilary not nelary. - def overriddenNilary(sym: Symbol) = sym.info.isInstanceOf[MethodType] - if (overridden != NoSymbol && vparamSymss.isEmpty && overridden.alternatives.exists(overriddenNilary)) { - def exempt() = meth.overrides.exists(sym => sym.isJavaDefined || isUniversalMember(sym)) - val msg = "method without a parameter list overrides a method with a single empty one" - def error(): Unit = if (!exempt()) { - ErrorUtils.issueNormalTypeError(ddef, msg) - ddef.tpt.defineType(ErrorType) - } - def warn(): Unit = if (!exempt()) { - context.warning(ddef.pos, msg, WarningCategory.OtherNullaryOverride) - meth.updateAttachment(NullaryOverrideAdapted) - } - context.unit.toCheck += (if (currentRun.isScala3) error _ else warn _) + // check the first override for paren purposes + def overridesNilary: Boolean = { + val toCheck = if (currentRun.isScala3) computeOverridden(immediate = true) else overridden + // must check `.info.isInstanceOf[MethodType]`, not `.isMethod`, to exclude NullaryMethodType. + // Note that the matching MethodType of a NullaryMethodType must be nilary not nelary. + toCheck != NoSymbol && toCheck.alternatives.exists(_.info.isInstanceOf[MethodType]) + } + if (vparamSymss.isEmpty && overridesNilary) { + meth.updateAttachment(NullaryOverrideAdapted) ListOfNil } else vparamSymss } @@ -1701,82 +1727,91 @@ trait Namers extends MethodSynthesis { } } - private def valDefSig(vdef: ValDef) = { + private def valDefSig(vdef: ValDef): Type = { val ValDef(_, _, tpt, rhs) = vdef - val result = - if (tpt.isEmpty) { - if (rhs.isEmpty) { - MissingParameterOrValTypeError(tpt) - ErrorType - } else { - // enterGetterSetter assigns the getter's symbol to a ValDef when there's no underlying field - // (a deferred val or most vals defined in a trait -- see Field.noFieldFor) - val isGetter = vdef.symbol hasFlag ACCESSOR - - val pt = { - val valOwner = owner.owner - // there's no overriding outside of classes, and we didn't use to do this in 2.11, so provide opt-out - - if (!valOwner.isClass) WildcardType - else { - // normalize to getter so that we correctly consider a val overriding a def - // (a val's name ends in a " ", so can't compare to def) - val overridingSym = if (isGetter) vdef.symbol else vdef.symbol.getterIn(valOwner) + def inferredValTpt: Type = { + // enterGetterSetter assigns the getter's symbol to a ValDef when there's no underlying field + // (a deferred val or most vals defined in a trait -- see Field.noFieldFor) + val isGetter = vdef.symbol hasFlag ACCESSOR + + val pt: Type = { + val valOwner = owner.owner + if (!valOwner.isClass) WildcardType + else { + // normalize to getter so that we correctly consider a val overriding a def + // (a val's name ends in a " ", so can't compare to def) + val overridingSym = if (isGetter) vdef.symbol else vdef.symbol.getterIn(valOwner) - // We're called from an accessorTypeCompleter, which is completing the info for the accessor's symbol, - // which may or may not be `vdef.symbol` (see isGetter above) - val overridden = safeNextOverriddenSymbol(overridingSym) + // We're called from an accessorTypeCompleter, which is completing the info for the accessor's symbol, + // which may or may not be `vdef.symbol` (see isGetter above) + val overridden = safeNextOverriddenSymbol(overridingSym) - if (overridden == NoSymbol || overridden.isOverloaded) WildcardType - else valOwner.thisType.memberType(overridden).resultType - } - } + if (overridden == NoSymbol || overridden.isOverloaded) WildcardType + else valOwner.thisType.memberType(overridden).resultType + } + } - def patchSymInfo(tp: Type): Unit = - if (pt ne WildcardType) // no patching up to do if we didn't infer a prototype - vdef.symbol setInfo (if (isGetter) NullaryMethodType(tp) else tp) + def patchSymInfo(tp: Type): Unit = + if (pt ne WildcardType) // no patching up to do if we didn't infer a prototype + vdef.symbol.setInfo { if (isGetter) NullaryMethodType(tp) else tp } - patchSymInfo(pt) + patchSymInfo(pt) - // derives the val's result type from type checking its rhs under the expected type `pt` - // vdef.tpt is mutated, and `vdef.tpt.tpe` is `assignTypeToTree`'s result - val tptFromRhsUnderPt = assignTypeToTree(vdef, typer, pt) + // derives the val's result type from type checking its rhs under the expected type `pt` + // vdef.tpt is mutated, and `vdef.tpt.tpe` is `assignTypeToTree`'s result + val tptFromRhsUnderPt = assignTypeToTree(vdef, typer, pt) - // need to re-align with assignTypeToTree, as the type we're returning from valDefSig (tptFromRhsUnderPt) - // may actually go to the accessor, not the valdef (and if assignTypeToTree returns a subtype of `pt`, - // we would be out of synch between field and its accessors), and thus the type completer won't - // fix the symbol's info for us -- we set it to tmpInfo above, which may need to be improved to tptFromRhsUnderPt - if (!isGetter) patchSymInfo(tptFromRhsUnderPt) + // need to re-align with assignTypeToTree, as the type we're returning from valDefSig (tptFromRhsUnderPt) + // may actually go to the accessor, not the valdef (and if assignTypeToTree returns a subtype of `pt`, + // we would be out of synch between field and its accessors), and thus the type completer won't + // fix the symbol's info for us -- we set it to tmpInfo above, which may need to be improved to tptFromRhsUnderPt + if (!isGetter) patchSymInfo(tptFromRhsUnderPt) - tptFromRhsUnderPt - } + tptFromRhsUnderPt + } + val result: Type = + if (tpt.isEmpty) { + if (rhs.isEmpty) { MissingParameterOrValTypeError(tpt); ErrorType } + else inferredValTpt } else { val tptTyped = typer.typedType(tpt) context.unit.transformed(tpt) = tptTyped tptTyped.tpe } - // println(s"val: $result / ${vdef.tpt.tpe} / ") - pluginsTypeSig(result, typer, vdef, if (tpt.isEmpty) WildcardType else result) } // Pretend we're an erroneous symbol, for now, so that we match while finding the overridden symbol, // but are not considered during implicit search. - private def safeNextOverriddenSymbol(sym: Symbol, schema: Type = ErrorType): Symbol = { + // `immediate` for immediate override only, not narrowest override + private def safeNextOverriddenSymbol(sym: Symbol, schema: Type = ErrorType, immediate: Boolean = false): Symbol = { val savedInfo = sym.rawInfo val savedFlags = sym.rawflags try { sym setInfo schema - sym.nextOverriddenSymbol + // pick the overridden symbol with narrowest type; dotty uses intersection + if (!immediate && currentRun.isScala3) { + def typeOf(s: Symbol): Type = { + val t = if (s.isMethod) s.asMethod.returnType else s.tpe + t.asSeenFrom(sym.owner.thisType, s.owner) + } + sym.allOverriddenSymbols match { + case Nil => NoSymbol + case overridden :: candidates => + candidates.foldLeft(overridden)((acc, o) => if (typeOf(o) <:< typeOf(acc)) o else acc) + } + } + else + sym.nextOverriddenSymbol } finally { sym setInfo savedInfo // setInfo resets the LOCKED flag, so restore saved flags as well sym.rawflags = savedFlags } } - private def safeNextOverriddenSymbolLazySchema(sym: Symbol, schema: () => Type): Symbol = - safeNextOverriddenSymbol(sym, new LazyType { override def complete(sym: Symbol): Unit = sym setInfo schema() }) + private def safeNextOverriddenSymbolLazySchema(sym: Symbol, schema: () => Type, immediate: Boolean): Symbol = + safeNextOverriddenSymbol(sym, new LazyType { override def complete(sym: Symbol): Unit = sym setInfo schema() }, immediate) //@M! an abstract type definition (abstract type member/type parameter) diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index 0e169c0d80b9..941a36f318f0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -405,7 +405,7 @@ trait NamesDefaults { self: Analyzer => */ def missingParams[T](args: List[T], params: List[Symbol], argName: T => Option[Name]): (List[Symbol], Boolean) = { // The argument list contains first a mix of positional args and named args that are on the - // right parameter position, and then a number or named args on different positions. + // right parameter position, and then a number of named args on different positions. // collect all named arguments whose position does not match the parameter they define val namedArgsOnChangedPosition = args.zip(params) dropWhile { @@ -413,9 +413,8 @@ trait NamesDefaults { self: Analyzer => val n = argName(arg) // drop the argument if // - it's not named, or - // - it's named, but defines the parameter on its current position, or - // - it's named, but none of the parameter names matches (treated as a positional argument, an assignment expression) - n.isEmpty || n.get == param.name || params.forall(_.name != n.get) + // - it's named, but defines the parameter on its current position + n.isEmpty || n.get == param.name } map (_._1) val paramsWithoutPositionalArg = params.drop(args.length - namedArgsOnChangedPosition.length) @@ -433,6 +432,8 @@ trait NamesDefaults { self: Analyzer => * Extend the argument list `givenArgs` with default arguments. Defaults are added * as named arguments calling the corresponding default getter. * + * Returns the extended arg list and missing parameters if any. + * * Example: given * def foo(x: Int = 2, y: String = "def") * foo(y = "lt") @@ -443,8 +444,8 @@ trait NamesDefaults { self: Analyzer => pos: scala.reflect.internal.util.Position, context: Context): (List[Tree], List[Symbol]) = { if (givenArgs.length < params.length) { val (missing, positional) = missingParams(givenArgs, params, nameOfNamedArg) - if (missing forall (_.hasDefault)) { - val defaultArgs = missing flatMap (p => { + if (missing.forall(_.hasDefault)) { + val defaultArgs = missing flatMap { p => val defGetter = defaultGetter(p, context) // TODO #3649 can create spurious errors when companion object is gone (because it becomes unlinked from scope) if (defGetter == NoSymbol) None // prevent crash in erroneous trees, #3649 @@ -463,9 +464,9 @@ trait NamesDefaults { self: Analyzer => else NamedArg(Ident(p.name), default2) }) } - }) + } (givenArgs ::: defaultArgs, Nil) - } else (givenArgs, missing filterNot (_.hasDefault)) + } else (givenArgs, missing.filterNot(_.hasDefault)) } else (givenArgs, Nil) } @@ -551,7 +552,7 @@ trait NamesDefaults { self: Analyzer => var positionalAllowed = true def stripNamedArg(arg: NamedArg, argIndex: Int): Tree = { val NamedArg(Ident(name), rhs) = arg: @unchecked - params indexWhere (p => matchesName(p, name, argIndex)) match { + params.indexWhere(p => matchesName(p, name, argIndex)) match { case -1 => val warnVariableInScope = !currentRun.isScala3 && context0.lookupSymbol(name, _.isVariable).isSuccess UnknownParameterNameNamesDefaultError(arg, name, warnVariableInScope) diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala index 176867663f40..ff65fd4a5bd3 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala @@ -281,7 +281,7 @@ trait PatternTypers { // use "tree" for the context, not context.tree: don't make another CaseDef context, // as instantiateTypeVar's bounds would end up there val ctorContext = context.makeNewScope(tree, context.owner) - freeVars foreach ctorContext.scope.enter + freeVars.foreach(ctorContext.scope.enter(_)) newTyper(ctorContext).infer.inferConstructorInstance(tree1, undetparams, ptSafe) // simplify types without losing safety, @@ -326,7 +326,7 @@ trait PatternTypers { } val GenPolyType(freeVars, unappFormal) = freshArgType(unapplyType.skolemizeExistential(context.owner, tree)) val unapplyContext = context.makeNewScope(tree, context.owner) - freeVars.foreach(unapplyContext.scope.enter) + freeVars.foreach(unapplyContext.scope.enter(_)) val pattp = newTyper(unapplyContext).infer.inferTypedPattern(tree, unappFormal, pt, canRemedy = canRemedy, isUnapply = true) // turn any unresolved type variables in freevars into existential skolems val skolems = freeVars.map(fv => unapplyContext.owner.newExistentialSkolem(fv, fv)) diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 2b455e38e17f..8c0dd5a00225 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -354,19 +354,23 @@ abstract class RefChecks extends Transform { overrideErrorWithMemberInfo("`override` modifier required to override concrete member:") } - def overrideAccessError(): Unit = { - val otherAccess = accessFlagsToString(other) - overrideError("weaker access privileges in overriding\n"+ - overriddenWithAddendum(s"override should ${(if (otherAccess == "") "be public" else "at least be " + otherAccess)}")) - } + def weakerAccessError(advice: String): Unit = + overrideError(sm"""|weaker access privileges in overriding + |${overriddenWithAddendum(advice)}""") + def overrideAccessError(): Unit = + weakerAccessError { + accessFlagsToString(other) match { + case "" => "override should be public" + case otherAccess => s"override should at least be $otherAccess" + } + } //Console.println(infoString(member) + " overrides " + infoString(other) + " in " + clazz);//DEBUG /* Is the intersection between given two lists of overridden symbols empty? */ - def intersectionIsEmpty(syms1: List[Symbol], syms2: List[Symbol]) = - !(syms1 exists (syms2 contains _)) + def intersectionIsEmpty(syms1: List[Symbol], syms2: List[Symbol]) = !syms1.exists(syms2.contains) - if (memberClass == ObjectClass && otherClass == AnyClass) {} // skip -- can we have a mode of symbolpairs where this pair doesn't even appear? + if (memberClass == ObjectClass && otherClass == AnyClass) () // skip -- can we have a mode of symbolpairs where this pair doesn't even appear? else if (typesOnly) checkOverrideTypes() else { // o: public | protected | package-protected (aka java's default access) @@ -374,17 +378,23 @@ abstract class RefChecks extends Transform { // m: public | public/protected | public/protected/package-protected-in-same-package-as-o if (member.isPrivate) // (1.1) - overrideError("weaker access privileges in overriding\n"+ - overriddenWithAddendum(s"override should not be private")) + weakerAccessError("override should not be private") // todo: align accessibility implication checking with isAccessible in Contexts - val ob = other.accessBoundary(memberClass) - val mb = member.accessBoundary(memberClass) - def isOverrideAccessOK = member.isPublic || { // member is public, definitely same or relaxed access - (!other.isProtected || member.isProtected) && // if o is protected, so is m - ((!isRootOrNone(ob) && ob.hasTransOwner(mb)) || // m relaxes o's access boundary - (other.isJavaDefined && other.isProtected)) // overriding a protected java member, see #3946 #12349 + @inline def protectedOK = !other.isProtected || member.isProtected + @inline def accessBoundaryOK = { + val ob = other.accessBoundary(memberClass) + val mb = member.accessBoundary(memberClass) + @inline def companionBoundaryOK = ob.isClass && mb.isModuleClass && mb.module == ob.companionSymbol + !isRootOrNone(ob) && (ob.hasTransOwner(mb) || companionBoundaryOK) } + @inline def otherIsJavaProtected = other.isJavaDefined && other.isProtected + def isOverrideAccessOK = + member.isPublic || // member is public, definitely same or relaxed access + protectedOK && // if o is protected, so is m + (accessBoundaryOK || // m relaxes o's access boundary + otherIsJavaProtected // overriding a protected java member, see #3946 #12349 + ) if (!isOverrideAccessOK) { overrideAccessError() } else if (other.isClass) { @@ -433,8 +443,16 @@ abstract class RefChecks extends Transform { // Only warn for the pair that has one leg in `clazz`. if (clazz == memberClass) checkOverrideDeprecated() def javaDetermined(sym: Symbol) = sym.isJavaDefined || isUniversalMember(sym) - if (!member.hasAttachment[NullaryOverrideAdapted.type] - && other.paramss.isEmpty && !member.paramss.isEmpty + if (member.hasAttachment[NullaryOverrideAdapted.type]) { + def exempt() = member.overrides.exists(sym => sym.isJavaDefined || isUniversalMember(sym)) + val msg = "method without a parameter list overrides a method with a single empty one" + if (!exempt()) + if (currentRun.isScala3) + overrideErrorWithMemberInfo(msg) + else + refchecksWarning(member.pos, msg, WarningCategory.OtherNullaryOverride) + } + else if (other.paramss.isEmpty && !member.paramss.isEmpty && !javaDetermined(member) && !member.overrides.exists(javaDetermined) ) { val msg = "method with a single empty parameter list overrides method without any parameter list" @@ -572,7 +590,7 @@ abstract class RefChecks extends Transform { def checkNoAbstractMembers(): Unit = { val NoError = null.asInstanceOf[String] val EmptyDiagnostic = "" - def diagnose(member: Symbol, accessors: List[Symbol]): String = { + def diagnose(member: Symbol, accessors: List[Symbol], nonPrivateMembers: Scope, fastDiagnostics: Boolean): String = { val underlying = analyzer.underlyingSymbol(member) // TODO: don't use this method // Give a specific error message for abstract vars based on why it fails: @@ -585,21 +603,21 @@ abstract class RefChecks extends Transform { else if (member.isGetter && !isMultiple) "an abstract var requires a getter in addition to the setter" else "variables need to be initialized to be defined" } - else if (underlying.isMethod) { + else if (!fastDiagnostics && underlying.isMethod) { // Highlight any member that nearly matches: same name and arity, // but differs in one param or param list. val abstractParamLists = underlying.paramLists - val matchingArity = clazz.tpe.nonPrivateMembersAdmitting(VBRIDGE).filter { m => + val matchingArity = nonPrivateMembers.reverseIterator.filter { m => !m.isDeferred && m.name == underlying.name && sameLength(m.paramLists, abstractParamLists) && sumSize(m.paramLists, 0) == sumSize(abstractParamLists, 0) && sameLength(m.tpe.typeParams, underlying.tpe.typeParams) && !(m.isJavaDefined && m.hasFlag(JAVA_DEFAULTMETHOD)) - } + }.toList matchingArity match { // So far so good: only one candidate method - case Scope(concrete) => + case concrete :: Nil => val concreteParamLists = concrete.paramLists val aplIter = abstractParamLists.iterator.flatten val cplIter = concreteParamLists.iterator.flatten @@ -642,17 +660,18 @@ abstract class RefChecks extends Transform { } else EmptyDiagnostic } - def emitErrors(missing: List[Symbol]): Unit = { + def emitErrors(missing: List[Symbol], nonPrivateMembers: Scope): Unit = { + val fastDiagnostics = missing.lengthCompare(100) > 0 // Group missing members by the name of the underlying symbol, to consolidate getters and setters. val byName = missing.groupBy(_.name.getterName) // There may be 1 or more missing members declared in 1 or more parents. // If a single parent, the message names it. Otherwise, missing members are grouped by declaring class. val byOwner = missing.groupBy(_.owner).toList val announceOwner = byOwner.size > 1 - def membersStrings(members: List[Symbol]) = + def membersStrings(members: List[Symbol]) = { members.sortBy(_.name).flatMap { m => val accessors = byName.getOrElse(m.name.getterName, Nil) - val diagnostic = diagnose(m, accessors) + val diagnostic = diagnose(m, accessors, nonPrivateMembers, fastDiagnostics) if (diagnostic == NoError) Nil else { val s0a = infoString0(m, showLocation = false) @@ -665,16 +684,18 @@ abstract class RefChecks extends Transform { s"$s1 = ???$comment" :: Nil } } + } var count = 0 - val stubs = + def isMulti = count > 1 + def helpfulListing = byOwner.sortBy(_._1.name.toString).flatMap { case (owner, members) => val ms = membersStrings(members) :+ "" count += ms.size - 1 if (announceOwner) s"// Members declared in ${owner.fullName}" :: ms else ms }.init.map(s => s" $s\n").mkString - val isMulti = count > 1 - val singleParent = if (byOwner.size == 1 && byOwner.head._1 != clazz) s" member${if (isMulti) "s" else ""} of ${byOwner.head._1}" else "" + val stubs = helpfulListing + def singleParent = if (byOwner.size == 1 && byOwner.head._1 != clazz) s" member${if (isMulti) "s" else ""} of ${byOwner.head._1}" else "" val line0 = if (isMulti) s"Missing implementations for ${count}${val p = singleParent ; if (p.isEmpty) " members" else p}." else s"Missing implementation${val p = singleParent ; if (p.isEmpty) p else s" for$p"}:" @@ -689,10 +710,11 @@ abstract class RefChecks extends Transform { } (ps, qs) } + val nonPrivateMembers = clazz.info.nonPrivateMembersAdmitting(VBRIDGE) // Avoid extra allocations with reverseIterator. Filter for abstract members of interest, and bad abstract override. val (missing, abstractIncomplete): (List[Symbol], List[Symbol]) = - filtered(clazz.info.nonPrivateMembersAdmitting(VBRIDGE).reverseIterator)(m => m.isDeferred & !ignoreDeferred(m))(m => m.isAbstractOverride && m.isIncompleteIn(clazz)) - if (missing.nonEmpty) emitErrors(missing) + filtered(nonPrivateMembers.reverseIterator)(m => m.isDeferred & !ignoreDeferred(m))(m => m.isAbstractOverride && m.isIncompleteIn(clazz)) + if (missing.nonEmpty) emitErrors(missing, nonPrivateMembers) // Check the remainder for invalid absoverride. for (member <- abstractIncomplete) { val explanation = member.superSymbolIn(clazz) match { @@ -753,10 +775,8 @@ abstract class RefChecks extends Transform { lazy val varargsType = toJavaRepeatedParam(member.tpe) def isSignatureMatch(sym: Symbol) = !sym.isTerm || { - val symtpe = clazz.thisType memberType sym - def matches(tp: Type) = tp matches symtpe - - matches(member.tpe) || (isVarargs && matches(varargsType)) + val symtpe = clazz.thisType.memberType(sym) + member.tpe.matches(symtpe) || (isVarargs && varargsType.matches(symtpe)) } /* The rules for accessing members which have an access boundary are more * restrictive in java than scala. Since java has no concept of package nesting, @@ -781,15 +801,16 @@ abstract class RefChecks extends Transform { || sym.isProtected // marked protected in java, thus accessible to subclasses || sym.privateWithin == member.enclosingPackageClass // exact package match ) - def classDecls = inclazz.info.nonPrivateDecl(member.name) - def matchingSyms = classDecls filter (sym => isSignatureMatch(sym) && javaAccessCheck(sym)) + def classDecl = inclazz.info.nonPrivateDecl(member.name) + .orElse(inclazz.info.nonPrivateDecl(member.unexpandedName)) + def matchingSyms = classDecl.filter(sym => isSignatureMatch(sym) && javaAccessCheck(sym)) (inclazz != clazz) && (matchingSyms != NoSymbol) } // 4. Check that every defined member with an `override` modifier overrides some other member. for (member <- clazz.info.decls) - if (member.isAnyOverride && !(clazz.thisType.baseClasses exists (hasMatchingSym(_, member)))) { + if (member.isAnyOverride && !clazz.thisType.baseClasses.exists(hasMatchingSym(_, member))) { // for (bc <- clazz.info.baseClasses.tail) Console.println("" + bc + " has " + bc.info.decl(member.name) + ":" + bc.info.decl(member.name).tpe);//DEBUG val nonMatching: List[Symbol] = clazz.info.member(member.name).alternatives.filterNot(_.owner == clazz).filterNot(_.isFinal) @@ -1272,7 +1293,7 @@ abstract class RefChecks extends Transform { if (sym.isDeprecated && // synthetic calls to deprecated case class constructor !(sym.isConstructor && sym.owner.isCaseClass && currentOwner.isSynthetic) && - !currentOwner.ownersIterator.exists(s => s.isDeprecated || s.companionClass.isDeprecated)) + !currentOwner.ownersIterator.exists(_.isDeprecated)) runReporting.deprecationWarning(pos, sym, currentOwner) // Similar to deprecation: check if the symbol is marked with @migration @@ -1323,15 +1344,14 @@ abstract class RefChecks extends Transform { && !otherSym.isProtected && !otherSym.isTypeParameterOrSkolem && !otherSym.isExistentiallyBound - && (otherSym isLessAccessibleThan memberSym) - && (otherSym isLessAccessibleThan memberSym.enclClass) + && memberSym.ownersIterator.forall(otherSym.isLessAccessibleThan(_)) ) private def lessAccessibleSymsInType(other: Type, memberSym: Symbol): List[Symbol] = { val extras = other match { case TypeRef(pre, _, args) => // checking the prefix here gives us spurious errors on e.g. a private[process] // object which contains a type alias, which normalizes to a visible type. - args filterNot (_ eq NoPrefix) flatMap (tp => lessAccessibleSymsInType(tp, memberSym)) + args.filterNot(_ eq NoPrefix).flatMap(lessAccessibleSymsInType(_, memberSym)) case _ => Nil } @@ -1367,7 +1387,7 @@ abstract class RefChecks extends Transform { // or if the normalized type is, that's good too else if ((tpe ne tpe.normalize) && lessAccessibleSymsInType(tpe.dealiasWiden, member).isEmpty) () // otherwise warn about the inaccessible syms in the unnormalized type - else inaccessible foreach (sym => warnLessAccessible(sym, member)) + else inaccessible.foreach(warnLessAccessible(_, member)) } // types of the value parameters @@ -1823,7 +1843,7 @@ abstract class RefChecks extends Transform { if (settings.warnNullaryUnit) checkNullaryMethodReturnType(sym) if (settings.warnInaccessible) { - if (!sym.isConstructor && !sym.isEffectivelyFinalOrNotOverridden && !sym.isSynthetic) + if (!sym.isConstructor && !sym.isEffectivelyFinalOrNotOverridden && !sym.owner.isSealed && !sym.isSynthetic) checkAccessibilityOfReferencedTypes(tree) } tree match { diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index ef168e5926c9..0652bcd3d76a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -117,15 +117,14 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT atPos(sel.pos)(Select(gen.mkAttributedThis(clazz), superAcc) setType sel.tpe) } - private def transformArgs(params: List[Symbol], args: List[Tree]) = { + private def transformArgs(params: List[Symbol], args: List[Tree]) = treeInfo.mapMethodParamsAndArgs(params, args) { (param, arg) => if (isByNameParamType(param.tpe)) withInvalidOwner(transform(arg)) else transform(arg) } - } - /** Check that a class and its companion object to not both define + /** Check that a class and its companion object do not both define * a class or module with same name */ private def checkCompanionNameClashes(sym: Symbol) = @@ -136,9 +135,9 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT if (other == NoSymbol) other = linked.info.decl(sym.name.toTermName).filter(_.isModule) if (other != NoSymbol) - reporter.error(sym.pos, "name clash: "+sym.owner+" defines "+sym+ - "\nand its companion "+sym.owner.companionModule+" also defines "+ - other) + reporter.error(sym.pos, + sm"""|name clash: ${sym.owner} defines $sym + |and its companion ${sym.owner.companionModule} also defines $other""") } } @@ -240,13 +239,13 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT val decls = sym.info.decls for (s <- decls) { val privateWithin = s.privateWithin - if (privateWithin.isClass && !s.hasFlag(EXPANDEDNAME | PROTECTED) && !privateWithin.isModuleClass && - !s.isConstructor) { + def isPrivateWithinNonCompanionModule = privateWithin.isModuleClass + if (privateWithin.isClass && !s.hasFlag(EXPANDEDNAME | PROTECTED) && !isPrivateWithinNonCompanionModule && !s.isConstructor) { val savedName = s.name decls.unlink(s) s.expandName(privateWithin) decls.enter(s) - log("Expanded '%s' to '%s' in %s".format(savedName, s.name, sym)) + log(s"Expanded '$savedName' to '${s.name}' in $sym") } } super.transform(tree) @@ -303,23 +302,15 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT // also exists in a superclass, because they may be surprised // to find out that a constructor parameter will shadow a // field. See scala/bug#4762. - if (settings.warnPrivateShadow) { - if (sym.isPrivateLocal && sym.paramss.isEmpty) { - qual.symbol.ancestors foreach { parent => - parent.info.decls filterNot (x => x.isPrivate || x.isLocalToThis) foreach { m2 => - if (sym.name == m2.name && m2.isGetter && m2.accessed.isMutable) { - runReporting.warning(sel.pos, - sym.accessString + " " + sym.fullLocationString + " shadows mutable " + m2.name - + " inherited from " + m2.owner + ". Changes to " + m2.name + " will not be visible within " - + sym.owner + " - you may want to give them distinct names.", - WarningCategory.LintPrivateShadow, - currentOwner) - } - } - } - } - } - + if (settings.warnPrivateShadow && sym.isPrivateLocal && sym.paramss.isEmpty) + for (parent <- qual.symbol.ancestors) + for (m2 <- parent.info.decls) + if (!m2.isPrivate && !m2.isLocalToThis && sym.name == m2.name && m2.isGetter && m2.accessed.isMutable) + runReporting.warning(sel.pos, + sq"""${sym.accessString} ${sym.fullLocationString} shadows mutable ${m2.name} inherited from ${m2.owner}. + > Changes to ${m2.name} will not be visible within ${sym.owner}; you may want to give them distinct names.""", + WarningCategory.LintPrivateShadow, + currentOwner) def isAccessibleFromSuper(sym: Symbol) = { val pre = SuperType(sym.owner.tpe, qual.tpe) diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index cc8655546ce7..960741741f73 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -562,6 +562,8 @@ trait TypeDiagnostics extends splain.SplainDiagnostics { case Assign(lhs, _) if isExisting(lhs.symbol) => setVars += lhs.symbol case Function(ps, _) if settings.warnUnusedParams && !t.isErrorTyped => params ++= ps.filterNot(p => atBounded(p) || p.symbol.isSynthetic).map(_.symbol) + case Literal(_) => + t.attachments.get[OriginalTreeAttachment].foreach(ota => traverse(ota.original)) case _ => } @@ -704,15 +706,18 @@ trait TypeDiagnostics extends splain.SplainDiagnostics { ) val why = if (sym.isPrivate) "private" else "local" var cond = "is never used" + def long = if (settings.uniqid.value) s" (${sym.nameString})" else "" + def getterNameString(sym: Symbol): String = sym.getterName.decoded + long val what = if (sym.isDefaultGetter) "default argument" else if (sym.isConstructor) "constructor" - else if (sym.isSetter) { cond = valAdvice ; s"var ${sym.getterName.decoded}" } - else if (sym.isVar || sym.isGetter && sym.accessed.isVar) s"var ${sym.getterName.decoded}" - else if (sym.isVal || sym.isGetter && sym.accessed.isVal || sym.isLazy) s"val ${sym.name.decoded}" - else if (sym.isMethod) s"method ${sym.name.decoded}" - else if (sym.isModule) s"object ${sym.name.decoded}" + else if (sym.isSetter) { cond = valAdvice ; s"var ${getterNameString(sym)}" } + else if (sym.isVar || sym.isGetter && sym.accessed.isVar) s"var ${sym.nameString}" + else if (sym.isVal || sym.isGetter && sym.accessed.isVal || sym.isLazy) s"val ${sym.nameString}" + else if (sym.isMethod) s"method ${sym.nameString}" + else if (sym.isModule) s"object ${sym.nameString}" else "term" + // consider using sym.owner.fullLocationString emitUnusedWarning(pos, s"$why $what in ${sym.owner} $cond", wcat(sym), sym) } def typeWarning(defn: SymTree): Unit = { @@ -724,7 +729,7 @@ trait TypeDiagnostics extends splain.SplainDiagnostics { for (defn <- unusedPrivates.unusedTypes if shouldWarnOn(defn.symbol)) { typeWarning(defn) } for (v <- unusedPrivates.unsetVars) { - emitUnusedWarning(v.pos, s"local var ${v.name} in ${v.owner} ${valAdvice}", WarningCategory.UnusedPrivates, v) + emitUnusedWarning(v.pos, s"local var ${v.nameString} in ${v.owner} ${valAdvice}", WarningCategory.UnusedPrivates, v) } } if (settings.warnUnusedPatVars) { @@ -754,8 +759,8 @@ trait TypeDiagnostics extends splain.SplainDiagnostics { ) for (s <- unusedPrivates.unusedParams if warnable(s)) { val what = - if (s.name.startsWith(nme.EVIDENCE_PARAM_PREFIX)) s"evidence parameter ${s.name} of type ${s.tpe}" - else s"parameter ${s/*.name*/}" + if (s.name.startsWith(nme.EVIDENCE_PARAM_PREFIX)) s"evidence parameter ${s.nameString} of type ${s.tpe}" + else s"parameter ${s.nameString}" val where = if (s.owner.isAnonymousFunction) "anonymous function" else s.owner emitUnusedWarning(s.pos, s"$what in $where is never used", WarningCategory.UnusedParams, s) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index ccc0126ee1bd..f211616c271b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -15,12 +15,11 @@ package tools.nsc package typechecker import scala.annotation.{tailrec, unused} -import scala.collection.mutable +import scala.collection.mutable, mutable.ListBuffer import scala.reflect.internal.{Chars, TypesStats} -import scala.reflect.internal.util.{FreshNameCreator, ListOfNil, Statistics} +import scala.reflect.internal.util.{FreshNameCreator, ListOfNil, Statistics, StringContextStripMarginOps} import scala.tools.nsc.Reporting.{MessageFilter, Suppression, WConf, WarningCategory} import scala.util.chaining._ -import mutable.ListBuffer import symtab.Flags._ import Mode._ @@ -465,7 +464,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case _ => QualifyingClassError(tree, qual) // Delay `setError` in namer, scala/bug#10748 - if (immediate) setError(tree) else unit.toCheck += (() => setError(tree)) + if (immediate) setError(tree) else unit.addPostUnitCheck(() => setError(tree)) NoSymbol } @@ -734,24 +733,24 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper */ def checkFeature(pos: Position, featureTrait: Symbol, construct: => String = "", immediate: Boolean = false): Boolean = isPastTyper || { - val nestedOwners = - featureTrait.owner.ownerChain.takeWhile(_ != languageFeatureModule.moduleClass).reverse - val featureName = nestedOwners.map(s => s"${s.name}.").mkString + featureTrait.name - def action(): Boolean = { + val featureName = { + val nestedOwners = featureTrait.owner.ownerChain.takeWhile(_ != languageFeatureModule.moduleClass).reverse + nestedOwners.map(s => s"${s.name}.").mkString + featureTrait.name + } + settings.language.contains(featureName) || { + def action(): Boolean = { + if (!immediate) + debuglog(s"deferred check of feature $featureTrait") def hasImport = inferImplicitByType(featureTrait.tpe, context).isSuccess - def hasOption = settings.language.contains(featureName) - hasOption || hasImport || { - val Some(AnnotationInfo(_, List(Literal(Constant(featureDesc: String)), Literal(Constant(required: Boolean))), _)) = - featureTrait.getAnnotation(LanguageFeatureAnnot): @unchecked - context.featureWarning(pos, featureName, featureDesc, featureTrait, construct, required) - false + hasImport || { + val Some(AnnotationInfo(_, List(Literal(Constant(featureDesc: String)), Literal(Constant(required: Boolean))), _)) = + featureTrait.getAnnotation(LanguageFeatureAnnot): @unchecked + context.featureWarning(pos, featureName, featureDesc, featureTrait, construct, required) + false + } } - } - if (immediate) { - action() - } else { - unit.toCheck += (() => action(): Unit) - true + if (immediate) action() + else { unit.addPostUnitCheck(() => action()); true } } } @@ -837,7 +836,6 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // refuses to re-attempt typechecking, and presumes that someone // else was responsible for issuing the related type error! fun.setSymbol(NoSymbol) - case _ => } debuglog(s"fallback on implicits: $tree/$resetTree") // scala/bug#10066 Need to patch the enclosing tree in the context to make translation of Dynamic @@ -884,26 +882,35 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def warnTree = original orElse tree - def warnEtaZero(): Boolean = { - if (!settings.warnEtaZero) return true - context.warning(tree.pos, - s"""An unapplied 0-arity method was eta-expanded (due to the expected type $pt), rather than applied to `()`. - |Write ${Apply(warnTree, Nil)} to invoke method ${meth.decodedName}, or change the expected type.""".stripMargin, - WarningCategory.LintEtaZero) + def warnEtaZero(): true = { + if (settings.warnEtaZero) { + context.warning(tree.pos, + s"""An unapplied 0-arity method was eta-expanded (due to the expected type $pt), rather than applied to `()`. + |Write ${Apply(warnTree, Nil)} to invoke method ${meth.decodedName}, or change the expected type.""".stripMargin, + WarningCategory.LintEtaZero) + } true } - def warnEtaSam(): Boolean = { - if (!settings.warnEtaSam) return true - val sam = samOf(pt) - val samClazz = sam.owner - // TODO: we allow a Java class as a SAM type, whereas Java only allows the @FunctionalInterface on interfaces -- align? - if (sam.exists && (!samClazz.hasFlag(JAVA) || samClazz.hasFlag(INTERFACE)) && !samClazz.hasAnnotation(definitions.FunctionalInterfaceClass)) - context.warning(tree.pos, - s"""Eta-expansion performed to meet expected type $pt, which is SAM-equivalent to ${samToFunctionType(pt)}, - |even though $samClazz is not annotated with `@FunctionalInterface`; - |to suppress warning, add the annotation or write out the equivalent function literal.""".stripMargin, - WarningCategory.LintEtaSam) + def warnEtaSam(): true = { + if (settings.warnEtaSam || currentRun.isScala3) { + val sam = samOf(pt) + if (sam.exists) { + val samClazz = sam.owner + val isJavaClass = samClazz.isJava && !samClazz.isInterface + if (!samClazz.hasAnnotation(definitions.FunctionalInterfaceClass)) { + val ft = samToFunctionType(pt) + val sample = Function(meth.paramss.head.map(ValDef(_)), Apply(meth, meth.paramss.head.map(p => Ident(p.name)): _*)) + val places = Apply(meth, meth.paramss.head.map(_ => Ident(nme.USCOREkw)): _*) + val advice = if (isJavaClass) "" else s"\n$samClazz should be annotated with `@FunctionalInterface` if eta-expansion is desired." + context.warning(tree.pos, + sm"""Eta-expansion to expected type $pt, which is not a function type but is SAM-convertible to $ft.$advice + |Avoid eta-expansion by writing the function literal `$sample` or `$places`. + |This warning can be filtered with `-Wconf:cat=lint-eta-sam`.""", + WarningCategory.LintEtaSam) + } + } + } true } @@ -1115,17 +1122,31 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper @inline def warnValueDiscard(): Unit = if (!isPastTyper && settings.warnValueDiscard.value && !treeInfo.isThisTypeResult(tree) && !treeInfo.hasExplicitUnit(tree)) context.warning(tree.pos, s"discarded non-Unit value of type ${tree.tpe}", WarningCategory.WFlagValueDiscard) - @inline def warnNumericWiden(tpSym: Symbol, ptSym: Symbol): Unit = - if (!isPastTyper) { - val isInharmonic = ( - (tpSym == IntClass && ptSym == FloatClass) - || (tpSym == LongClass && (ptSym == FloatClass || ptSym == DoubleClass)) - ) - if (isInharmonic) - // not `context.deprecationWarning` because they are not buffered in silent mode - context.warning(tree.pos, s"Widening conversion from ${tpSym.name} to ${ptSym.name} is deprecated because it loses precision. Write `.to${ptSym.name}` instead.", WarningCategory.Deprecation) - else if (settings.warnNumericWiden.value) context.warning(tree.pos, "implicit numeric widening", WarningCategory.WFlagNumericWiden) + @inline def warnNumericWiden(tpSym: Symbol, ptSym: Symbol): Unit = if (!isPastTyper) { + val targetIsWide = ptSym == FloatClass || ptSym == DoubleClass + val isInharmonic = { + def intWidened = tpSym == IntClass && ptSym == FloatClass + def longWidened = tpSym == LongClass && targetIsWide + intWidened || longWidened } + if (isInharmonic) + // not `context.deprecationWarning` because they are not buffered in silent mode + context.warning(tree.pos, s"Widening conversion from ${tpSym.name} to ${ptSym.name} is deprecated because it loses precision. Write `.to${ptSym.name}` instead.", WarningCategory.Deprecation) + else { + object warnIntDiv extends Traverser { + def isInt(t: Tree) = ScalaIntegralValueClasses(t.tpe.typeSymbol) + override def traverse(tree: Tree): Unit = tree match { + case Apply(Select(q, nme.DIV), _) if isInt(q) => + context.warning(tree.pos, s"integral division is implicitly converted (widened) to floating point. Add an explicit `.to${ptSym.name}`.", WarningCategory.LintIntDivToFloat) + case Apply(Select(a1, _), List(a2)) if isInt(tree) && isInt(a1) && isInt(a2) => traverse(a1); traverse(a2) + case Select(q, _) if isInt(tree) && isInt(q) => traverse(q) + case _ => + } + } + if (targetIsWide && settings.lintIntDivToFloat) warnIntDiv(tree) + if (settings.warnNumericWiden.value) context.warning(tree.pos, "implicit numeric widening", WarningCategory.WFlagNumericWiden) + } + } // The <: Any requirement inhibits attempts to adapt continuation types to non-continuation types. val anyTyped = tree.tpe <:< AnyTpe @@ -1243,7 +1264,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper adaptAfterOverloadResolution(tree, mode, pt, original) case NullaryMethodType(restpe) => // (2) if (hasUndets && settings.lintUniversalMethods && (isCastSymbol(tree.symbol) || isTypeTestSymbol(tree.symbol)) && context.undetparams.exists(_.owner == tree.symbol)) - context.warning(tree.pos, s"missing type argument to ${tree.symbol}", WarningCategory.LintAdaptedArgs) + context.warning(tree.pos, s"missing type argument to ${tree.symbol}", WarningCategory.LintUniversalMethods) val resTpDeconst = // keep constant types when they are safe to fold. erasure eliminates constant types modulo some exceptions, so keep those. if (isBeforeErasure && tree.symbol.isAccessor && tree.symbol.hasFlag(STABLE) && treeInfo.isExprSafeToInline(tree)) restpe else restpe.deconst @@ -1457,7 +1478,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (stat.symbol.isAuxiliaryConstructor) notAllowed("secondary constructor") else if (isValueClass && (name == nme.equals_ || name == nme.hashCode_) && !stat.symbol.isSynthetic) - notAllowed(s"redefinition of $name method. See SIP-15, criterion 4.") + notAllowed(s"redefinition of $name method. See SIP-15, criterion 5.") else if (stat.symbol != null && stat.symbol.isParamAccessor) notAllowed("additional parameter") checkEphemeralDeep.traverse(rhs) @@ -1789,7 +1810,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (parent == DynamicClass) checkFeature(parentPos, currentRun.runDefinitions.DynamicsFeature) def validateParentClass(parent: Tree, superclazz: Symbol) = - if (!parent.isErrorTyped) { + if (!parent.isErrorTyped) { // redundant val psym = parent.tpe.typeSymbol.initialize if (!context.unit.isJava) @@ -1854,7 +1875,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (!parents.isEmpty && parents.forall(!_.isErrorTyped)) { val superclazz = parents.head.tpe.typeSymbol - for (p <- parents) validateParentClass(p, superclazz) + parents.foreach(validateParentClass(_, superclazz)) } pending.foreach(ErrorUtils.issueTypeError) @@ -2032,7 +2053,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val body1 = pluginsEnterStats(this, namer.expandMacroAnnotations(templ.body)) enterSyms(context.outer.make(templ, clazz, clazz.info.decls), body1) if (!templ.isErrorTyped) // if `parentTypes` has invalidated the template, don't validate it anymore - validateParentClasses(parents1, selfType, clazz.isTrait) + validateParentClasses(parents1, selfType, clazz.isTrait) if (clazz.isCase) validateNoCaseAncestor(clazz) if (clazz.isTrait && hasSuperArgs(parents1.head)) @@ -2066,11 +2087,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper ctors.foreach(AuxConstrInConstantAnnotation(_, clazz)) } - if (clazz.isTrait) { - for (decl <- clazz.info.decls if decl.isTerm && decl.isEarlyInitialized) { - context.warning(decl.pos, "Implementation restriction: early definitions in traits are not initialized before the super class is initialized.", WarningCategory.Other) - } + for (decl <- clazz.info.decls) + if (decl.isTerm && decl.isEarlyInitialized) + context.warning(decl.pos, "Implementation restriction: early definitions in traits are not initialized before the super class is initialized.", WarningCategory.Other) } treeCopy.Template(templ, parents1, self1, body3) setType clazz.tpe_* @@ -2164,7 +2184,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } /** Analyze the super constructor call to record information used later to compute parameter aliases */ - def analyzeSuperConsructor(meth: Symbol, vparamss: List[List[ValDef]], rhs: Tree): Unit = { + def analyzeSuperConstructor(meth: Symbol, vparamss: List[List[ValDef]], rhs: Tree): Unit = { val clazz = meth.owner debuglog(s"computing param aliases for $clazz:${clazz.primaryConstructor.tpe}:$rhs") val pending = ListBuffer[AbsTypeError]() @@ -2346,7 +2366,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } } - val tparams1 = ddef.tparams mapConserve typedTypeDef + val tparams1 = ddef.tparams.mapConserve(typedTypeDef) val vparamss1 = ddef.vparamss.mapConserve(_.mapConserve(typedValDef)) warnTypeParameterShadow(tparams1, meth) @@ -2386,9 +2406,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (meth.isClassConstructor && !isPastTyper && !meth.owner.isSubClass(AnyValClass) && !meth.isJava) { // There are no supercalls for AnyVal or constructors from Java sources, which - // would blow up in analyzeSuperConsructor; there's nothing to be computed for them anyway. + // would blow up in analyzeSuperConstructor; there's nothing to be computed for them anyway. if (meth.isPrimaryConstructor) - analyzeSuperConsructor(meth, vparamss1, rhs1) + analyzeSuperConstructor(meth, vparamss1, rhs1) else checkSelfConstructorArgs(ddef, meth.owner) } @@ -2415,13 +2435,18 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (meth.isStructuralRefinementMember) checkMethodStructuralCompatible(ddef) - if (meth.isImplicit && !meth.isSynthetic) meth.paramss match { - case List(param) :: _ if !param.isImplicit => - checkFeature(ddef.pos, currentRun.runDefinitions.ImplicitConversionsFeature, meth.toString) - case _ => + if (meth.isImplicit) { + if (!meth.isSynthetic) meth.paramss match { + case List(param) :: _ if !param.isImplicit => + checkFeature(ddef.pos, currentRun.runDefinitions.ImplicitConversionsFeature, meth.toString) + case _ => + } + if (meth.isGetter && !meth.isLocalToBlock && meth.accessed.hasAttachment[FieldTypeInferred.type]) { + meth.accessed.removeAttachment[FieldTypeInferred.type] + InferredImplicitError(ddef, meth.accessed.tpe.resultType, context) + } } } - treeCopy.DefDef(ddef, typedMods, ddef.name, tparams1, vparamss1, tpt1, rhs1) setType NoType } finally { currentRun.profiler.afterTypedImplDef(meth) @@ -2627,6 +2652,31 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val selectorTp = packCaptured(selector1.tpe.widen).skolemizeExistential(context.owner, selector) val casesTyped = typedCases(cases, selectorTp, pt) + def initChildren(sym: Symbol): Unit = + if (sym.isJava && sym.isSealed) + sym.attachments.get[PermittedSubclassSymbols] match { + case Some(PermittedSubclassSymbols(permits)) => + for (child <- permits if child.isJava) + initChildren(child.initialize) + case _ => + val seen = mutable.HashSet.empty[Symbol] + def populate(): Unit = + patmat.javaClassesByUnit.get(sym.pos.source) match { + case Some(classes) => + classes.find(!seen(_)) match { + case Some(unseen) => + seen += unseen + unseen.initialize.companionSymbol.moduleClass.initialize + if (unseen.hasAttachment[PermittedSubclassSymbols]) initChildren(unseen) + populate() + case _ => + } + case _ => + } + populate() + } + initChildren(selectorTp.typeSymbol) + def finish(cases: List[CaseDef], matchType: Type) = treeCopy.Match(tree, selector1, cases) setType matchType @@ -2908,69 +2958,71 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper * function type is a built-in FunctionN or some SAM type * */ - def inferSamType(fun: Tree, pt: Type, mode: Mode): Boolean = fun match { - case fun@Function(vparams, _) if !isFunctionType(pt) => - // TODO: can we ensure there's always a SAMFunction attachment, instead of looking up the sam again??? - // seems like overloading complicates things? - val sam = samOfProto(pt) - - if (!samMatchesFunctionBasedOnArity(sam, vparams)) false - else { - def fullyDefinedMeetsExpectedFunTp(pt: Type): Boolean = isFullyDefined(pt) && { - val samMethType = pt memberInfo sam - fun.tpe <:< functionType(samMethType.paramTypes, samMethType.resultType) - } + def inferSamType(fun: Tree, pt: Type, mode: Mode): Boolean = + if (pt.isInstanceOf[OverloadedArgProto]) inferSamType(fun, pt.underlying, mode) // scala/bug#12560 + else fun match { + case fun@Function(vparams, _) if !isFunctionType(pt) => + // TODO: can we ensure there's always a SAMFunction attachment, instead of looking up the sam again??? + // seems like overloading complicates things? + val sam = samOfProto(pt) + + if (!samMatchesFunctionBasedOnArity(sam, vparams)) false + else { + def fullyDefinedMeetsExpectedFunTp(pt: Type): Boolean = isFullyDefined(pt) && { + val samMethType = pt memberInfo sam + fun.tpe <:< functionType(samMethType.paramTypes, samMethType.resultType) + } - val samTp = - if (!sam.exists) NoType - else if (fullyDefinedMeetsExpectedFunTp(pt)) pt - else try { - val ptFullyDefined = instantiateSamFromFunction(fun.tpe, pt, sam) - if (ptFullyDefined <:< pt && fullyDefinedMeetsExpectedFunTp(ptFullyDefined)) { - debuglog(s"sam fully defined expected type: $ptFullyDefined from $pt for ${fun.tpe}") - ptFullyDefined - } else { - debuglog(s"Could not define type $pt using ${fun.tpe} <:< ${pt memberInfo sam} (for $sam)") - NoType + val samTp = + if (!sam.exists) NoType + else if (fullyDefinedMeetsExpectedFunTp(pt)) pt + else try { + val ptFullyDefined = instantiateSamFromFunction(fun.tpe, pt, sam) + if (ptFullyDefined <:< pt && fullyDefinedMeetsExpectedFunTp(ptFullyDefined)) { + debuglog(s"sam fully defined expected type: $ptFullyDefined from $pt for ${fun.tpe}") + ptFullyDefined + } else { + debuglog(s"Could not define type $pt using ${fun.tpe} <:< ${pt memberInfo sam} (for $sam)") + NoType + } + } catch { + case e@(_: NoInstance | _: TypeError) => + debuglog(s"Error during SAM synthesis: could not define type $pt using ${fun.tpe} <:< ${pt memberInfo sam} (for $sam)\n$e") + NoType } - } catch { - case e@(_: NoInstance | _: TypeError) => - debuglog(s"Error during SAM synthesis: could not define type $pt using ${fun.tpe} <:< ${pt memberInfo sam} (for $sam)\n$e") - NoType - } - if (samTp eq NoType) false - else { - /* Make a synthetic class symbol to represent the synthetic class that + if (samTp eq NoType) false + else { + /* Make a synthetic class symbol to represent the synthetic class that * will be spun up by LMF for this function. This is necessary because * it's possible that the SAM method might need bridges, and they have * to go somewhere. Erasure knows to compute bridges for these classes * just as if they were real templates extending the SAM type. */ - val synthCls = fun.symbol.owner.newClassWithInfo( - name = tpnme.ANON_CLASS_NAME, - parents = ObjectTpe :: samTp :: Nil, - scope = newScope, - pos = sam.pos, - newFlags = SYNTHETIC | ARTIFACT - ) + val synthCls = fun.symbol.owner.newClassWithInfo( + name = tpnme.ANON_CLASS_NAME, + parents = ObjectTpe :: samTp :: Nil, + scope = newScope, + pos = sam.pos, + newFlags = SYNTHETIC | ARTIFACT + ) - synthCls.info.decls.enter { - val newFlags = (sam.flags & ~DEFERRED) | SYNTHETIC - sam.cloneSymbol(synthCls, newFlags).setInfo(samTp memberInfo sam) - } + synthCls.info.decls.enter { + val newFlags = (sam.flags & ~DEFERRED) | SYNTHETIC + sam.cloneSymbol(synthCls, newFlags).setInfo(samTp memberInfo sam) + } - fun.setType(samTp) + fun.setType(samTp) - /* Arguably I should do `fun.setSymbol(samCls)` rather than leaning + /* Arguably I should do `fun.setSymbol(samCls)` rather than leaning * on an attachment, but doing that confounds lambdalift's free var * analysis in a way which does not seem to be trivially reparable. */ - fun.updateAttachment(SAMFunction(samTp, sam, synthCls)) + fun.updateAttachment(SAMFunction(samTp, sam, synthCls)) - true + true + } } - } - case _ => false - } + case _ => false + } /** * Deconstruct an expected function-ish type `pt` into `numVparams` argument prototypes and a result prototype. @@ -3202,29 +3254,29 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def typedEtaExpansion(tree: Tree, mode: Mode, pt: Type): Tree = { debuglog(s"eta-expanding $tree: ${tree.tpe} to $pt") - if (tree.tpe.isDependentMethodType) DependentMethodTpeConversionToFunctionError(tree, tree.tpe) // TODO: support this - else { - val expansion = etaExpand(tree, context.owner) - if (context.undetparams.isEmpty) typed(expansion, mode, pt) - else instantiate(typed(expansion, mode), mode, pt) - } + val expansion = etaExpand(tree, context.owner) + if (context.undetparams.isEmpty) typed(expansion, mode, pt) + else instantiate(typed(expansion, mode), mode, pt) } def typedRefinement(templ: Template): Unit = { val stats = templ.body - namer.enterSyms(stats) - - // need to delay rest of typedRefinement to avoid cyclic reference errors - unit.toCheck += { () => - val stats1 = typedStats(stats, NoSymbol) - // this code kicks in only after typer, so `stats` will never be filled in time - // as a result, most of compound type trees with non-empty stats will fail to reify - // todo. investigate whether something can be done about this - val att = templ.attachments.get[CompoundTypeTreeOriginalAttachment].getOrElse(CompoundTypeTreeOriginalAttachment(Nil, Nil)) - templ.removeAttachment[CompoundTypeTreeOriginalAttachment] - templ updateAttachment att.copy(stats = stats1) - for (stat <- stats1 if stat.isDef && stat.symbol.isOverridingSymbol) - stat.symbol setFlag OVERRIDE + if (!stats.isEmpty) { + namer.enterSyms(stats) + + // need to delay rest of typedRefinement to avoid cyclic reference errors + debuglog(s"deferred typed refinement") + unit.addPostUnitCheck { () => + val stats1 = typedStats(stats, NoSymbol) + // this code kicks in only after typer, so `stats` will never be filled in time + // as a result, most of compound type trees with non-empty stats will fail to reify + // todo. investigate whether something can be done about this + val att = templ.attachments.get[CompoundTypeTreeOriginalAttachment].getOrElse(CompoundTypeTreeOriginalAttachment(Nil, Nil)) + templ.removeAttachment[CompoundTypeTreeOriginalAttachment] + templ updateAttachment att.copy(stats = stats1) + for (stat <- stats1 if stat.isDef && stat.symbol.isOverridingSymbol) + stat.symbol setFlag OVERRIDE + } } } @@ -3722,7 +3774,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper doTypedApply(tree, if (blockIsEmpty) fun else fun1, allArgs, mode, pt).tap(checkRecursive) } else { rollbackNamesDefaultsOwnerChanges() - tryTupleApply orElse duplErrorTree(NotEnoughArgsError(tree, fun, missing)) + tryTupleApply orElse { + removeNames(Typer.this)(allArgs, params) // report bad names + duplErrorTree(NotEnoughArgsError(tree, fun, missing)) + } } } } @@ -4692,15 +4747,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case _ => EmptyTree } - if (treeInfo.mayBeVarGetter(varsym)) { - val res = setterRewrite - if (!res.isEmpty) return res - } + val rewritten = + if (treeInfo.mayBeVarGetter(varsym)) setterRewrite + else EmptyTree -// if (varsym.isVariable || -// // setter-rewrite has been done above, so rule out methods here, but, wait a minute, why are we assigning to non-variables after erasure?! -// (phase.erasedTypes && varsym.isValue && !varsym.isMethod)) { - if (varsym.isVariable || varsym.isValue && phase.assignsFields) { + if (!rewritten.isEmpty) rewritten + else if (varsym.isVariable || varsym.isValue && phase.assignsFields) { val rhs1 = typedByValueExpr(rhs, lhs1.tpe) treeCopy.Assign(tree, lhs1, checkDead(context, rhs1)) setType UnitTpe } @@ -5104,8 +5156,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } def typedApply(tree: Apply) = tree match { - case Apply(Block(stats, expr), args) => - typed1(atPos(tree.pos)(Block(stats, Apply(expr, args) setPos tree.pos.makeTransparent)), mode, pt) + case Apply(blk @ Block(stats, expr), args) => + val ap1 = treeCopy.Apply(tree, expr, args).clearType().setPos(tree.pos.makeTransparent) + val blk1 = treeCopy.Block(blk, stats, ap1).clearType() + typed1(blk1, mode, pt) case Apply(fun, args) => normalTypedApply(tree, fun, args) match { case treeInfo.ArrayInstantiation(level, componentType, arg) => @@ -5261,6 +5315,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def asDynamicCall = mkInvoke(context, tree, qual, name) map { t => wrapErrors(t, _.typed1(t, mode, pt)) } + def checkDubiousUnitSelection(result: Tree): Unit = + if (!isPastTyper && isUniversalMember(result.symbol)) + context.warning(tree.pos, s"dubious usage of ${result.symbol} with unit value", WarningCategory.LintUniversalMethods) val sym = tree.symbol orElse member(qual.tpe, name) orElse inCompanionForJavaStatic(qual.tpe.typeSymbol, name) if ((sym eq NoSymbol) && name != nme.CONSTRUCTOR && mode.inAny(EXPRmode | PATTERNmode)) { @@ -5268,9 +5325,16 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // member. Added `| PATTERNmode` to allow enrichment in patterns (so we can add e.g., an // xml member to StringContext, which in turn has an unapply[Seq] method) + def checkDubiousAdaptation(sel: Tree): Unit = if (!isPastTyper && settings.lintNumericMethods) { + val dubious = ScalaIntegralValueClasses(qualTp.typeSymbol) && ( + sel.symbol.owner.eq(BoxedFloatClass) || sel.symbol.owner.eq(RichFloatClass)) + if (dubious) + context.warning(tree.pos, s"dubious usage of ${sel.symbol} with integer value", WarningCategory.LintNumericMethods) + } val qual1 = adaptToMemberWithArgs(tree, qual, name, mode) if ((qual1 ne qual) && !qual1.isErrorTyped) return typed(treeCopy.Select(tree, qual1, name), mode, pt) + .tap(checkDubiousAdaptation) } // This special-case complements the logic in `adaptMember` in erasure, it handles selections @@ -5388,6 +5452,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper name ) case _ => + if (settings.lintUniversalMethods && qualTp.widen.eq(UnitTpe)) checkDubiousUnitSelection(result) result } } @@ -5486,6 +5551,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case sym => typed1(tree setSymbol sym, mode, pt) } case LookupSucceeded(qual, sym) => + sym.getAndRemoveAttachment[LookupAmbiguityWarning].foreach(w => + runReporting.warning(tree.pos, w.msg, WarningCategory.Other, context.owner)) (// this -> Foo.this if (sym.isThisSym) typed1(This(sym.owner) setPos tree.pos, mode, pt) @@ -5963,7 +6030,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // Trees not allowed during pattern mode. def typedOutsidePatternMode(tree: Tree): Tree = tree match { - case tree: Block => typerWithLocalContext(context.makeNewScope(tree, context.owner))(_.typedBlock(tree, mode, pt)) + case tree: Block => + val blockContext = context.makeNewScope(tree, context.owner) + try typerWithLocalContext(blockContext)(_.typedBlock(tree, mode, pt)) + finally context.undetparams ++= blockContext.undetparams case tree: If => typedIf(tree) case tree: TypeApply => typedTypeApply(tree) case tree: Function => typedFunction(tree) diff --git a/src/compiler/scala/tools/nsc/typechecker/splain/SplainData.scala b/src/compiler/scala/tools/nsc/typechecker/splain/SplainData.scala index 39a405796c95..d00442686f99 100644 --- a/src/compiler/scala/tools/nsc/typechecker/splain/SplainData.scala +++ b/src/compiler/scala/tools/nsc/typechecker/splain/SplainData.scala @@ -14,6 +14,7 @@ package scala.tools.nsc package typechecker package splain +import scala.annotation.tailrec import scala.util.matching.Regex trait SplainData { @@ -68,10 +69,20 @@ trait SplainData { } object ImplicitError { - def unapplyCandidate(e: ImplicitError): Tree = - e.candidate match { - case TypeApply(fun, _) => fun - case a => a + def unapplyCandidate(e: ImplicitError): Tree = unapplyRecursively(e.candidate) + + @tailrec + private def unapplyRecursively(tree: Tree): Tree = + tree match { + case TypeApply(fun, _) => unapplyRecursively(fun) + case Apply(fun, _) => unapplyRecursively(fun) + case a => a + } + + def cleanCandidate(e: ImplicitError): String = + unapplyCandidate(e).toString match { + case ImplicitError.candidateRegex(suf) => suf + case a => a } def candidateName(e: ImplicitError): String = @@ -83,12 +94,6 @@ trait SplainData { val candidateRegex: Regex = """.*\.this\.(.*)""".r - def cleanCandidate(e: ImplicitError): String = - unapplyCandidate(e).toString match { - case candidateRegex(suf) => suf - case a => a - } - def shortName(ident: String): String = ident.substring(ident.lastIndexOf(".") + 1) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/splain/SplainFormatting.scala b/src/compiler/scala/tools/nsc/typechecker/splain/SplainFormatting.scala index b283485b6086..88e5d868751c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/splain/SplainFormatting.scala +++ b/src/compiler/scala/tools/nsc/typechecker/splain/SplainFormatting.scala @@ -14,8 +14,9 @@ package scala.tools.nsc package typechecker package splain +import scala.collection.immutable.{List, Nil, Seq} import scala.collection.mutable -import scala.reflect.internal.TypeDebugging.AnsiColor._ +import scala.language.implicitConversions class FormatCache[K, V](cache: mutable.Map[K, V]) { def apply(k: K, orElse: => V): V = cache.getOrElseUpdate(k, orElse) @@ -28,7 +29,10 @@ object FormatCache { trait SplainFormatters { self: Analyzer => - import global._, definitions._ + import global._ + import definitions._ + + implicit def asSimpleName(s: String): SimpleName = SimpleName(s) def formatType(tpe: Type, top: Boolean): Formatted @@ -74,33 +78,71 @@ trait SplainFormatters { } object RefinedFormatter extends SpecialFormatter { + object DeclSymbol { def unapply(sym: Symbol): Option[(Formatted, Formatted)] = - if (sym.hasRawInfo) Some((Simple(SimpleName(sym.simpleName.toString)), formatType(sym.rawInfo, true))) + if (sym.hasRawInfo) Some((Simple(sym.simpleName.toString), formatType(sym.rawInfo, top = true))) else None } - def ignoredTypes: List[Type] = List(typeOf[Object], typeOf[Any], typeOf[AnyRef]) + lazy val ignoredTypes: List[Type] = List(typeOf[Object], typeOf[Any], typeOf[AnyRef]) + + def sanitizeParents: List[Type] => List[Type] = { ps => + val tpes = ps.distinct + val result = tpes.filterNot(t => ignoredTypes.exists(_ =:= t)) - def sanitizeParents: List[Type] => List[Type] = { - case List(tpe) => List(tpe) - case tpes => tpes.filter(t => !ignoredTypes.exists(_ =:= t)) + if (result.isEmpty) tpes.headOption.toList + else result + } + + object Refined { + def unapply(tpe: Type): Option[(List[Type], Scope)] = + tpe match { + case TypeRef(pre, sym, List(RefinedType(parents, decls))) + if decls.isEmpty && pre.typeSymbol.fullName == "zio" && sym.fullName == "zio.Has" => + val sanitized = sanitizeParents(parents) + if (sanitized.length == 1) + Some((List(TypeRef(pre, sym, sanitized.headOption.toList)), decls)) + else + None + case RefinedType(types, scope) => + if (scope.isEmpty) { + val subtypes = types.map(_.dealias).flatMap { + case Refined(types, _) => + types + case tpe => + List(tpe) + } + Some((subtypes, scope)) + } else + Some((types, scope)) + case t@SingleType(_, _) => + unapply(t.underlying) + case _ => + None + } } def formatDecl: Symbol => Formatted = { case DeclSymbol(n, t) => Decl(n, t) - case sym => Simple(SimpleName(sym.toString)) + case sym => Simple(sym.toString) } - def apply[A]( - tpe: Type, simple: String, args: List[A], formattedArgs: => List[Formatted], top: Boolean, - )(rec: (A, Boolean) => Formatted): Option[Formatted] = tpe match { - case Refined(parents, decls) => - Some(RefinedForm(sanitizeParents(parents).map(formatType(_, top)), decls.toList.map(formatDecl))) - case _ => None + override def apply[A]( + tpe: Type, + simple: String, + args: List[A], + formattedArgs: => List[Formatted], + top: Boolean + )(rec: (A, Boolean) => Formatted): Option[Formatted] = { + tpe match { + case Refined(parents, decls) => + Some(RefinedForm(sanitizeParents(parents).map(formatType(_, top)), decls.toList.map(formatDecl))) + case _ => None + } } - val none: Formatted = Simple(SimpleName("")) + val none: Formatted = Simple("") def separate[A](left: List[A], right: List[A]): (List[A], List[A], List[A]) = { val leftS = Set(left: _*) @@ -112,13 +154,22 @@ trait SplainFormatters { } def matchTypes(left: List[Type], right: List[Type]): List[Formatted] = { - val (common, uniqueLeft, uniqueRight) = separate(left.map(formatType(_, true)), right.map(formatType(_, true))) - val diffs = uniqueLeft.zipAll(uniqueRight, none, none).map { case (l, r) => Diff(l, r) } - common ::: diffs + val (common, uniqueLeft, uniqueRight) = + separate(left.map(formatType(_, top = true)), right.map(formatType(_, top = true))) + val diffs = uniqueLeft + .zipAll(uniqueRight, none, none) + .map { + case (l, r) => + Diff(l, r) + } + common ++ diffs } def filterDecls(syms: List[Symbol]): List[(Formatted, Formatted)] = - syms.collect { case DeclSymbol(sym, rhs) => (sym, rhs) } + syms.collect { + case DeclSymbol(sym, rhs) => + (sym, rhs) + } def matchDecls(left: List[Symbol], right: List[Symbol]): List[Formatted] = { val (common, uniqueLeft, uniqueRight) = separate(filterDecls(left), filterDecls(right)) @@ -130,7 +181,9 @@ trait SplainFormatters { case (None, Some((sym, r))) => DeclDiff(sym, none, r) case (Some((sym, l)), None) => DeclDiff(sym, l, none) } - common.map { case (sym, rhs) => Decl(sym, rhs) } ++ diffs + common.map { + case (sym, rhs) => Decl(sym, rhs) + } ++ diffs } def diff(left: Type, right: Type, top: Boolean): Option[Formatted] = @@ -155,11 +208,247 @@ trait SplainFormatters { } } +object SplainFormatting { + + import scala.reflect.internal.TypeDebugging.AnsiColor._ + + val ELLIPSIS: String = "⋮".blue +} + trait SplainFormatting extends SplainFormatters { self: Analyzer => import global._ + import SplainFormatting._ + import scala.reflect.internal.TypeDebugging.AnsiColor._ + + case class ImplicitErrorLink( + fromTree: ImplicitError, + fromHistory: DivergentImplicitTypeError + ) { + + val sameCandidateTree: Boolean = fromTree.candidate equalsStructure fromHistory.underlyingTree + + val samePendingType: Boolean = fromTree.specifics match { + case ss: ImplicitErrorSpecifics.NotFound => + fromHistory.pt0 =:= ss.param.tpe + case _ => + false + } + + val moreSpecificPendingType: Boolean = fromTree.specifics match { + case ss: ImplicitErrorSpecifics.NotFound => + fromHistory.pt0 <:< ss.param.tpe + case _ => + false + } + + val sameStartingWith: Boolean = { + fromHistory.sym.fullLocationString == fromTree.candidate.symbol.fullLocationString + } + + lazy val divergingSearchStartingWithHere: Boolean = sameStartingWith + + lazy val divergingSearchDiscoveredHere: Boolean = sameCandidateTree && moreSpecificPendingType + } + + object ImplicitErrorLink {} + + case class ImplicitErrorTree( + error: ImplicitError, + children: Seq[ImplicitErrorTree] = Nil + ) { + + import ImplicitErrorTree._ + + def doCollectFull(alwaysDisplayRoot: Boolean = false): Seq[ErrorNode] = + if (children.isEmpty) Seq(ErrorNode(error, alwaysShow = true)) + else { + + Seq(ErrorNode(error, alwaysShow = alwaysDisplayRoot)) ++ { + + if (children.size >= 2) children.flatMap(_.doCollectFull(true)) + else children.flatMap(_.doCollectFull(false)) + } + } + + lazy val collectFull: Seq[ErrorNode] = doCollectFull(true) + + lazy val collectCompact: Seq[ErrorNode] = { + + val displayed = collectFull.zipWithIndex.filter { + case (v, _) => + v.alwaysShow + } + + val ellipsisIndices = displayed.map(_._2 - 1).toSet + (collectFull.size - 1) + + val withEllipsis = displayed.map { + case (v, i) => + if (!ellipsisIndices.contains(i)) v.copy(showEllipsis = true) + else v + } + + withEllipsis + } + + case class FormattedChain( + source: Seq[ErrorNode] + ) { + + val toList: List[String] = { + val collected = source.toList + val baseIndent = collected.headOption.map(_.nesting).getOrElse(0) + + val formatted = collected.map { v => + val formatted = v.formatted + if (v.showEllipsis) formatted.copy(_2 = formatted._2 :+ ELLIPSIS) + else formatted + } + + indentTree(formatted, baseIndent) + } + + override lazy val toString: String = toList.mkString("\n") + } + + object FormattedChain { + + object Full extends FormattedChain(collectFull) + + object Compact extends FormattedChain(collectCompact) + + val display: FormattedChain = if (settings.VimplicitsVerboseTree.value) Full else Compact + } + + override def toString: String = FormattedChain.Full.toString + } + + object ImplicitErrorTree { + + case class ErrorNode( + error: ImplicitError, + alwaysShow: Boolean, + showEllipsis: Boolean = false + ) { + + def nesting: RunId = error.nesting + + val formatted: (String, List[String], RunId) = + formatNestedImplicit(error) + } + + def fromNode( + Node: ImplicitError, + offsprings: List[ImplicitError] + ): ImplicitErrorTree = { + val topNesting = Node.nesting + + val children = fromChildren( + offsprings, + topNesting + ) + + ImplicitErrorTree(Node, children) + } + + def fromChildren( + offsprings: List[ImplicitError], + topNesting: Int + ): List[ImplicitErrorTree] = { + + if (offsprings.isEmpty) + return Nil + + val minNesting = offsprings.map(v => v.nesting).min + + if (minNesting < topNesting + 1) + throw new InternalError( + "Detail: nesting level of offsprings of an implicit search tree node should be higher" + ) + + val wII = offsprings.zipWithIndex + + val childrenII = wII + .filter { + case (sub, _) => + if (sub.nesting < minNesting) { + throw new InternalError( + s"Detail: Sub-node in implicit tree can only have nesting level larger than top node," + + s" but (${sub.nesting} < $minNesting)" + ) + } + + sub.nesting == minNesting + } + .map(_._2) + + val ranges = { + + val seqs = (childrenII ++ Seq(offsprings.size)) + .sliding(2) + .toList + + seqs.map { + case Seq(from, until) => + from -> until + case _ => + throw new InternalError("Detail: index should not be empty") + } + } + + val children = ranges.map { range => + val _top = offsprings(range._1) + + val _offsprings = offsprings.slice(range._1 + 1, range._2) + + fromNode( + _top, + _offsprings + ) + } + + mergeDuplicates(children) + // children + } + + def mergeDuplicates(children: List[ImplicitErrorTree]): List[ImplicitErrorTree] = { + val errors = children.map(_.error).distinct + + val grouped = errors.map { ee => + val group = children.filter(c => c.error == ee) + + val mostSpecificError = group.head.error + // TODO: this old design is based on a huge hypothesis, should it be improved + // val mostSpecificError = group.map(_.error).maxBy(v => v.candidate.toString.length) + + val allChildren = group.flatMap(v => v.children) + val mergedChildren = mergeDuplicates(allChildren) + + ImplicitErrorTree(mostSpecificError, mergedChildren) + } + + grouped.distinctBy(v => v.FormattedChain.Full.toString) // TODO: this may lose information + } + } + + + def formatNestedImplicit(err: ImplicitError): (String, List[String], Int) = { + + val candidate = ImplicitError.cleanCandidate(err) + val problem = s"${candidate.red} invalid because" + val reason = err.specifics match { + case e: ImplicitErrorSpecifics.NotFound => implicitMessage(e.param, NoImplicitFoundAnnotation(err.candidate, e.param)._2) + case e: ImplicitErrorSpecifics.NonconformantBounds => formatNonConfBounds(e) + } + val base = (problem, reason, err.nesting) + + val reasons = base._2 + + (problem, reasons, base._3) + } + val breakInfixLength: Int = 70 def dealias(tpe: Type) = @@ -451,16 +740,6 @@ trait SplainFormatting extends SplainFormatters { List("nonconformant bounds;", types.red, params.green) } - def formatNestedImplicit(err: ImplicitError): (String, List[String], Int) = { - val candidate = ImplicitError.cleanCandidate(err) - val problem = s"${candidate.red} invalid because" - val reason = err.specifics match { - case e: ImplicitErrorSpecifics.NotFound => implicitMessage(e.param, NoImplicitFoundAnnotation(err.candidate, e.param)._2) - case e: ImplicitErrorSpecifics.NonconformantBounds => formatNonConfBounds(e) - } - (problem, reason, err.nesting) - } - def hideImpError(error: ImplicitError): Boolean = error.specifics match { case ImplicitErrorSpecifics.NonconformantBounds(_, _, _) => true case ImplicitErrorSpecifics.NotFound(_) => false @@ -480,41 +759,12 @@ trait SplainFormatting extends SplainFormatters { def deepestLevel(chain: List[ImplicitError]) = chain.foldLeft(0)((z, a) => if (a.nesting > z) a.nesting else z) - def formatImplicitChainTreeCompact(chain: List[ImplicitError]): Option[List[String]] = { - chain.headOption.map { head => - val max = deepestLevel(chain) - val leaves = chain.drop(1).dropWhile(_.nesting < max) - val base = if (head.nesting == 0) 0 else 1 - val (fhh, fht, fhn) = formatNestedImplicit(head) - val spacer = if (leaves.nonEmpty && leaves.length < chain.length) List("⋮".blue) else Nil - val fh = (fhh, fht ++ spacer, fhn) - val ft = leaves.map(formatNestedImplicit) - indentTree(fh :: ft, base) - } - } - def formatImplicitChainTreeFull(chain: List[ImplicitError]): List[String] = formatIndentTree(chain, chain.headOption.map(_.nesting).getOrElse(0)) def formatImplicitChainFlat(chain: List[ImplicitError]): List[String] = chain.map(formatNestedImplicit).flatMap { case (h, t, _) => h :: t } - def formatImplicitChain(chain: List[ImplicitError]): List[String] = { - val compact = if (settings.VimplicitsVerboseTree.value) None else formatImplicitChainTreeCompact(chain) - compact.getOrElse(formatImplicitChainTreeFull(chain)) - } - - /** Remove duplicates and special cases that should not be shown. - * In some cases, candidates are reported twice, once as `Foo.f` and once as - * `f`. `ImplicitError.equals` checks the simple names for identity, which - * is suboptimal, but works for 99% of cases. - * Special cases are handled in [[hideImpError]] */ - def formatNestedImplicits(errors: List[ImplicitError]) = { - val visible = errors.filterNot(hideImpError) - val chains = splitChains(visible).map(_.distinct).distinct - chains.map(formatImplicitChain).flatMap("" :: _).drop(1) - } - def implicitMessage(param: Symbol, annotationMsg: String): List[String] = { val tpe = param.tpe val msg = if (annotationMsg.isEmpty) Nil else annotationMsg.split("\n").toList.map(_.blue) :+ "" @@ -535,6 +785,24 @@ trait SplainFormatting extends SplainFormatters { } } - def formatImplicitError(param: Symbol, errors: List[ImplicitError], annotationMsg: String) = - ("implicit error;" :: implicitMessage(param, annotationMsg) ::: formatNestedImplicits(errors)).mkString("\n") + def formatImplicitError( + param: Symbol, + errors: List[ImplicitError], + annotationMsg: String + ): String = { + + val msg = implicitMessage(param, annotationMsg) + val errorTrees = ImplicitErrorTree.fromChildren(errors, -1) + + val errorTreesStr = errorTrees.map(_.FormattedChain.display.toString) + + val components: Seq[String] = + Seq("implicit error;") ++ + msg ++ + errorTreesStr + + val result = components.mkString("\n") + + result + } } diff --git a/src/compiler/scala/tools/nsc/util/ClassPath.scala b/src/compiler/scala/tools/nsc/util/ClassPath.scala index 520d10c73e9e..291b0422f5d8 100644 --- a/src/compiler/scala/tools/nsc/util/ClassPath.scala +++ b/src/compiler/scala/tools/nsc/util/ClassPath.scala @@ -14,8 +14,7 @@ package scala.tools.nsc package util import io.{AbstractFile, Directory, File, Jar} -import java.net.MalformedURLException -import java.net.URL +import java.net.{MalformedURLException, URI, URISyntaxException, URL} import java.util.regex.PatternSyntaxException import File.pathSeparator @@ -184,8 +183,8 @@ object ClassPath { } def specToURL(spec: String): Option[URL] = - try Some(new URL(spec)) - catch { case _: MalformedURLException => None } + try Some(new URI(spec).toURL) + catch { case _: MalformedURLException | _: URISyntaxException => None } def manifests: List[java.net.URL] = { import scala.jdk.CollectionConverters._ diff --git a/src/compiler/scala/tools/nsc/util/SimpleTracer.scala b/src/compiler/scala/tools/nsc/util/SimpleTracer.scala index af49114e52f5..da9cccbcb923 100644 --- a/src/compiler/scala/tools/nsc/util/SimpleTracer.scala +++ b/src/compiler/scala/tools/nsc/util/SimpleTracer.scala @@ -18,8 +18,8 @@ package util import java.io.PrintStream /** A simple tracer - * @param out: The print stream where trace info should be sent - * @param enabled: A condition that must be true for trace info to be produced. + * @param out The print stream where trace info should be sent + * @param enabled A condition that must be true for trace info to be produced. */ class SimpleTracer(out: PrintStream, enabled: Boolean = true) { def apply[T](msg: => String)(value: T): T = { diff --git a/src/compiler/scala/tools/nsc/util/StringUtil.scala b/src/compiler/scala/tools/nsc/util/StringUtil.scala index 14eb7aae381b..cbe98088a26e 100644 --- a/src/compiler/scala/tools/nsc/util/StringUtil.scala +++ b/src/compiler/scala/tools/nsc/util/StringUtil.scala @@ -15,15 +15,12 @@ package util import scala.collection.immutable.Seq -trait StringUtil { - def oxford(vs: Seq[String], conj: String): String = { +object StringUtil { + def oxford(vs: Seq[String], conj: String): String = vs match { case Seq() => "" case Seq(a) => a case Seq(a, b) => s"$a $conj $b" - case xs => xs.dropRight(1).mkString(", ") + s", $conj " + xs.last + case xs => xs.init.mkString("", ", ", s", $conj ${xs.last}") } - } } - -object StringUtil extends StringUtil diff --git a/src/compiler/scala/tools/reflect/package.scala b/src/compiler/scala/tools/reflect/package.scala index 9deafe710e08..3f592e3ae57b 100644 --- a/src/compiler/scala/tools/reflect/package.scala +++ b/src/compiler/scala/tools/reflect/package.scala @@ -89,7 +89,7 @@ package object reflect { val NSC_ERROR = Reporter.ERROR def doReport(pos: Position, msg: String, nscSeverity: NscSeverity): Unit = - frontEnd.log(pos, msg, nscSeverity match { + frontEnd.log(pos, msg, (nscSeverity: @unchecked) match { case NSC_INFO => API_INFO case NSC_WARNING => API_WARNING case NSC_ERROR => API_ERROR diff --git a/src/compiler/scala/tools/tasty/TastyFormat.scala b/src/compiler/scala/tools/tasty/TastyFormat.scala index a967b916ee51..8412767fdfa9 100644 --- a/src/compiler/scala/tools/tasty/TastyFormat.scala +++ b/src/compiler/scala/tools/tasty/TastyFormat.scala @@ -35,7 +35,7 @@ object TastyFormat { * compatibility, but remains backwards compatible, with all * preceeding `MinorVersion`. */ - final val MinorVersion: Int = 2 + final val MinorVersion: Int = 3 /** Natural Number. The `ExperimentalVersion` allows for * experimentation with changes to TASTy without committing @@ -325,6 +325,7 @@ object TastyFormat { // final val ??? = 178 // final val ??? = 179 final val METHODtype = 180 + final val APPLYsigpoly = 181 final val MATCHtype = 190 final val MATCHtpt = 191 @@ -491,6 +492,7 @@ object TastyFormat { case BOUNDED => "BOUNDED" case APPLY => "APPLY" case TYPEAPPLY => "TYPEAPPLY" + case APPLYsigpoly => "APPLYsigpoly" case NEW => "NEW" case THROW => "THROW" case TYPED => "TYPED" diff --git a/src/compiler/scala/tools/util/PathResolver.scala b/src/compiler/scala/tools/util/PathResolver.scala index a26219b6606b..6ac715ae9c99 100644 --- a/src/compiler/scala/tools/util/PathResolver.scala +++ b/src/compiler/scala/tools/util/PathResolver.scala @@ -16,12 +16,12 @@ package util import java.net.URL +import scala.reflect.io.{Directory, File, Path} import scala.tools.reflect.WrappedProperties.AccessControl import scala.tools.nsc.{CloseableRegistry, Settings} +import scala.tools.nsc.classpath._ import scala.tools.nsc.util.ClassPath -import scala.reflect.io.{Directory, File, Path} import PartialFunction.condOpt -import scala.tools.nsc.classpath._ // Loosely based on the draft specification at: // https://wiki.scala-lang.org/display/SIW/Classpath @@ -211,22 +211,21 @@ final class PathResolver(settings: Settings, closeableRegistry: CloseableRegistr private val classPathFactory = new ClassPathFactory(settings, closeableRegistry) - import PathResolver.{ AsLines, Defaults, ppcp } + import PathResolver.{AsLines, Defaults, ppcp} - private def cmdLineOrElse(name: String, alt: String) = { - (commandLineFor(name) match { - case Some("") => None - case x => x - }) getOrElse alt - } + private def cmdLineOrElse(name: String, alt: String) = + commandLineFor(name) match { + case Some("") | None => alt + case Some(x) => x + } private def commandLineFor(s: String): Option[String] = condOpt(s) { - case "javabootclasspath" => settings.javabootclasspath.value - case "javaextdirs" => settings.javaextdirs.value - case "bootclasspath" => settings.bootclasspath.value - case "extdirs" => settings.extdirs.value - case "classpath" | "cp" => settings.classpath.value - case "sourcepath" => settings.sourcepath.value + case "javabootclasspath" => settings.javabootclasspath.value + case "javaextdirs" => settings.javaextdirs.value + case "bootclasspath" => settings.bootclasspath.value + case "extdirs" => settings.extdirs.value + case "classpath" | "cp" => settings.classpath.value + case "sourcepath" => settings.sourcepath.value } /** Calculated values based on any given command line options, falling back on @@ -258,7 +257,8 @@ final class PathResolver(settings: Settings, closeableRegistry: CloseableRegistr // Assemble the elements! def basis = List[Iterable[ClassPath]]( - jrt, // 0. The Java 9+ classpath (backed by the ct.sym or jrt:/ virtual system, if available) + jrt // 0. The Java 9+ classpath (backed by the ct.sym or jrt:/ virtual system, if available) + .filter(_ => !settings.javabootclasspath.isSetByUser), // respect explicit `-javabootclasspath rt.jar` classesInPath(javaBootClassPath), // 1. The Java bootstrap class path. contentsOfDirsInPath(javaExtDirs), // 2. The Java extension class path. classesInExpandedPath(javaUserClassPath), // 3. The Java application class path. diff --git a/src/intellij/scala.ipr.SAMPLE b/src/intellij/scala.ipr.SAMPLE index 7d917aa7f815..750a66fa890b 100644 --- a/src/intellij/scala.ipr.SAMPLE +++ b/src/intellij/scala.ipr.SAMPLE @@ -232,7 +232,7 @@ - + @@ -243,7 +243,7 @@ - + @@ -252,7 +252,7 @@ - + @@ -266,7 +266,7 @@ - + @@ -287,7 +287,7 @@ - + @@ -296,14 +296,14 @@ - + - + @@ -312,7 +312,7 @@ - + @@ -448,7 +448,7 @@ - + @@ -457,7 +457,7 @@ - + @@ -467,7 +467,7 @@ - + @@ -498,7 +498,7 @@ - + @@ -514,7 +514,7 @@ - + @@ -525,7 +525,7 @@ - + diff --git a/src/interactive/scala/tools/nsc/interactive/Global.scala b/src/interactive/scala/tools/nsc/interactive/Global.scala index 327da6359c5f..9fe850139525 100644 --- a/src/interactive/scala/tools/nsc/interactive/Global.scala +++ b/src/interactive/scala/tools/nsc/interactive/Global.scala @@ -995,6 +995,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") respond(response) { scopeMemberFlatten(scopeMembers(pos)) } } + @nowarn("msg=inheritance from class LinkedHashMap") private class Members[M <: Member] extends LinkedHashMap[Name, Set[M]] { override def default(key: Name) = Set() diff --git a/src/library/scala/AnyVal.scala b/src/library/scala/AnyVal.scala index 1935a82c1b7f..018c388e9228 100644 --- a/src/library/scala/AnyVal.scala +++ b/src/library/scala/AnyVal.scala @@ -38,7 +38,7 @@ package scala * User-defined value classes which avoid object allocation... * * - must have a single `val` parameter that is the underlying runtime representation. - * - can define `def`s, but no `val`s, `var`s, or nested `traits`s, `class`es or `object`s. + * - can define `def`s, but no `val`s, `var`s, or nested `trait`s, `class`es or `object`s. * - typically extend no other trait apart from `AnyVal`. * - cannot be used in type tests or pattern matching. * - may not override `equals` or `hashCode` methods. diff --git a/src/library/scala/Array.scala b/src/library/scala/Array.scala index c6266e6ef1c4..6544548efdec 100644 --- a/src/library/scala/Array.scala +++ b/src/library/scala/Array.scala @@ -624,7 +624,7 @@ object Array { * * @see [[https://www.scala-lang.org/files/archive/spec/2.13/ Scala Language Specification]], for in-depth information on the transformations the Scala compiler makes on Arrays (Sections 6.6 and 6.15 respectively.) * @see [[https://docs.scala-lang.org/sips/scala-2-8-arrays.html "Scala 2.8 Arrays"]] the Scala Improvement Document detailing arrays since Scala 2.8. - * @see [[https://docs.scala-lang.org/overviews/collections/arrays.html "The Scala 2.8 Collections' API"]] section on `Array` by Martin Odersky for more information. + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/arrays.html "The Scala 2.8 Collections' API"]] section on `Array` by Martin Odersky for more information. * @hideImplicitConversion scala.Predef.booleanArrayOps * @hideImplicitConversion scala.Predef.byteArrayOps * @hideImplicitConversion scala.Predef.charArrayOps diff --git a/src/library/scala/MatchError.scala b/src/library/scala/MatchError.scala index 49a72b4ef1fa..bc94f2cbabdb 100644 --- a/src/library/scala/MatchError.scala +++ b/src/library/scala/MatchError.scala @@ -23,11 +23,11 @@ final class MatchError(@transient obj: Any) extends RuntimeException { private[this] lazy val objString = { def ofClass = "of class " + obj.getClass.getName if (obj == null) "null" - else try { - obj.toString() + " (" + ofClass + ")" - } catch { - case _: Throwable => "an instance " + ofClass - } + else + try s"$obj ($ofClass)" + catch { + case _: Throwable => "an instance " + ofClass + } } @throws[java.io.ObjectStreamException] diff --git a/src/library/scala/annotation/elidable.scala b/src/library/scala/annotation/elidable.scala index 9d15449fac18..f2028bfd02fb 100644 --- a/src/library/scala/annotation/elidable.scala +++ b/src/library/scala/annotation/elidable.scala @@ -75,6 +75,29 @@ package scala.annotation * (O: C).f() // elided if compiled with `-Xelide-below 1` * } * }}} + * + * Note for Scala 3 users: + * If you're using Scala 3, the annotation exists since Scala 3 uses the Scala 2 + * standard library, but it's unsupported by the Scala 3 compiler. Instead, to + * achieve the same result you'd want to utilize the `inline if` feature to + * introduce behavior that makes a method de facto elided at compile-time. + * {{{ + * type LogLevel = Int + * + * object LogLevel: + * inline val Info = 0 + * inline val Warn = 1 + * inline val Debug = 2 + * + * inline val appLogLevel = LogLevel.Warn + * + * inline def log(msg: String, inline level: LogLevel): Unit = + * inline if (level <= appLogLevel) then println(msg) + * + * log("Warn log", LogLevel.Warn) + * + * log("Debug log", LogLevel. Debug) + * }}} */ final class elidable(final val level: Int) extends scala.annotation.ConstantAnnotation diff --git a/src/library/scala/collection/ArrayOps.scala b/src/library/scala/collection/ArrayOps.scala index 35de85f837e0..485427886625 100644 --- a/src/library/scala/collection/ArrayOps.scala +++ b/src/library/scala/collection/ArrayOps.scala @@ -125,11 +125,12 @@ object ArrayOps { private[this] val len = xs.length override def knownSize: Int = len - pos def hasNext: Boolean = pos < len - def next(): A = try { + def next(): A = { + if (pos >= xs.length) Iterator.empty.next() val r = xs(pos) pos += 1 r - } catch { case _: ArrayIndexOutOfBoundsException => Iterator.empty.next() } + } override def drop(n: Int): Iterator[A] = { if (n > 0) { val newPos = pos + n @@ -145,11 +146,12 @@ object ArrayOps { private final class ReverseIterator[@specialized(Specializable.Everything) A](xs: Array[A]) extends AbstractIterator[A] with Serializable { private[this] var pos = xs.length-1 def hasNext: Boolean = pos >= 0 - def next(): A = try { + def next(): A = { + if (pos < 0) Iterator.empty.next() val r = xs(pos) pos -= 1 r - } catch { case _: ArrayIndexOutOfBoundsException => Iterator.empty.next() } + } override def drop(n: Int): Iterator[A] = { if (n > 0) pos = Math.max( -1, pos - n) @@ -227,14 +229,14 @@ final class ArrayOps[A](private val xs: Array[A]) extends AnyVal { * @return the first element of this array. * @throws NoSuchElementException if the array is empty. */ - def head: A = try xs.apply(0) catch { case _: ArrayIndexOutOfBoundsException => throw new NoSuchElementException("head of empty array") } + def head: A = if (nonEmpty) xs.apply(0) else throw new NoSuchElementException("head of empty array") /** Selects the last element. * * @return The last element of this array. * @throws NoSuchElementException If the array is empty. */ - def last: A = try xs.apply(xs.length-1) catch { case _: ArrayIndexOutOfBoundsException => throw new NoSuchElementException("last of empty array") } + def last: A = if (nonEmpty) xs.apply(xs.length-1) else throw new NoSuchElementException("last of empty array") /** Optionally selects the first element. * @@ -500,7 +502,7 @@ final class ArrayOps[A](private val xs: Array[A]) extends AnyVal { * @tparam A2 the element type of the second resulting collection * @param f the 'split function' mapping the elements of this array to an [[scala.util.Either]] * - * @return a pair of arrays: the first one made of those values returned by `f` that were wrapped in [[scala.util.Left]], + * @return a pair of arrays: the first one made of those values returned by `f` that were wrapped in [[scala.util.Left]], * and the second one made of those wrapped in [[scala.util.Right]]. */ def partitionMap[A1: ClassTag, A2: ClassTag](f: A => Either[A1, A2]): (Array[A1], Array[A2]) = { val res1 = ArrayBuilder.make[A1] diff --git a/src/library/scala/collection/BuildFrom.scala b/src/library/scala/collection/BuildFrom.scala index e0e92a7e66a1..bc9c49d9493c 100644 --- a/src/library/scala/collection/BuildFrom.scala +++ b/src/library/scala/collection/BuildFrom.scala @@ -45,14 +45,14 @@ trait BuildFrom[-From, -A, +C] extends Any { self => object BuildFrom extends BuildFromLowPriority1 { /** Build the source collection type from a MapOps */ - implicit def buildFromMapOps[CC[X, Y] <: Map[X, Y] with MapOps[X, Y, CC, _], K0, V0, K, V]: BuildFrom[CC[K0, V0], (K, V), CC[K, V]] = new BuildFrom[CC[K0, V0], (K, V), CC[K, V]] { + implicit def buildFromMapOps[CC[X, Y] <: Map[X, Y] with MapOps[X, Y, CC, _], K0, V0, K, V]: BuildFrom[CC[K0, V0] with Map[K0, V0], (K, V), CC[K, V] with Map[K, V]] = new BuildFrom[CC[K0, V0], (K, V), CC[K, V]] { //TODO: Reuse a prototype instance def newBuilder(from: CC[K0, V0]): Builder[(K, V), CC[K, V]] = (from: MapOps[K0, V0, CC, _]).mapFactory.newBuilder[K, V] def fromSpecific(from: CC[K0, V0])(it: IterableOnce[(K, V)]): CC[K, V] = (from: MapOps[K0, V0, CC, _]).mapFactory.from(it) } /** Build the source collection type from a SortedMapOps */ - implicit def buildFromSortedMapOps[CC[X, Y] <: SortedMap[X, Y] with SortedMapOps[X, Y, CC, _], K0, V0, K : Ordering, V]: BuildFrom[CC[K0, V0], (K, V), CC[K, V]] = new BuildFrom[CC[K0, V0], (K, V), CC[K, V]] { + implicit def buildFromSortedMapOps[CC[X, Y] <: SortedMap[X, Y] with SortedMapOps[X, Y, CC, _], K0, V0, K : Ordering, V]: BuildFrom[CC[K0, V0] with SortedMap[K0, V0], (K, V), CC[K, V] with SortedMap[K, V]] = new BuildFrom[CC[K0, V0], (K, V), CC[K, V]] { def newBuilder(from: CC[K0, V0]): Builder[(K, V), CC[K, V]] = (from: SortedMapOps[K0, V0, CC, _]).sortedMapFactory.newBuilder[K, V] def fromSpecific(from: CC[K0, V0])(it: IterableOnce[(K, V)]): CC[K, V] = (from: SortedMapOps[K0, V0, CC, _]).sortedMapFactory.from(it) } @@ -92,7 +92,10 @@ object BuildFrom extends BuildFromLowPriority1 { trait BuildFromLowPriority1 extends BuildFromLowPriority2 { /** Build the source collection type from an Iterable with SortedOps */ - implicit def buildFromSortedSetOps[CC[X] <: SortedSet[X] with SortedSetOps[X, CC, _], A0, A : Ordering]: BuildFrom[CC[A0], A, CC[A]] = new BuildFrom[CC[A0], A, CC[A]] { + // Restating the upper bound of CC in the result type seems redundant, but it serves to prune the + // implicit search space for faster compilation and reduced change of divergence. See the compilation + // test in test/junit/scala/collection/BuildFromTest.scala and discussion in https://github.com/scala/scala/pull/10209 + implicit def buildFromSortedSetOps[CC[X] <: SortedSet[X] with SortedSetOps[X, CC, _], A0, A : Ordering]: BuildFrom[CC[A0] with SortedSet[A0], A, CC[A] with SortedSet[A]] = new BuildFrom[CC[A0], A, CC[A]] { def newBuilder(from: CC[A0]): Builder[A, CC[A]] = (from: SortedSetOps[A0, CC, _]).sortedIterableFactory.newBuilder[A] def fromSpecific(from: CC[A0])(it: IterableOnce[A]): CC[A] = (from: SortedSetOps[A0, CC, _]).sortedIterableFactory.from(it) } diff --git a/src/library/scala/collection/IterableOnce.scala b/src/library/scala/collection/IterableOnce.scala index 2f5fc3ad77ec..65d8dce08ae4 100644 --- a/src/library/scala/collection/IterableOnce.scala +++ b/src/library/scala/collection/IterableOnce.scala @@ -19,6 +19,7 @@ import scala.collection.mutable.StringBuilder import scala.language.implicitConversions import scala.math.{Numeric, Ordering} import scala.reflect.ClassTag +import scala.runtime.AbstractFunction2 /** * A template trait for collections which can be traversed either once only @@ -76,8 +77,8 @@ trait IterableOnce[+A] extends Any { } /** @return The number of elements in this $coll, if it can be cheaply computed, - * -1 otherwise. Cheaply usually means: Not requiring a collection traversal. - */ + * -1 otherwise. Cheaply usually means: Not requiring a collection traversal. + */ def knownSize: Int = -1 } @@ -318,184 +319,184 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => /////////////////////////////////////////////////////////////// Abstract methods that must be implemented /** Produces a $coll containing cumulative results of applying the - * operator going left to right, including the initial value. - * - * $willNotTerminateInf - * $orderDependent - * - * @tparam B the type of the elements in the resulting collection - * @param z the initial value - * @param op the binary operator applied to the intermediate result and the element - * @return collection with intermediate results - */ + * operator going left to right, including the initial value. + * + * $willNotTerminateInf + * $orderDependent + * + * @tparam B the type of the elements in the resulting collection + * @param z the initial value + * @param op the binary operator applied to the intermediate result and the element + * @return collection with intermediate results + */ def scanLeft[B](z: B)(op: (B, A) => B): CC[B] /** Selects all elements of this $coll which satisfy a predicate. - * - * @param p the predicate used to test elements. - * @return a new $coll consisting of all elements of this $coll that satisfy the given - * predicate `p`. The order of the elements is preserved. - */ + * + * @param p the predicate used to test elements. + * @return a new $coll consisting of all elements of this $coll that satisfy the given + * predicate `p`. The order of the elements is preserved. + */ def filter(p: A => Boolean): C /** Selects all elements of this $coll which do not satisfy a predicate. - * - * @param pred the predicate used to test elements. - * @return a new $coll consisting of all elements of this $coll that do not satisfy the given - * predicate `pred`. Their order may not be preserved. - */ + * + * @param pred the predicate used to test elements. + * @return a new $coll consisting of all elements of this $coll that do not satisfy the given + * predicate `pred`. Their order may not be preserved. + */ def filterNot(pred: A => Boolean): C /** Selects the first ''n'' elements. - * $orderDependent - * @param n the number of elements to take from this $coll. - * @return a $coll consisting only of the first `n` elements of this $coll, - * or else the whole $coll, if it has less than `n` elements. - * If `n` is negative, returns an empty $coll. - */ + * $orderDependent + * @param n the number of elements to take from this $coll. + * @return a $coll consisting only of the first `n` elements of this $coll, + * or else the whole $coll, if it has less than `n` elements. + * If `n` is negative, returns an empty $coll. + */ def take(n: Int): C /** Takes longest prefix of elements that satisfy a predicate. - * $orderDependent - * @param p The predicate used to test elements. - * @return the longest prefix of this $coll whose elements all satisfy - * the predicate `p`. - */ + * $orderDependent + * @param p The predicate used to test elements. + * @return the longest prefix of this $coll whose elements all satisfy + * the predicate `p`. + */ def takeWhile(p: A => Boolean): C /** Selects all elements except first ''n'' ones. - * $orderDependent - * @param n the number of elements to drop from this $coll. - * @return a $coll consisting of all elements of this $coll except the first `n` ones, or else the - * empty $coll, if this $coll has less than `n` elements. - * If `n` is negative, don't drop any elements. - */ + * $orderDependent + * @param n the number of elements to drop from this $coll. + * @return a $coll consisting of all elements of this $coll except the first `n` ones, or else the + * empty $coll, if this $coll has less than `n` elements. + * If `n` is negative, don't drop any elements. + */ def drop(n: Int): C /** Drops longest prefix of elements that satisfy a predicate. - * $orderDependent - * @param p The predicate used to test elements. - * @return the longest suffix of this $coll whose first element - * does not satisfy the predicate `p`. - */ + * $orderDependent + * @param p The predicate used to test elements. + * @return the longest suffix of this $coll whose first element + * does not satisfy the predicate `p`. + */ def dropWhile(p: A => Boolean): C /** Selects an interval of elements. The returned $coll is made up - * of all elements `x` which satisfy the invariant: - * {{{ - * from <= indexOf(x) < until - * }}} - * $orderDependent - * - * @param from the lowest index to include from this $coll. - * @param until the lowest index to EXCLUDE from this $coll. - * @return a $coll containing the elements greater than or equal to - * index `from` extending up to (but not including) index `until` - * of this $coll. - */ + * of all elements `x` which satisfy the invariant: + * {{{ + * from <= indexOf(x) < until + * }}} + * $orderDependent + * + * @param from the lowest index to include from this $coll. + * @param until the lowest index to EXCLUDE from this $coll. + * @return a $coll containing the elements greater than or equal to + * index `from` extending up to (but not including) index `until` + * of this $coll. + */ def slice(from: Int, until: Int): C /** Builds a new $coll by applying a function to all elements of this $coll. - * - * @param f the function to apply to each element. - * @tparam B the element type of the returned $coll. - * @return a new $coll resulting from applying the given function - * `f` to each element of this $coll and collecting the results. - */ + * + * @param f the function to apply to each element. + * @tparam B the element type of the returned $coll. + * @return a new $coll resulting from applying the given function + * `f` to each element of this $coll and collecting the results. + */ def map[B](f: A => B): CC[B] /** Builds a new $coll by applying a function to all elements of this $coll - * and using the elements of the resulting collections. - * - * For example: - * - * {{{ - * def getWords(lines: Seq[String]): Seq[String] = lines flatMap (line => line split "\\W+") - * }}} - * - * The type of the resulting collection is guided by the static type of $coll. This might - * cause unexpected results sometimes. For example: - * - * {{{ - * // lettersOf will return a Seq[Char] of likely repeated letters, instead of a Set - * def lettersOf(words: Seq[String]) = words flatMap (word => word.toSet) - * - * // lettersOf will return a Set[Char], not a Seq - * def lettersOf(words: Seq[String]) = words.toSet flatMap ((word: String) => word.toSeq) - * - * // xs will be an Iterable[Int] - * val xs = Map("a" -> List(11,111), "b" -> List(22,222)).flatMap(_._2) - * - * // ys will be a Map[Int, Int] - * val ys = Map("a" -> List(1 -> 11,1 -> 111), "b" -> List(2 -> 22,2 -> 222)).flatMap(_._2) - * }}} - * - * @param f the function to apply to each element. - * @tparam B the element type of the returned collection. - * @return a new $coll resulting from applying the given collection-valued function - * `f` to each element of this $coll and concatenating the results. - */ + * and using the elements of the resulting collections. + * + * For example: + * + * {{{ + * def getWords(lines: Seq[String]): Seq[String] = lines flatMap (line => line split "\\W+") + * }}} + * + * The type of the resulting collection is guided by the static type of $coll. This might + * cause unexpected results sometimes. For example: + * + * {{{ + * // lettersOf will return a Seq[Char] of likely repeated letters, instead of a Set + * def lettersOf(words: Seq[String]) = words flatMap (word => word.toSet) + * + * // lettersOf will return a Set[Char], not a Seq + * def lettersOf(words: Seq[String]) = words.toSet flatMap ((word: String) => word.toSeq) + * + * // xs will be an Iterable[Int] + * val xs = Map("a" -> List(11,111), "b" -> List(22,222)).flatMap(_._2) + * + * // ys will be a Map[Int, Int] + * val ys = Map("a" -> List(1 -> 11,1 -> 111), "b" -> List(2 -> 22,2 -> 222)).flatMap(_._2) + * }}} + * + * @param f the function to apply to each element. + * @tparam B the element type of the returned collection. + * @return a new $coll resulting from applying the given collection-valued function + * `f` to each element of this $coll and concatenating the results. + */ def flatMap[B](f: A => IterableOnce[B]): CC[B] /** Converts this $coll of iterable collections into - * a $coll formed by the elements of these iterable - * collections. - * - * The resulting collection's type will be guided by the - * type of $coll. For example: - * - * {{{ - * val xs = List( - * Set(1, 2, 3), - * Set(1, 2, 3) - * ).flatten - * // xs == List(1, 2, 3, 1, 2, 3) - * - * val ys = Set( - * List(1, 2, 3), - * List(3, 2, 1) - * ).flatten - * // ys == Set(1, 2, 3) - * }}} - * - * @tparam B the type of the elements of each iterable collection. - * @param asIterable an implicit conversion which asserts that the element - * type of this $coll is an `Iterable`. - * @return a new $coll resulting from concatenating all element ${coll}s. - */ + * a $coll formed by the elements of these iterable + * collections. + * + * The resulting collection's type will be guided by the + * type of $coll. For example: + * + * {{{ + * val xs = List( + * Set(1, 2, 3), + * Set(1, 2, 3) + * ).flatten + * // xs == List(1, 2, 3, 1, 2, 3) + * + * val ys = Set( + * List(1, 2, 3), + * List(3, 2, 1) + * ).flatten + * // ys == Set(1, 2, 3) + * }}} + * + * @tparam B the type of the elements of each iterable collection. + * @param asIterable an implicit conversion which asserts that the element + * type of this $coll is an `Iterable`. + * @return a new $coll resulting from concatenating all element ${coll}s. + */ def flatten[B](implicit asIterable: A => IterableOnce[B]): CC[B] /** Builds a new $coll by applying a partial function to all elements of this $coll - * on which the function is defined. - * - * @param pf the partial function which filters and maps the $coll. - * @tparam B the element type of the returned $coll. - * @return a new $coll resulting from applying the given partial function - * `pf` to each element on which it is defined and collecting the results. - * The order of the elements is preserved. - */ + * on which the function is defined. + * + * @param pf the partial function which filters and maps the $coll. + * @tparam B the element type of the returned $coll. + * @return a new $coll resulting from applying the given partial function + * `pf` to each element on which it is defined and collecting the results. + * The order of the elements is preserved. + */ def collect[B](pf: PartialFunction[A, B]): CC[B] /** Zips this $coll with its indices. - * - * @return A new $coll containing pairs consisting of all elements of this $coll paired with their index. - * Indices start at `0`. - * @example - * `List("a", "b", "c").zipWithIndex == List(("a", 0), ("b", 1), ("c", 2))` - */ + * + * @return A new $coll containing pairs consisting of all elements of this $coll paired with their index. + * Indices start at `0`. + * @example + * `List("a", "b", "c").zipWithIndex == List(("a", 0), ("b", 1), ("c", 2))` + */ def zipWithIndex: CC[(A @uncheckedVariance, Int)] /** Splits this $coll into a prefix/suffix pair according to a predicate. - * - * Note: `c span p` is equivalent to (but possibly more efficient than) - * `(c takeWhile p, c dropWhile p)`, provided the evaluation of the - * predicate `p` does not cause any side-effects. - * $orderDependent - * - * @param p the test predicate - * @return a pair consisting of the longest prefix of this $coll whose - * elements all satisfy `p`, and the rest of this $coll. - */ + * + * Note: `c span p` is equivalent to (but possibly more efficient than) + * `(c takeWhile p, c dropWhile p)`, provided the evaluation of the + * predicate `p` does not cause any side-effects. + * $orderDependent + * + * @param p the test predicate + * @return a pair consisting of the longest prefix of this $coll whose + * elements all satisfy `p`, and the rest of this $coll. + */ def span(p: A => Boolean): (C, C) /** Splits this $coll into a prefix/suffix pair at a given position. @@ -531,32 +532,32 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => /////////////////////////////////////////////////////////////// Concrete methods based on iterator /** Tests whether this $coll is known to have a finite size. - * All strict collections are known to have finite size. For a non-strict - * collection such as `Stream`, the predicate returns `'''true'''` if all - * elements have been computed. It returns `'''false'''` if the stream is - * not yet evaluated to the end. Non-empty Iterators usually return - * `'''false'''` even if they were created from a collection with a known - * finite size. - * - * Note: many collection methods will not work on collections of infinite sizes. - * The typical failure mode is an infinite loop. These methods always attempt a - * traversal without checking first that `hasDefiniteSize` returns `'''true'''`. - * However, checking `hasDefiniteSize` can provide an assurance that size is - * well-defined and non-termination is not a concern. - * - * @deprecated This method is deprecated in 2.13 because it does not provide any - * actionable information. As noted above, even the collection library itself - * does not use it. When there is no guarantee that a collection is finite, it - * is generally best to attempt a computation anyway and document that it will - * not terminate for infinite collections rather than backing out because this - * would prevent performing the computation on collections that are in fact - * finite even though `hasDefiniteSize` returns `false`. - * - * @see method `knownSize` for a more useful alternative - * - * @return `'''true'''` if this collection is known to have finite size, - * `'''false'''` otherwise. - */ + * All strict collections are known to have finite size. For a non-strict + * collection such as `Stream`, the predicate returns `'''true'''` if all + * elements have been computed. It returns `'''false'''` if the stream is + * not yet evaluated to the end. Non-empty Iterators usually return + * `'''false'''` even if they were created from a collection with a known + * finite size. + * + * Note: many collection methods will not work on collections of infinite sizes. + * The typical failure mode is an infinite loop. These methods always attempt a + * traversal without checking first that `hasDefiniteSize` returns `'''true'''`. + * However, checking `hasDefiniteSize` can provide an assurance that size is + * well-defined and non-termination is not a concern. + * + * @deprecated This method is deprecated in 2.13 because it does not provide any + * actionable information. As noted above, even the collection library itself + * does not use it. When there is no guarantee that a collection is finite, it + * is generally best to attempt a computation anyway and document that it will + * not terminate for infinite collections rather than backing out because this + * would prevent performing the computation on collections that are in fact + * finite even though `hasDefiniteSize` returns `false`. + * + * @see method `knownSize` for a more useful alternative + * + * @return `'''true'''` if this collection is known to have finite size, + * `'''false'''` otherwise. + */ @deprecated("Check .knownSize instead of .hasDefiniteSize for more actionable information (see scaladoc for details)", "2.13.0") def hasDefiniteSize: Boolean = true @@ -568,21 +569,21 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => def isTraversableAgain: Boolean = false /** Apply `f` to each element for its side effects - * Note: [U] parameter needed to help scalac's type inference. - */ + * Note: [U] parameter needed to help scalac's type inference. + */ def foreach[U](f: A => U): Unit = { val it = iterator while(it.hasNext) f(it.next()) } /** Tests whether a predicate holds for all elements of this $coll. - * - * $mayNotTerminateInf - * - * @param p the predicate used to test elements. - * @return `true` if this $coll is empty or the given predicate `p` - * holds for all elements of this $coll, otherwise `false`. - */ + * + * $mayNotTerminateInf + * + * @param p the predicate used to test elements. + * @return `true` if this $coll is empty or the given predicate `p` + * holds for all elements of this $coll, otherwise `false`. + */ def forall(p: A => Boolean): Boolean = { var res = true val it = iterator @@ -591,12 +592,12 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => } /** Tests whether a predicate holds for at least one element of this $coll. - * - * $mayNotTerminateInf - * - * @param p the predicate used to test elements. - * @return `true` if the given predicate `p` is satisfied by at least one element of this $coll, otherwise `false` - */ + * + * $mayNotTerminateInf + * + * @param p the predicate used to test elements. + * @return `true` if the given predicate `p` is satisfied by at least one element of this $coll, otherwise `false` + */ def exists(p: A => Boolean): Boolean = { var res = false val it = iterator @@ -605,12 +606,12 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => } /** Counts the number of elements in the $coll which satisfy a predicate. - * - * $willNotTerminateInf - * - * @param p the predicate used to test elements. - * @return the number of elements satisfying the predicate `p`. - */ + * + * $willNotTerminateInf + * + * @param p the predicate used to test elements. + * @return the number of elements satisfying the predicate `p`. + */ def count(p: A => Boolean): Int = { var res = 0 val it = iterator @@ -619,14 +620,14 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => } /** Finds the first element of the $coll satisfying a predicate, if any. - * - * $mayNotTerminateInf - * $orderDependent - * - * @param p the predicate used to test elements. - * @return an option value containing the first element in the $coll - * that satisfies `p`, or `None` if none exists. - */ + * + * $mayNotTerminateInf + * $orderDependent + * + * @param p the predicate used to test elements. + * @return an option value containing the first element in the $coll + * that satisfies `p`, or `None` if none exists. + */ def find(p: A => Boolean): Option[A] = { val it = iterator while (it.hasNext) { @@ -636,15 +637,15 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => None } - // TODO 2.14+ move to IndexedSeqOps - private[this] def foldl[B](seq: IndexedSeq[A], start: Int, z: B, op: (B, A) => B): B = { + // in future, move to IndexedSeqOps + private def foldl[X >: A, B](seq: IndexedSeq[X], start: Int, z: B, op: (B, X) => B): B = { @tailrec def loop(at: Int, end: Int, acc: B): B = if (at == end) acc else loop(at + 1, end, op(acc, seq(at))) loop(start, seq.length, z) } - private[this] def foldr[B >: A](seq: IndexedSeq[A], op: (A, B) => B): B = { + private def foldr[X >: A, B >: X](seq: IndexedSeq[X], op: (X, B) => B): B = { @tailrec def loop(at: Int, acc: B): B = if (at == 0) acc else loop(at - 1, op(seq(at - 1), acc)) @@ -652,22 +653,22 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => } /** Applies a binary operator to a start value and all elements of this $coll, - * going left to right. - * - * $willNotTerminateInf - * $orderDependentFold - * - * @param z the start value. - * @param op the binary operator. - * @tparam B the result type of the binary operator. - * @return the result of inserting `op` between consecutive elements of this $coll, - * going left to right with the start value `z` on the left: - * `op(...op(z, x,,1,,), x,,2,,, ..., x,,n,,)` where `x,,1,,, ..., x,,n,,` - * are the elements of this $coll. - * Returns `z` if this $coll is empty. - */ + * going left to right. + * + * $willNotTerminateInf + * $orderDependentFold + * + * @param z the start value. + * @param op the binary operator. + * @tparam B the result type of the binary operator. + * @return the result of inserting `op` between consecutive elements of this $coll, + * going left to right with the start value `z` on the left: + * `op(...op(z, x,,1,,), x,,2,,, ..., x,,n,,)` where `x,,1,,, ..., x,,n,,` + * are the elements of this $coll. + * Returns `z` if this $coll is empty. + */ def foldLeft[B](z: B)(op: (B, A) => B): B = this match { - case seq: IndexedSeq[A @unchecked] => foldl(seq, 0, z, op) + case seq: IndexedSeq[A @unchecked] => foldl[A, B](seq, 0, z, op) case _ => var result = z val it = iterator @@ -678,19 +679,19 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => } /** Applies a binary operator to all elements of this $coll and a start value, - * going right to left. - * - * $willNotTerminateInf - * $orderDependentFold - * @param z the start value. - * @param op the binary operator. - * @tparam B the result type of the binary operator. - * @return the result of inserting `op` between consecutive elements of this $coll, - * going right to left with the start value `z` on the right: - * `op(x,,1,,, op(x,,2,,, ... op(x,,n,,, z)...))` where `x,,1,,, ..., x,,n,,` - * are the elements of this $coll. - * Returns `z` if this $coll is empty. - */ + * going right to left. + * + * $willNotTerminateInf + * $orderDependentFold + * @param z the start value. + * @param op the binary operator. + * @tparam B the result type of the binary operator. + * @return the result of inserting `op` between consecutive elements of this $coll, + * going right to left with the start value `z` on the right: + * `op(x,,1,,, op(x,,2,,, ... op(x,,n,,, z)...))` where `x,,1,,, ..., x,,n,,` + * are the elements of this $coll. + * Returns `z` if this $coll is empty. + */ def foldRight[B](z: B)(op: (A, B) => B): B = reversed.foldLeft(z)((b, a) => op(a, b)) @deprecated("Use foldLeft instead of /:", "2.13.0") @@ -700,145 +701,164 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => @`inline` final def :\ [B](z: B)(op: (A, B) => B): B = foldRight[B](z)(op) /** Folds the elements of this $coll using the specified associative binary operator. - * The default implementation in `IterableOnce` is equivalent to `foldLeft` but may be - * overridden for more efficient traversal orders. - * - * $undefinedorder - * $willNotTerminateInf - * - * @tparam A1 a type parameter for the binary operator, a supertype of `A`. - * @param z a neutral element for the fold operation; may be added to the result - * an arbitrary number of times, and must not change the result (e.g., `Nil` for list concatenation, - * 0 for addition, or 1 for multiplication). - * @param op a binary operator that must be associative. - * @return the result of applying the fold operator `op` between all the elements and `z`, or `z` if this $coll is empty. - */ + * The default implementation in `IterableOnce` is equivalent to `foldLeft` but may be + * overridden for more efficient traversal orders. + * + * $undefinedorder + * $willNotTerminateInf + * + * @tparam A1 a type parameter for the binary operator, a supertype of `A`. + * @param z a neutral element for the fold operation; may be added to the result + * an arbitrary number of times, and must not change the result (e.g., `Nil` for list concatenation, + * 0 for addition, or 1 for multiplication). + * @param op a binary operator that must be associative. + * @return the result of applying the fold operator `op` between all the elements and `z`, or `z` if this $coll is empty. + */ def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op) /** Reduces the elements of this $coll using the specified associative binary operator. - * - * $undefinedorder - * - * @tparam B A type parameter for the binary operator, a supertype of `A`. - * @param op A binary operator that must be associative. - * @return The result of applying reduce operator `op` between all the elements if the $coll is nonempty. - * @throws UnsupportedOperationException if this $coll is empty. - */ + * + * $undefinedorder + * + * @tparam B A type parameter for the binary operator, a supertype of `A`. + * @param op A binary operator that must be associative. + * @return The result of applying reduce operator `op` between all the elements if the $coll is nonempty. + * @throws UnsupportedOperationException if this $coll is empty. + */ def reduce[B >: A](op: (B, B) => B): B = reduceLeft(op) /** Reduces the elements of this $coll, if any, using the specified - * associative binary operator. - * - * $undefinedorder - * - * @tparam B A type parameter for the binary operator, a supertype of `A`. - * @param op A binary operator that must be associative. - * @return An option value containing result of applying reduce operator `op` between all - * the elements if the collection is nonempty, and `None` otherwise. - */ + * associative binary operator. + * + * $undefinedorder + * + * @tparam B A type parameter for the binary operator, a supertype of `A`. + * @param op A binary operator that must be associative. + * @return An option value containing result of applying reduce operator `op` between all + * the elements if the collection is nonempty, and `None` otherwise. + */ def reduceOption[B >: A](op: (B, B) => B): Option[B] = reduceLeftOption(op) /** Applies a binary operator to all elements of this $coll, - * going left to right. - * $willNotTerminateInf - * $orderDependentFold - * - * @param op the binary operator. - * @tparam B the result type of the binary operator. - * @return the result of inserting `op` between consecutive elements of this $coll, - * going left to right: - * `op( op( ... op(x,,1,,, x,,2,,) ..., x,,n-1,,), x,,n,,)` where `x,,1,,, ..., x,,n,,` - * are the elements of this $coll. - * @throws UnsupportedOperationException if this $coll is empty. */ + * going left to right. + * $willNotTerminateInf + * $orderDependentFold + * + * @param op the binary operator. + * @tparam B the result type of the binary operator. + * @return the result of inserting `op` between consecutive elements of this $coll, + * going left to right: + * `op( op( ... op(x,,1,,, x,,2,,) ..., x,,n-1,,), x,,n,,)` where `x,,1,,, ..., x,,n,,` + * are the elements of this $coll. + * @throws UnsupportedOperationException if this $coll is empty. + */ def reduceLeft[B >: A](op: (B, A) => B): B = this match { case seq: IndexedSeq[A @unchecked] if seq.length > 0 => foldl(seq, 1, seq(0), op) - case _ => - val it = iterator - if (it.isEmpty) - throw new UnsupportedOperationException("empty.reduceLeft") - - var first = true - var acc: B = null.asInstanceOf[B] - - while (it.hasNext) { - val x = it.next() - if (first) { - acc = x - first = false - } - else acc = op(acc, x) - } + case _ if knownSize == 0 => throw new UnsupportedOperationException("empty.reduceLeft") + case _ => reduceLeftIterator[B](throw new UnsupportedOperationException("empty.reduceLeft"))(op) + } + private final def reduceLeftIterator[B >: A](onEmpty: => B)(op: (B, A) => B): B = { + val it = iterator + if (it.hasNext) { + var acc: B = it.next() + while (it.hasNext) + acc = op(acc, it.next()) acc + } + else onEmpty } /** Applies a binary operator to all elements of this $coll, going right to left. - * $willNotTerminateInf - * $orderDependentFold - * - * @param op the binary operator. - * @tparam B the result type of the binary operator. - * @return the result of inserting `op` between consecutive elements of this $coll, - * going right to left: - * `op(x,,1,,, op(x,,2,,, ..., op(x,,n-1,,, x,,n,,)...))` where `x,,1,,, ..., x,,n,,` - * are the elements of this $coll. - * @throws UnsupportedOperationException if this $coll is empty. - */ + * $willNotTerminateInf + * $orderDependentFold + * + * @param op the binary operator. + * @tparam B the result type of the binary operator. + * @return the result of inserting `op` between consecutive elements of this $coll, + * going right to left: + * `op(x,,1,,, op(x,,2,,, ..., op(x,,n-1,,, x,,n,,)...))` where `x,,1,,, ..., x,,n,,` + * are the elements of this $coll. + * @throws UnsupportedOperationException if this $coll is empty. + */ def reduceRight[B >: A](op: (A, B) => B): B = this match { - case seq: IndexedSeq[A @unchecked] if seq.length > 0 => foldr(seq, op) - case _ => - val it = iterator - if (it.isEmpty) - throw new UnsupportedOperationException("empty.reduceRight") - - reversed.reduceLeft[B]((x, y) => op(y, x)) + case seq: IndexedSeq[A @unchecked] if seq.length > 0 => foldr[A, B](seq, op) + case _ if knownSize == 0 => throw new UnsupportedOperationException("empty.reduceRight") + case _ => reversed.reduceLeft[B]((x, y) => op(y, x)) // reduceLeftIterator } /** Optionally applies a binary operator to all elements of this $coll, going left to right. - * $willNotTerminateInf - * $orderDependentFold - * - * @param op the binary operator. - * @tparam B the result type of the binary operator. - * @return an option value containing the result of `reduceLeft(op)` if this $coll is nonempty, - * `None` otherwise. - */ - def reduceLeftOption[B >: A](op: (B, A) => B): Option[B] = if (isEmpty) None else Some(reduceLeft(op)) + * $willNotTerminateInf + * $orderDependentFold + * + * @param op the binary operator. + * @tparam B the result type of the binary operator. + * @return an option value containing the result of `reduceLeft(op)` if this $coll is nonempty, + * `None` otherwise. + */ + def reduceLeftOption[B >: A](op: (B, A) => B): Option[B] = + knownSize match { + case -1 => reduceLeftOptionIterator[B](op) + case 0 => None + case _ => Some(reduceLeft(op)) + } + private final def reduceLeftOptionIterator[B >: A](op: (B, A) => B): Option[B] = reduceOptionIterator[A, B](iterator)(op) + private final def reduceOptionIterator[X >: A, B >: X](it: Iterator[X])(op: (B, X) => B): Option[B] = { + if (it.hasNext) { + var acc: B = it.next() + while (it.hasNext) + acc = op(acc, it.next()) + Some(acc) + } + else None + } /** Optionally applies a binary operator to all elements of this $coll, going - * right to left. - * $willNotTerminateInf - * $orderDependentFold - * - * @param op the binary operator. - * @tparam B the result type of the binary operator. - * @return an option value containing the result of `reduceRight(op)` if this $coll is nonempty, - * `None` otherwise. - */ - def reduceRightOption[B >: A](op: (A, B) => B): Option[B] = if (isEmpty) None else Some(reduceRight(op)) + * right to left. + * $willNotTerminateInf + * $orderDependentFold + * + * @param op the binary operator. + * @tparam B the result type of the binary operator. + * @return an option value containing the result of `reduceRight(op)` if this $coll is nonempty, + * `None` otherwise. + */ + def reduceRightOption[B >: A](op: (A, B) => B): Option[B] = + knownSize match { + case -1 => reduceOptionIterator[A, B](reversed.iterator)((x, y) => op(y, x)) + case 0 => None + case _ => Some(reduceRight(op)) + } /** Tests whether the $coll is empty. - * - * Note: Implementations in subclasses that are not repeatedly iterable must take - * care not to consume any elements when `isEmpty` is called. - * - * @return `true` if the $coll contains no elements, `false` otherwise. - */ - def isEmpty: Boolean = !iterator.hasNext + * + * Note: The default implementation creates and discards an iterator. + * + * Note: Implementations in subclasses that are not repeatedly iterable must take + * care not to consume any elements when `isEmpty` is called. + * + * @return `true` if the $coll contains no elements, `false` otherwise. + */ + def isEmpty: Boolean = + knownSize match { + case -1 => !iterator.hasNext + case 0 => true + case _ => false + } /** Tests whether the $coll is not empty. - * - * @return `true` if the $coll contains at least one element, `false` otherwise. - */ + * + * @return `true` if the $coll contains at least one element, `false` otherwise. + */ @deprecatedOverriding("nonEmpty is defined as !isEmpty; override isEmpty instead", "2.13.0") def nonEmpty: Boolean = !isEmpty /** The size of this $coll. - * - * $willNotTerminateInf - * - * @return the number of elements in this $coll. - */ - def size: Int = { + * + * $willNotTerminateInf + * + * @return the number of elements in this $coll. + */ + def size: Int = if (knownSize >= 0) knownSize else { val it = iterator @@ -846,7 +866,6 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => while (it.hasNext) { len += 1; it.next() } len } - } @deprecated("Use `dest ++= coll` instead", "2.13.0") @inline final def copyToBuffer[B >: A](dest: mutable.Buffer[B]): Unit = dest ++= this @@ -868,37 +887,37 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => def copyToArray[B >: A](xs: Array[B]): Int = copyToArray(xs, 0, Int.MaxValue) /** Copy elements to an array, returning the number of elements written. - * - * Fills the given array `xs` starting at index `start` with values of this $coll. - * - * Copying will stop once either all the elements of this $coll have been copied, - * or the end of the array is reached. - * - * @param xs the array to fill. - * @param start the starting index of xs. - * @tparam B the type of the elements of the array. - * @return the number of elements written to the array - * - * @note Reuse: $consumesIterator - */ + * + * Fills the given array `xs` starting at index `start` with values of this $coll. + * + * Copying will stop once either all the elements of this $coll have been copied, + * or the end of the array is reached. + * + * @param xs the array to fill. + * @param start the starting index of xs. + * @tparam B the type of the elements of the array. + * @return the number of elements written to the array + * + * @note Reuse: $consumesIterator + */ @deprecatedOverriding("This should always forward to the 3-arg version of this method", since = "2.13.4") def copyToArray[B >: A](xs: Array[B], start: Int): Int = copyToArray(xs, start, Int.MaxValue) /** Copy elements to an array, returning the number of elements written. - * - * Fills the given array `xs` starting at index `start` with at most `len` elements of this $coll. - * - * Copying will stop once either all the elements of this $coll have been copied, - * or the end of the array is reached, or `len` elements have been copied. - * - * @param xs the array to fill. - * @param start the starting index of xs. - * @param len the maximal number of elements to copy. - * @tparam B the type of the elements of the array. - * @return the number of elements written to the array - * - * @note Reuse: $consumesIterator - */ + * + * Fills the given array `xs` starting at index `start` with at most `len` elements of this $coll. + * + * Copying will stop once either all the elements of this $coll have been copied, + * or the end of the array is reached, or `len` elements have been copied. + * + * @param xs the array to fill. + * @param start the starting index of xs. + * @param len the maximal number of elements to copy. + * @tparam B the type of the elements of the array. + * @return the number of elements written to the array + * + * @note Reuse: $consumesIterator + */ def copyToArray[B >: A](xs: Array[B], start: Int, len: Int): Int = { val it = iterator var i = start @@ -910,201 +929,208 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => i - start } - /** Sums up the elements of this collection. - * - * $willNotTerminateInf - * - * @param num an implicit parameter defining a set of numeric operations - * which includes the `+` operator to be used in forming the sum. - * @tparam B the result type of the `+` operator. - * @return the sum of all elements of this $coll with respect to the `+` operator in `num`. - */ - def sum[B >: A](implicit num: Numeric[B]): B = if (isEmpty) num.zero else reduce(num.plus) + /** Sums the elements of this collection. + * + * The default implementation uses `reduce` for a known non-empty collection, `foldLeft` otherwise. + * + * $willNotTerminateInf + * + * @param num an implicit parameter defining a set of numeric operations + * which includes the `+` operator to be used in forming the sum. + * @tparam B the result type of the `+` operator. + * @return the sum of all elements of this $coll with respect to the `+` operator in `num`. + */ + def sum[B >: A](implicit num: Numeric[B]): B = + knownSize match { + case -1 => foldLeft(num.zero)(num.plus) + case 0 => num.zero + case _ => reduce(num.plus) + } - /** Multiplies up the elements of this collection. - * - * $willNotTerminateInf - * - * @param num an implicit parameter defining a set of numeric operations - * which includes the `*` operator to be used in forming the product. - * @tparam B the result type of the `*` operator. - * @return the product of all elements of this $coll with respect to the `*` operator in `num`. - */ - def product[B >: A](implicit num: Numeric[B]): B = if (isEmpty) num.one else reduce(num.times) + /** Multiplies together the elements of this collection. + * + * The default implementation uses `reduce` for a known non-empty collection, `foldLeft` otherwise. + * + * $willNotTerminateInf + * + * @param num an implicit parameter defining a set of numeric operations + * which includes the `*` operator to be used in forming the product. + * @tparam B the result type of the `*` operator. + * @return the product of all elements of this $coll with respect to the `*` operator in `num`. + */ + def product[B >: A](implicit num: Numeric[B]): B = + knownSize match { + case -1 => foldLeft(num.one)(num.times) + case 0 => num.one + case _ => reduce(num.times) + } /** Finds the smallest element. - * - * $willNotTerminateInf - * - * @param ord An ordering to be used for comparing elements. - * @tparam B The type over which the ordering is defined. - * @throws UnsupportedOperationException if this $coll is empty. - * @return the smallest element of this $coll with respect to the ordering `ord`. - * - */ - def min[B >: A](implicit ord: Ordering[B]): A = { - if (isEmpty) - throw new UnsupportedOperationException("empty.min") - reduceLeft(ord.min) - } + * + * $willNotTerminateInf + * + * @param ord An ordering to be used for comparing elements. + * @tparam B The type over which the ordering is defined. + * @throws UnsupportedOperationException if this $coll is empty. + * @return the smallest element of this $coll with respect to the ordering `ord`. + * + */ + def min[B >: A](implicit ord: Ordering[B]): A = + knownSize match { + case -1 => reduceLeftIterator[A](throw new UnsupportedOperationException("empty.min"))(ord.min) + case 0 => throw new UnsupportedOperationException("empty.min") + case _ => reduceLeft(ord.min) + } /** Finds the smallest element. - * - * $willNotTerminateInf - * - * @param ord An ordering to be used for comparing elements. - * @tparam B The type over which the ordering is defined. - * @return an option value containing the smallest element of this $coll - * with respect to the ordering `ord`. - */ - def minOption[B >: A](implicit ord: Ordering[B]): Option[A] = { - if (isEmpty) - None - else - Some(min(ord)) - } + * + * $willNotTerminateInf + * + * @param ord An ordering to be used for comparing elements. + * @tparam B The type over which the ordering is defined. + * @return an option value containing the smallest element of this $coll + * with respect to the ordering `ord`. + */ + def minOption[B >: A](implicit ord: Ordering[B]): Option[A] = + knownSize match { + case -1 => reduceLeftOptionIterator[A](ord.min) + case 0 => None + case _ => Some(reduceLeft(ord.min)) + } /** Finds the largest element. - * - * $willNotTerminateInf - * - * @param ord An ordering to be used for comparing elements. - * @tparam B The type over which the ordering is defined. - * @throws UnsupportedOperationException if this $coll is empty. - * @return the largest element of this $coll with respect to the ordering `ord`. - */ - def max[B >: A](implicit ord: Ordering[B]): A = { - if (isEmpty) - throw new UnsupportedOperationException("empty.max") - reduceLeft(ord.max) - } + * + * $willNotTerminateInf + * + * @param ord An ordering to be used for comparing elements. + * @tparam B The type over which the ordering is defined. + * @throws UnsupportedOperationException if this $coll is empty. + * @return the largest element of this $coll with respect to the ordering `ord`. + */ + def max[B >: A](implicit ord: Ordering[B]): A = + knownSize match { + case -1 => reduceLeftIterator[A](throw new UnsupportedOperationException("empty.max"))(ord.max) + case 0 => throw new UnsupportedOperationException("empty.max") + case _ => reduceLeft(ord.max) + } /** Finds the largest element. - * - * $willNotTerminateInf - * - * @param ord An ordering to be used for comparing elements. - * @tparam B The type over which the ordering is defined. - * @return an option value containing the largest element of this $coll with - * respect to the ordering `ord`. - */ - def maxOption[B >: A](implicit ord: Ordering[B]): Option[A] = { - if (isEmpty) - None - else - Some(max(ord)) - } + * + * $willNotTerminateInf + * + * @param ord An ordering to be used for comparing elements. + * @tparam B The type over which the ordering is defined. + * @return an option value containing the largest element of this $coll with + * respect to the ordering `ord`. + */ + def maxOption[B >: A](implicit ord: Ordering[B]): Option[A] = + knownSize match { + case -1 => reduceLeftOptionIterator[A](ord.max) + case 0 => None + case _ => Some(reduceLeft(ord.max)) + } /** Finds the first element which yields the largest value measured by function f. - * - * $willNotTerminateInf - * - * @param cmp An ordering to be used for comparing elements. - * @tparam B The result type of the function f. - * @param f The measuring function. - * @throws UnsupportedOperationException if this $coll is empty. - * @return the first element of this $coll with the largest value measured by function f - * with respect to the ordering `cmp`. - */ - def maxBy[B](f: A => B)(implicit cmp: Ordering[B]): A = { - if (isEmpty) - throw new UnsupportedOperationException("empty.maxBy") - - class Maximizer extends runtime.AbstractFunction1[A, Unit] { - var maxF: B = null.asInstanceOf[B] - var maxElem: A = null.asInstanceOf[A] - var first = true - def apply(elem: A) = { - val fx = f(elem) - if (first && { first = false ; true } || cmp.gt(fx, maxF)) { - maxElem = elem - maxF = fx + * + * $willNotTerminateInf + * + * @param cmp An ordering to be used for comparing elements. + * @tparam B The result type of the function f. + * @param f The measuring function. + * @throws UnsupportedOperationException if this $coll is empty. + * @return the first element of this $coll with the largest value measured by function f + * with respect to the ordering `cmp`. + */ + def maxBy[B](f: A => B)(implicit ord: Ordering[B]): A = + knownSize match { + case 0 => throw new UnsupportedOperationException("empty.maxBy") + case _ => foldLeft(new Maximized[A, B]("maxBy")(f)(ord.gt))((m, a) => m(m, a)).result + } + + private class Maximized[X, B](descriptor: String)(f: X => B)(cmp: (B, B) => Boolean) extends AbstractFunction2[Maximized[X, B], X, Maximized[X, B]] { + var maxElem: X = null.asInstanceOf[X] + var maxF: B = null.asInstanceOf[B] + var nonEmpty = false + def toOption: Option[X] = if (nonEmpty) Some(maxElem) else None + def result: X = if (nonEmpty) maxElem else throw new UnsupportedOperationException(s"empty.$descriptor") + def apply(m: Maximized[X, B], a: X): Maximized[X, B] = + if (m.nonEmpty) { + val fa = f(a) + if (cmp(fa, maxF)) { + maxF = fa + maxElem = a } + m + } + else { + m.nonEmpty = true + m.maxElem = a + m.maxF = f(a) + m } - } - val maximizer = new Maximizer - foreach(maximizer) - maximizer.maxElem } /** Finds the first element which yields the largest value measured by function f. - * - * $willNotTerminateInf - * - * @param cmp An ordering to be used for comparing elements. - * @tparam B The result type of the function f. - * @param f The measuring function. - * @return an option value containing the first element of this $coll with the - * largest value measured by function f with respect to the ordering `cmp`. - */ - def maxByOption[B](f: A => B)(implicit cmp: Ordering[B]): Option[A] = { - if (isEmpty) - None - else - Some(maxBy(f)(cmp)) - } + * + * $willNotTerminateInf + * + * @param cmp An ordering to be used for comparing elements. + * @tparam B The result type of the function f. + * @param f The measuring function. + * @return an option value containing the first element of this $coll with the + * largest value measured by function f with respect to the ordering `cmp`. + */ + def maxByOption[B](f: A => B)(implicit ord: Ordering[B]): Option[A] = + knownSize match { + case 0 => None + case _ => foldLeft(new Maximized[A, B]("maxBy")(f)(ord.gt))((m, a) => m(m, a)).toOption + } /** Finds the first element which yields the smallest value measured by function f. - * - * $willNotTerminateInf - * - * @param cmp An ordering to be used for comparing elements. - * @tparam B The result type of the function f. - * @param f The measuring function. - * @throws UnsupportedOperationException if this $coll is empty. - * @return the first element of this $coll with the smallest value measured by function f - * with respect to the ordering `cmp`. - */ - def minBy[B](f: A => B)(implicit cmp: Ordering[B]): A = { - if (isEmpty) - throw new UnsupportedOperationException("empty.minBy") - - class Minimizer extends runtime.AbstractFunction1[A, Unit] { - var minF: B = null.asInstanceOf[B] - var minElem: A = null.asInstanceOf[A] - var first = true - def apply(elem: A) = { - val fx = f(elem) - if (first && { first = false ; true } || cmp.lt(fx, minF)) { - minElem = elem - minF = fx - } - } + * + * $willNotTerminateInf + * + * @param cmp An ordering to be used for comparing elements. + * @tparam B The result type of the function f. + * @param f The measuring function. + * @throws UnsupportedOperationException if this $coll is empty. + * @return the first element of this $coll with the smallest value measured by function f + * with respect to the ordering `cmp`. + */ + def minBy[B](f: A => B)(implicit ord: Ordering[B]): A = + knownSize match { + case 0 => throw new UnsupportedOperationException("empty.minBy") + case _ => foldLeft(new Maximized[A, B]("minBy")(f)(ord.lt))((m, a) => m(m, a)).result } - val minimizer = new Minimizer - foreach(minimizer) - minimizer.minElem - } /** Finds the first element which yields the smallest value measured by function f. - * - * $willNotTerminateInf - * - * @param cmp An ordering to be used for comparing elements. - * @tparam B The result type of the function f. - * @param f The measuring function. - * @return an option value containing the first element of this $coll - * with the smallest value measured by function f - * with respect to the ordering `cmp`. - */ - def minByOption[B](f: A => B)(implicit cmp: Ordering[B]): Option[A] = { - if (isEmpty) - None - else - Some(minBy(f)(cmp)) - } + * + * $willNotTerminateInf + * + * @param cmp An ordering to be used for comparing elements. + * @tparam B The result type of the function f. + * @param f The measuring function. + * @return an option value containing the first element of this $coll + * with the smallest value measured by function f + * with respect to the ordering `cmp`. + */ + def minByOption[B](f: A => B)(implicit ord: Ordering[B]): Option[A] = + knownSize match { + case 0 => None + case _ => foldLeft(new Maximized[A, B]("minBy")(f)(ord.lt))((m, a) => m(m, a)).toOption + } /** Finds the first element of the $coll for which the given partial - * function is defined, and applies the partial function to it. - * - * $mayNotTerminateInf - * $orderDependent - * - * @param pf the partial function - * @return an option value containing pf applied to the first - * value for which it is defined, or `None` if none exists. - * @example `Seq("a", 1, 5L).collectFirst({ case x: Int => x*10 }) = Some(10)` - */ + * function is defined, and applies the partial function to it. + * + * $mayNotTerminateInf + * $orderDependent + * + * @param pf the partial function + * @return an option value containing pf applied to the first + * value for which it is defined, or `None` if none exists. + * @example `Seq("a", 1, 5L).collectFirst({ case x: Int => x*10 }) = Some(10)` + */ def collectFirst[B](pf: PartialFunction[A, B]): Option[B] = { // Presumably the fastest way to get in and out of a partial function is for a sentinel function to return itself // (Tested to be lower-overhead than runWith. Would be better yet to not need to (formally) allocate it) @@ -1145,74 +1171,73 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => a.hasNext == b.hasNext } - /** Displays all elements of this $coll in a string using start, end, and - * separator strings. - * - * Delegates to addString, which can be overridden. - * - * @param start the starting string. - * @param sep the separator string. - * @param end the ending string. - * @return a string representation of this $coll. The resulting string - * begins with the string `start` and ends with the string - * `end`. Inside, the string representations (w.r.t. the method - * `toString`) of all elements of this $coll are separated by - * the string `sep`. - * - * @example `List(1, 2, 3).mkString("(", "; ", ")") = "(1; 2; 3)"` - */ + /** Displays all elements of this $coll in a string using start, end, and separator strings. + * + * Delegates to addString, which can be overridden. + * + * @param start the starting string. + * @param sep the separator string. + * @param end the ending string. + * @return a string representation of this $coll. The resulting string + * begins with the string `start` and ends with the string + * `end`. Inside, the string representations (w.r.t. the method + * `toString`) of all elements of this $coll are separated by + * the string `sep`. + * + * @example `List(1, 2, 3).mkString("(", "; ", ")") = "(1; 2; 3)"` + */ final def mkString(start: String, sep: String, end: String): String = - if (isEmpty) start + end + if (knownSize == 0) start + end else addString(new StringBuilder(), start, sep, end).result() /** Displays all elements of this $coll in a string using a separator string. - * - * Delegates to addString, which can be overridden. - * - * @param sep the separator string. - * @return a string representation of this $coll. In the resulting string - * the string representations (w.r.t. the method `toString`) - * of all elements of this $coll are separated by the string `sep`. - * - * @example `List(1, 2, 3).mkString("|") = "1|2|3"` - */ + * + * Delegates to addString, which can be overridden. + * + * @param sep the separator string. + * @return a string representation of this $coll. In the resulting string + * the string representations (w.r.t. the method `toString`) + * of all elements of this $coll are separated by the string `sep`. + * + * @example `List(1, 2, 3).mkString("|") = "1|2|3"` + */ @inline final def mkString(sep: String): String = mkString("", sep, "") /** Displays all elements of this $coll in a string. - * - * Delegates to addString, which can be overridden. - * - * @return a string representation of this $coll. In the resulting string - * the string representations (w.r.t. the method `toString`) - * of all elements of this $coll follow each other without any - * separator string. - */ + * + * Delegates to addString, which can be overridden. + * + * @return a string representation of this $coll. In the resulting string + * the string representations (w.r.t. the method `toString`) + * of all elements of this $coll follow each other without any + * separator string. + */ @inline final def mkString: String = mkString("") /** Appends all elements of this $coll to a string builder using start, end, and separator strings. - * The written text begins with the string `start` and ends with the string `end`. - * Inside, the string representations (w.r.t. the method `toString`) - * of all elements of this $coll are separated by the string `sep`. - * - * Example: - * - * {{{ - * scala> val a = List(1,2,3,4) - * a: List[Int] = List(1, 2, 3, 4) - * - * scala> val b = new StringBuilder() - * b: StringBuilder = - * - * scala> a.addString(b , "List(" , ", " , ")") - * res5: StringBuilder = List(1, 2, 3, 4) - * }}} - * - * @param b the string builder to which elements are appended. - * @param start the starting string. - * @param sep the separator string. - * @param end the ending string. - * @return the string builder `b` to which elements were appended. - */ + * The written text begins with the string `start` and ends with the string `end`. + * Inside, the string representations (w.r.t. the method `toString`) + * of all elements of this $coll are separated by the string `sep`. + * + * Example: + * + * {{{ + * scala> val a = List(1,2,3,4) + * a: List[Int] = List(1, 2, 3, 4) + * + * scala> val b = new StringBuilder() + * b: StringBuilder = + * + * scala> a.addString(b , "List(" , ", " , ")") + * res5: StringBuilder = List(1, 2, 3, 4) + * }}} + * + * @param b the string builder to which elements are appended. + * @param start the starting string. + * @param sep the separator string. + * @param end the ending string. + * @return the string builder `b` to which elements were appended. + */ def addString(b: StringBuilder, start: String, sep: String, end: String): b.type = { val jsb = b.underlying if (start.length != 0) jsb.append(start) @@ -1229,57 +1254,59 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => } /** Appends all elements of this $coll to a string builder using a separator string. - * The written text consists of the string representations (w.r.t. the method `toString`) - * of all elements of this $coll, separated by the string `sep`. - * - * Example: - * - * {{{ - * scala> val a = List(1,2,3,4) - * a: List[Int] = List(1, 2, 3, 4) - * - * scala> val b = new StringBuilder() - * b: StringBuilder = - * - * scala> a.addString(b, ", ") - * res0: StringBuilder = 1, 2, 3, 4 - * }}} - * - * @param b the string builder to which elements are appended. - * @param sep the separator string. - * @return the string builder `b` to which elements were appended. - */ + * The written text consists of the string representations (w.r.t. the method `toString`) + * of all elements of this $coll, separated by the string `sep`. + * + * Example: + * + * {{{ + * scala> val a = List(1,2,3,4) + * a: List[Int] = List(1, 2, 3, 4) + * + * scala> val b = new StringBuilder() + * b: StringBuilder = + * + * scala> a.addString(b, ", ") + * res0: StringBuilder = 1, 2, 3, 4 + * }}} + * + * @param b the string builder to which elements are appended. + * @param sep the separator string. + * @return the string builder `b` to which elements were appended. + */ @inline final def addString(b: StringBuilder, sep: String): b.type = addString(b, "", sep, "") /** Appends all elements of this $coll to a string builder. - * The written text consists of the string representations (w.r.t. the method - * `toString`) of all elements of this $coll without any separator string. - * - * Example: - * - * {{{ - * scala> val a = List(1,2,3,4) - * a: List[Int] = List(1, 2, 3, 4) - * - * scala> val b = new StringBuilder() - * b: StringBuilder = - * - * scala> val h = a.addString(b) - * h: StringBuilder = 1234 - * }}} - - * @param b the string builder to which elements are appended. - * @return the string builder `b` to which elements were appended. - */ + * The written text consists of the string representations (w.r.t. the method + * `toString`) of all elements of this $coll without any separator string. + * + * Example: + * + * {{{ + * scala> val a = List(1,2,3,4) + * a: List[Int] = List(1, 2, 3, 4) + * + * scala> val b = new StringBuilder() + * b: StringBuilder = + * + * scala> val h = a.addString(b) + * h: StringBuilder = 1234 + * }}} + * + * @param b the string builder to which elements are appended. + * @return the string builder `b` to which elements were appended. + */ @inline final def addString(b: StringBuilder): b.type = addString(b, "") /** Given a collection factory `factory`, convert this collection to the appropriate - * representation for the current element type `A`. Example uses: - * - * xs.to(List) - * xs.to(ArrayBuffer) - * xs.to(BitSet) // for xs: Iterable[Int] - */ + * representation for the current element type `A`. Example uses: + * + * {{{ + * xs.to(List) + * xs.to(ArrayBuffer) + * xs.to(BitSet) // for xs: Iterable[Int] + * }}} + */ def to[C1](factory: Factory[A, C1]): C1 = factory.fromSpecific(this) @deprecated("Use .iterator instead of .toIterator", "2.13.0") @@ -1294,9 +1321,8 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => def toSet[B >: A]: immutable.Set[B] = immutable.Set.from(this) - /** - * @return This collection as a `Seq[A]`. This is equivalent to `to(Seq)` but might be faster. - */ + /** @return This collection as a `Seq[A]`. This is equivalent to `to(Seq)` but might be faster. + */ def toSeq: immutable.Seq[A] = immutable.Seq.from(this) def toIndexedSeq: immutable.IndexedSeq[A] = immutable.IndexedSeq.from(this) diff --git a/src/library/scala/collection/Seq.scala b/src/library/scala/collection/Seq.scala index 36a54a48b94a..d960838fdcb7 100644 --- a/src/library/scala/collection/Seq.scala +++ b/src/library/scala/collection/Seq.scala @@ -735,22 +735,23 @@ trait SeqOps[+A, +CC[_], +C] extends Any } /** Sorts this $coll according to a comparison function. - * $willNotTerminateInf - * $willForceEvaluation - * - * The sort is stable. That is, elements that are equal (as determined by - * `lt`) appear in the same order in the sorted sequence as in the original. - * - * @param lt the comparison function which tests whether - * its first argument precedes its second argument in - * the desired ordering. - * @return a $coll consisting of the elements of this $coll - * sorted according to the comparison function `lt`. - * @example {{{ - * List("Steve", "Tom", "John", "Bob").sortWith(_.compareTo(_) < 0) = - * List("Bob", "John", "Steve", "Tom") - * }}} - */ + * $willNotTerminateInf + * $willForceEvaluation + * + * The sort is stable. That is, elements that are equal + * (`lt` returns false for both directions of comparison) + * appear in the same order in the sorted sequence as in the original. + * + * @param lt a predicate that is true if + * its first argument strictly precedes its second argument in + * the desired ordering. + * @return a $coll consisting of the elements of this $coll + * sorted according to the comparison function `lt`. + * @example {{{ + * List("Steve", "Bobby", "Tom", "John", "Bob").sortWith((x, y) => x.take(3).compareTo(y.take(3)) < 0) = + * List("Bobby", "Bob", "John", "Steve", "Tom") + * }}} + */ def sortWith(lt: (A, A) => Boolean): C = sorted(Ordering.fromLessThan(lt)) /** Sorts this $coll according to the Ordering which results from transforming diff --git a/src/library/scala/collection/StrictOptimizedMapOps.scala b/src/library/scala/collection/StrictOptimizedMapOps.scala index 02cf26b490f6..1f5791bbb718 100644 --- a/src/library/scala/collection/StrictOptimizedMapOps.scala +++ b/src/library/scala/collection/StrictOptimizedMapOps.scala @@ -38,7 +38,11 @@ trait StrictOptimizedMapOps[K, +V, +CC[_, _] <: IterableOps[_, AnyConstr, _], +C @deprecated("Use ++ with an explicit collection argument instead of + with varargs", "2.13.0") override def + [V1 >: V](elem1: (K, V1), elem2: (K, V1), elems: (K, V1)*): CC[K, V1] = { - val m = ((this + elem1).asInstanceOf[Map[K, V]] + elem2).asInstanceOf[CC[K, V1]] - if(elems.isEmpty) m else m.concat(elems).asInstanceOf[CC[K, V1]] + val b = mapFactory.newBuilder[K, V1] + b ++= this + b += elem1 + b += elem2 + if (elems.nonEmpty) b ++= elems + b.result() } } diff --git a/src/library/scala/collection/StringOps.scala b/src/library/scala/collection/StringOps.scala index ce47c765d534..f0be485af8ae 100644 --- a/src/library/scala/collection/StringOps.scala +++ b/src/library/scala/collection/StringOps.scala @@ -33,21 +33,23 @@ object StringOps { private class StringIterator(private[this] val s: String) extends AbstractIterator[Char] { private[this] var pos = 0 def hasNext: Boolean = pos < s.length - def next(): Char = try { + def next(): Char = { + if (pos >= s.length) Iterator.empty.next() val r = s.charAt(pos) pos += 1 r - } catch { case _: IndexOutOfBoundsException => Iterator.empty.next() } + } } private class ReverseIterator(private[this] val s: String) extends AbstractIterator[Char] { private[this] var pos = s.length-1 def hasNext: Boolean = pos >= 0 - def next(): Char = try { + def next(): Char = { + if (pos < 0) Iterator.empty.next() val r = s.charAt(pos) pos -= 1 r - } catch { case _: IndexOutOfBoundsException => Iterator.empty.next() } + } } private class GroupedIterator(s: String, groupSize: Int) extends AbstractIterator[String] { diff --git a/src/library/scala/collection/concurrent/Map.scala b/src/library/scala/collection/concurrent/Map.scala index 2fd444035bf5..c2b996b93102 100644 --- a/src/library/scala/collection/concurrent/Map.scala +++ b/src/library/scala/collection/concurrent/Map.scala @@ -19,7 +19,7 @@ import scala.annotation.tailrec * * $concurrentmapinfo * - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-mutable-collection-classes.html#concurrent_maps "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-mutable-collection-classes.html#concurrent_maps "Scala's Collection Library overview"]] * section on `Concurrent Maps` for more information. * * @tparam K the key type of the map diff --git a/src/library/scala/collection/convert/JavaCollectionWrappers.scala b/src/library/scala/collection/convert/JavaCollectionWrappers.scala index 49d6596b1a44..29c3dcbac5db 100644 --- a/src/library/scala/collection/convert/JavaCollectionWrappers.scala +++ b/src/library/scala/collection/convert/JavaCollectionWrappers.scala @@ -56,7 +56,15 @@ private[collection] object JavaCollectionWrappers extends Serializable { } @SerialVersionUID(3L) - class IterableWrapper[A](val underlying: Iterable[A]) extends ju.AbstractCollection[A] with IterableWrapperTrait[A] with Serializable + class IterableWrapper[A](val underlying: Iterable[A]) extends ju.AbstractCollection[A] with IterableWrapperTrait[A] with Serializable { + import scala.runtime.Statics._ + override def equals(other: Any): Boolean = + other match { + case other: IterableWrapper[_] => underlying.equals(other.underlying) + case _ => false + } + override def hashCode = finalizeHash(mix(mix(0xcafebabe, "IterableWrapper".hashCode), anyHash(underlying)), 1) + } @SerialVersionUID(3L) class JIterableWrapper[A](val underlying: jl.Iterable[A]) diff --git a/src/library/scala/collection/immutable/BitSet.scala b/src/library/scala/collection/immutable/BitSet.scala index 4e8c4ee1a244..9461264850a9 100644 --- a/src/library/scala/collection/immutable/BitSet.scala +++ b/src/library/scala/collection/immutable/BitSet.scala @@ -20,7 +20,7 @@ import scala.annotation.{implicitNotFound, nowarn} /** A class for immutable bitsets. * $bitsetinfo - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-immutable-collection-classes.html#immutable-bitsets "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-immutable-collection-classes.html#immutable-bitsets "Scala's Collection Library overview"]] * section on `Immutable BitSets` for more information. * * @define Coll `immutable.BitSet` @@ -244,36 +244,45 @@ object BitSet extends SpecificIterableFactory[Int, BitSet] { anyChanges ||= currentWord != oldWord i -= 1 } - if (i < 0) { - // all indices >= 0 have had result 0, so the bitset is empty - this.empty - } else { - val minimumNonZeroIndex: Int = i + 1 - while (!anyChanges && i >= 0) { - val oldWord = word(i) - currentWord = oldWord & ~bs.word(i) - anyChanges ||= currentWord != oldWord - i -= 1 - } - if (anyChanges) { - if (minimumNonZeroIndex == -1) { - this.empty - } else if (minimumNonZeroIndex == 0) { - new BitSet1(currentWord) - } else if (minimumNonZeroIndex == 1) { - new BitSet2(word(0) & ~bs.word(0), currentWord) + i match { + case -1 => + if (anyChanges) { + if (currentWord == 0) { + this.empty + } else { + new BitSet1(currentWord) + } } else { + this + } + case 0 => + val oldFirstWord = word(0) + val firstWord = oldFirstWord & ~bs.word(0) + anyChanges ||= firstWord != oldFirstWord + if (anyChanges) { + new BitSet2(firstWord, currentWord) + } else { + this + } + case _ => + val minimumNonZeroIndex: Int = i + 1 + while (!anyChanges && i >= 0) { + val oldWord = word(i) + currentWord = oldWord & ~bs.word(i) + anyChanges ||= currentWord != oldWord + i -= 1 + } + if (anyChanges) { val newArray = elems.take(minimumNonZeroIndex + 1) newArray(i + 1) = currentWord while (i >= 0) { newArray(i) = word(i) & ~bs.word(i) i -= 1 } - this.fromBitMaskNoCopy(newArray) + new BitSetN(newArray) + } else { + this } - } else { - this - } } } else { var i = bsnwords - 1 @@ -314,36 +323,45 @@ object BitSet extends SpecificIterableFactory[Int, BitSet] { anyChanges ||= currentWord != oldWord i -= 1 } - if (i < 0) { - // all indices >= 0 have had result 0, so the bitset is empty - if (currentWord == 0) this.empty else this.fromBitMaskNoCopy(Array(currentWord)) - } else { - val minimumNonZeroIndex: Int = i + 1 - while (!anyChanges && i >= 0) { - val oldWord = word(i) - currentWord = BitSetOps.computeWordForFilter(pred, isFlipped, oldWord, i) - anyChanges ||= currentWord != oldWord - i -= 1 - } - if (anyChanges) { - if (minimumNonZeroIndex == -1) { - this.empty - } else if (minimumNonZeroIndex == 0) { - new BitSet1(currentWord) - } else if (minimumNonZeroIndex == 1) { - new BitSet2(BitSetOps.computeWordForFilter(pred, isFlipped, word(0), 0), currentWord) + i match { + case -1 => + if (anyChanges) { + if (currentWord == 0) { + this.empty + } else { + new BitSet1(currentWord) + } } else { + this + } + case 0 => + val oldFirstWord = word(0) + val firstWord = BitSetOps.computeWordForFilter(pred, isFlipped, oldFirstWord, 0) + anyChanges ||= firstWord != oldFirstWord + if (anyChanges) { + new BitSet2(firstWord, currentWord) + } else { + this + } + case _ => + val minimumNonZeroIndex: Int = i + 1 + while (!anyChanges && i >= 0) { + val oldWord = word(i) + currentWord = BitSetOps.computeWordForFilter(pred, isFlipped, oldWord, i) + anyChanges ||= currentWord != oldWord + i -= 1 + } + if (anyChanges) { val newArray = elems.take(minimumNonZeroIndex + 1) newArray(i + 1) = currentWord while (i >= 0) { newArray(i) = BitSetOps.computeWordForFilter(pred, isFlipped, word(i), i) i -= 1 } - this.fromBitMaskNoCopy(newArray) + new BitSetN(newArray) + } else { + this } - } else { - this - } } } diff --git a/src/library/scala/collection/immutable/HashMap.scala b/src/library/scala/collection/immutable/HashMap.scala index 7a9231231d32..2e8378c4d810 100644 --- a/src/library/scala/collection/immutable/HashMap.scala +++ b/src/library/scala/collection/immutable/HashMap.scala @@ -190,6 +190,27 @@ final class HashMap[K, +V] private[immutable] (private[immutable] val rootNode: } } this + case lhm: mutable.LinkedHashMap[K @unchecked, V @unchecked] => + val iter = lhm.entryIterator + var current = rootNode + while (iter.hasNext) { + val next = iter.next() + val originalHash = lhm.unimproveHash(next.hash) + val improved = improve(originalHash) + current = current.updated(next.key, next.value, originalHash, improved, 0, replaceValue = true) + + if (current ne rootNode) { + var shallowlyMutableNodeMap = Node.bitposFrom(Node.maskFrom(improved, 0)) + + while (iter.hasNext) { + val next = iter.next() + val originalHash = lhm.unimproveHash(next.hash) + shallowlyMutableNodeMap = current.updateWithShallowMutations(next.key, next.value, originalHash, improve(originalHash), 0, shallowlyMutableNodeMap) + } + return new HashMap(current) + } + } + this case _ => class accum extends AbstractFunction2[K, V1, Unit] with Function1[(K, V1), Unit] { var changed = false @@ -397,6 +418,24 @@ final class HashMap[K, +V] private[immutable] (private[immutable] val rootNode: } newHashMapOrThis(curr) } + case lhashSet: collection.mutable.LinkedHashSet[K] => + if (lhashSet.isEmpty) { + this + } else { + val iter = lhashSet.entryIterator + var curr = rootNode + + while (iter.hasNext) { + val next = iter.next() + val originalHash = lhashSet.unimproveHash(next.hash) + val improved = improve(originalHash) + curr = curr.removed(next.key, originalHash, improved, 0) + if (curr.size == 0) { + return HashMap.empty + } + } + newHashMapOrThis(curr) + } case _ => val iter = keys.iterator var curr = rootNode @@ -2353,6 +2392,14 @@ private[immutable] final class HashMapBuilder[K, V] extends ReusableBuilder[(K, val hash = improve(originalHash) update(rootNode, next.key, next.value, originalHash, hash, 0) } + case lhm: collection.mutable.LinkedHashMap[K, V] => + val iter = lhm.entryIterator + while (iter.hasNext) { + val next = iter.next() + val originalHash = lhm.unimproveHash(next.hash) + val hash = improve(originalHash) + update(rootNode, next.key, next.value, originalHash, hash, 0) + } case thatMap: Map[K, V] => thatMap.foreachEntry((key, value) => addOne(key, value)) case other => diff --git a/src/library/scala/collection/immutable/HashSet.scala b/src/library/scala/collection/immutable/HashSet.scala index 1785ceb2c0ea..459fcf1682aa 100644 --- a/src/library/scala/collection/immutable/HashSet.scala +++ b/src/library/scala/collection/immutable/HashSet.scala @@ -121,6 +121,27 @@ final class HashSet[A] private[immutable](private[immutable] val rootNode: Bitma } } this + case lhs: collection.mutable.LinkedHashSet[A] => + val iter = lhs.entryIterator + var current = rootNode + while (iter.hasNext) { + val next = iter.next() + val originalHash = lhs.unimproveHash(next.hash) + val improved = improve(originalHash) + current = current.updated(next.key, originalHash, improved, 0) + + if (current ne rootNode) { + var shallowlyMutableNodeMap = Node.bitposFrom(Node.maskFrom(improved, 0)) + while (iter.hasNext) { + val next = iter.next() + val originalHash = lhs.unimproveHash(next.hash) + val improved = improve(originalHash) + shallowlyMutableNodeMap = current.updateWithShallowMutations(next.key, originalHash, improved, 0, shallowlyMutableNodeMap) + } + return new HashSet(current) + } + } + this case _ => val iter = that.iterator var current = rootNode diff --git a/src/library/scala/collection/immutable/LazyList.scala b/src/library/scala/collection/immutable/LazyList.scala index d9d52e8da44b..8b7ad26dc5ae 100644 --- a/src/library/scala/collection/immutable/LazyList.scala +++ b/src/library/scala/collection/immutable/LazyList.scala @@ -220,7 +220,7 @@ import scala.runtime.Statics * * @tparam A the type of the elements contained in this lazy list. * - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-immutable-collection-classes.html#lazylists "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-immutable-collection-classes.html#lazylists "Scala's Collection Library overview"]] * section on `LazyLists` for more information. * @define Coll `LazyList` * @define coll lazy list diff --git a/src/library/scala/collection/immutable/List.scala b/src/library/scala/collection/immutable/List.scala index dc117a0bdb72..5358922752fb 100644 --- a/src/library/scala/collection/immutable/List.scala +++ b/src/library/scala/collection/immutable/List.scala @@ -65,7 +65,7 @@ import scala.runtime.Statics.releaseFence * objects that rely on structural sharing), will be serialized and deserialized with multiple lists, one for * each reference to it. I.e. structural sharing is lost after serialization/deserialization. * - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-immutable-collection-classes.html#lists "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-immutable-collection-classes.html#lists "Scala's Collection Library overview"]] * section on `Lists` for more information. * * @define coll list diff --git a/src/library/scala/collection/immutable/ListMap.scala b/src/library/scala/collection/immutable/ListMap.scala index 68496f445bc3..4a2b8dbd807c 100644 --- a/src/library/scala/collection/immutable/ListMap.scala +++ b/src/library/scala/collection/immutable/ListMap.scala @@ -119,7 +119,7 @@ sealed class ListMap[K, +V] * n elements will take O(n^2^) time. This makes the builder suitable only for a small number of * elements. * - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-immutable-collection-classes.html#list-maps "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-immutable-collection-classes.html#list-maps "Scala's Collection Library overview"]] * section on `List Maps` for more information. * @define Coll ListMap * @define coll list map diff --git a/src/library/scala/collection/immutable/NumericRange.scala b/src/library/scala/collection/immutable/NumericRange.scala index 728fe3acbf54..d1ee494711a7 100644 --- a/src/library/scala/collection/immutable/NumericRange.scala +++ b/src/library/scala/collection/immutable/NumericRange.scala @@ -132,6 +132,75 @@ sealed class NumericRange[T]( // are forgiving: therefore the checks are with the methods. private def locationAfterN(n: Int): T = start + (step * fromInt(n)) + private def crossesTheEndAfterN(n: Int): Boolean = { + // if we're sure that subtraction in the context of T won't overflow, we use this function + // to calculate the length of the range + def unsafeRangeLength(r: NumericRange[T]): T = { + val diff = num.minus(r.end, r.start) + val quotient = num.quot(diff, r.step) + val remainder = num.rem(diff, r.step) + if (!r.isInclusive && num.equiv(remainder, num.zero)) + num.max(quotient, num.zero) + else + num.max(num.plus(quotient, num.one), num.zero) + } + + // detects whether value can survive a bidirectional trip to -and then from- Int. + def fitsInInteger(value: T): Boolean = num.equiv(num.fromInt(num.toInt(value)), value) + + val stepIsInTheSameDirectionAsStartToEndVector = + (num.gt(end, start) && num.gt(step, num.zero)) || (num.lt(end, start) && num.sign(step) == -num.one) + + if (num.equiv(start, end) || n <= 0 || !stepIsInTheSameDirectionAsStartToEndVector) return n >= 1 + + val sameSign = num.equiv(num.sign(start), num.sign(end)) + + if (sameSign) { // subtraction is safe + val len = unsafeRangeLength(this) + if (fitsInInteger(len)) n >= num.toInt(len) else num.gteq(num.fromInt(n), len) + } else { + // split to two ranges, which subtraction is safe in both of them (around zero) + val stepsRemainderToZero = num.rem(start, step) + val walksOnZero = num.equiv(stepsRemainderToZero, num.zero) + val closestToZero = if (walksOnZero) -step else stepsRemainderToZero + + /* + When splitting into two ranges, we should be super-careful about one of the sides hitting MinValue of T, + so we take two steps smaller than zero to ensure unsafeRangeLength won't overflow (taking one step may overflow depending on the step). + Same thing happens for MaxValue from zero, so we take one step further to ensure the safety of unsafeRangeLength. + After performing such operation, there are some elements remaining in between and around zero, + which their length is represented by carry. + */ + val (l: NumericRange[T], r: NumericRange[T], carry: Int) = + if (num.lt(start, num.zero)) { + if (walksOnZero) { + val twoStepsAfterLargestNegativeNumber = num.plus(closestToZero, num.times(step, num.fromInt(2))) + (NumericRange(start, closestToZero, step), copy(twoStepsAfterLargestNegativeNumber, end, step), 2) + } else { + (NumericRange(start, closestToZero, step), copy(num.plus(closestToZero, step), end, step), 1) + } + } else { + if (walksOnZero) { + val twoStepsAfterZero = num.times(step, num.fromInt(2)) + (copy(twoStepsAfterZero, end, step), NumericRange.inclusive(start, -step, step), 2) + } else { + val twoStepsAfterSmallestPositiveNumber = num.plus(closestToZero, num.times(step, num.fromInt(2))) + (copy(twoStepsAfterSmallestPositiveNumber, end, step), NumericRange.inclusive(start, closestToZero, step), 2) + } + } + + val leftLength = unsafeRangeLength(l) + val rightLength = unsafeRangeLength(r) + + // instead of `n >= rightLength + leftLength + curry` which may cause addition overflow, + // this can be used `(n - leftLength - curry) >= rightLength` (Both in Int and T, depends on whether the lengths fit in Int) + if (fitsInInteger(leftLength) && fitsInInteger(rightLength)) + n - num.toInt(leftLength) - carry >= num.toInt(rightLength) + else + num.gteq(num.minus(num.minus(num.fromInt(n), leftLength), num.fromInt(carry)), rightLength) + } + } + // When one drops everything. Can't ever have unchecked operations // like "end + 1" or "end - 1" because ranges involving Int.{ MinValue, MaxValue } // will overflow. This creates an exclusive range where start == end @@ -140,13 +209,13 @@ sealed class NumericRange[T]( override def take(n: Int): NumericRange[T] = { if (n <= 0 || isEmpty) newEmptyRange(start) - else if (n >= length) this + else if (crossesTheEndAfterN(n)) this else new NumericRange.Inclusive(start, locationAfterN(n - 1), step) } override def drop(n: Int): NumericRange[T] = { if (n <= 0 || isEmpty) this - else if (n >= length) newEmptyRange(end) + else if (crossesTheEndAfterN(n)) newEmptyRange(end) else copy(locationAfterN(n), end, step) } diff --git a/src/library/scala/collection/immutable/Queue.scala b/src/library/scala/collection/immutable/Queue.scala index eb12f6fd8b14..3d0f8206b6a9 100644 --- a/src/library/scala/collection/immutable/Queue.scala +++ b/src/library/scala/collection/immutable/Queue.scala @@ -27,7 +27,7 @@ import scala.collection.mutable.{Builder, ListBuffer} * where a pivot is required, in which case, a cost of `O(n)` is incurred, where `n` is the number of elements in the queue. When this happens, * `n` remove operations with `O(1)` cost are guaranteed. Removing an item is on average `O(1)`. * - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-immutable-collection-classes.html#immutable-queues "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-immutable-collection-classes.html#immutable-queues "Scala's Collection Library overview"]] * section on `Immutable Queues` for more information. * * @define Coll `immutable.Queue` diff --git a/src/library/scala/collection/immutable/TreeMap.scala b/src/library/scala/collection/immutable/TreeMap.scala index a0f0e8692f97..a51c7b9e7bf6 100644 --- a/src/library/scala/collection/immutable/TreeMap.scala +++ b/src/library/scala/collection/immutable/TreeMap.scala @@ -60,7 +60,7 @@ import scala.runtime.AbstractFunction2 * @tparam V the type of the values associated with the keys. * @param ordering the implicit ordering used to compare objects of type `A`. * - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-immutable-collection-classes.html#red-black-trees "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-immutable-collection-classes.html#red-black-trees "Scala's Collection Library overview"]] * section on `Red-Black Trees` for more information. * * @define Coll immutable.TreeMap diff --git a/src/library/scala/collection/immutable/TreeSet.scala b/src/library/scala/collection/immutable/TreeSet.scala index e51479ae657b..f0be91b72acc 100644 --- a/src/library/scala/collection/immutable/TreeSet.scala +++ b/src/library/scala/collection/immutable/TreeSet.scala @@ -26,7 +26,7 @@ import scala.runtime.AbstractFunction1 * @tparam A the type of the elements contained in this tree set * @param ordering the implicit ordering used to compare objects of type `A` * - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-immutable-collection-classes.html#red-black-trees "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-immutable-collection-classes.html#red-black-trees "Scala's Collection Library overview"]] * section on `Red-Black Trees` for more information. * * @define Coll `immutable.TreeSet` diff --git a/src/library/scala/collection/immutable/Vector.scala b/src/library/scala/collection/immutable/Vector.scala index 9edbf2bc9b90..aa3fac5acd69 100644 --- a/src/library/scala/collection/immutable/Vector.scala +++ b/src/library/scala/collection/immutable/Vector.scala @@ -206,6 +206,7 @@ sealed abstract class Vector[+A] private[immutable] (private[immutable] final va } protected[this] def prependedAll0[B >: A](prefix: collection.IterableOnce[B], k: Int): Vector[B] = { + // k >= 0, k = prefix.knownSize val tinyAppendLimit = 4 + vectorSliceCount if (k < tinyAppendLimit /*|| k < (this.size >>> Log2ConcatFaster)*/) { var v: Vector[B] = this @@ -217,12 +218,15 @@ sealed abstract class Vector[+A] private[immutable] (private[immutable] final va val it = this.iterator while (it.hasNext) v = v :+ it.next() v + } else if (k < this.size - AlignToFaster) { + new VectorBuilder[B].alignTo(k, this).addAll(prefix).addAll(this).result() } else super.prependedAll(prefix) } protected[this] def appendedAll0[B >: A](suffix: collection.IterableOnce[B], k: Int): Vector[B] = { + // k >= 0, k = suffix.knownSize val tinyAppendLimit = 4 + vectorSliceCount - if(k > 0 && k < tinyAppendLimit) { + if (k < tinyAppendLimit) { var v: Vector[B] = this suffix match { case it: Iterable[_] => it.asInstanceOf[Iterable[B]].foreach(x => v = v.appended(x)) @@ -234,6 +238,9 @@ sealed abstract class Vector[+A] private[immutable] (private[immutable] final va val ri = this.reverseIterator while (ri.hasNext) v = v.prepended(ri.next()) v + } else if (this.size < k - AlignToFaster && suffix.isInstanceOf[Vector[_]]) { + val v = suffix.asInstanceOf[Vector[B]] + new VectorBuilder[B].alignTo(this.size, v).addAll(this).addAll(v).result() } else new VectorBuilder[B].initFrom(this).addAll(suffix).result() } @@ -289,11 +296,11 @@ sealed abstract class Vector[+A] private[immutable] (private[immutable] final va override final def foreach[U](f: A => U): Unit = { val c = vectorSliceCount - var i = 0 - while(i < c) { - foreachRec(vectorSliceDim(c, i)-1, vectorSlice(i), f) - i += 1 - } + var i = 0 + while (i < c) { + foreachRec(vectorSliceDim(c, i) - 1, vectorSlice(i), f) + i += 1 + } } // The following definitions are needed for binary compatibility with ParVector @@ -310,9 +317,8 @@ private sealed abstract class VectorImpl[+A](_prefix1: Arr1) extends Vector[A](_ override final def slice(from: Int, until: Int): Vector[A] = { val lo = mmax(from, 0) val hi = mmin(until, length) - val newlen = hi - lo - if(newlen == length) this - else if(hi <= lo) Vector0 + if (hi <= lo) Vector0 + else if (hi - lo == length) this else slice0(lo, hi) } } @@ -1394,6 +1400,7 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { private[this] var a2: Arr2 = _ private[this] var a1: Arr1 = new Arr1(WIDTH) private[this] var len1, lenRest, offset = 0 + private[this] var prefixIsRightAligned = false private[this] var depth = 1 @inline private[this] final def setLen(i: Int): Unit = { @@ -1417,6 +1424,7 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { len1 = 0 lenRest = 0 offset = 0 + prefixIsRightAligned = false depth = 1 } @@ -1538,7 +1546,7 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { depth = 6 offset = WIDTH5 - v6.len12345 setLen(v6.length0 + offset) - a6 = new Arr6(WIDTH) + a6 = new Arr6(LASTWIDTH) a6(0) = copyPrepend(copyPrepend(copyPrepend(copyPrepend(v6.prefix1, v6.prefix2), v6.prefix3), v6.prefix4), v6.prefix5) System.arraycopy(d6, 0, a6, 1, d6.length) a5 = copyOf(s5, WIDTH) @@ -1559,6 +1567,134 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { this } + //TODO Make public; this method is only private for binary compatibility + private[collection] def alignTo(before: Int, bigVector: Vector[A]): this.type = { + if (len1 != 0 || lenRest != 0) + throw new UnsupportedOperationException("A non-empty VectorBuilder cannot be aligned retrospectively. Please call .reset() or use a new VectorBuilder.") + val (prefixLength, maxPrefixLength) = bigVector match { + case Vector0 => (0, 1) + case v1: Vector1[_] => (0, 1) + case v2: Vector2[_] => (v2.len1, WIDTH) + case v3: Vector3[_] => (v3.len12, WIDTH2) + case v4: Vector4[_] => (v4.len123, WIDTH3) + case v5: Vector5[_] => (v5.len1234, WIDTH4) + case v6: Vector6[_] => (v6.len12345, WIDTH5) + } + if (maxPrefixLength == 1) return this // does not really make sense to align for <= 32 element-vector + val overallPrefixLength = (before + prefixLength) % maxPrefixLength + offset = (maxPrefixLength - overallPrefixLength) % maxPrefixLength + // pretend there are already `offset` elements added + advanceN(offset & ~MASK) + len1 = offset & MASK + prefixIsRightAligned = true + this + } + + /** + * Removes `offset` leading `null`s in the prefix. + * This is needed after calling `alignTo` and subsequent additions, + * directly before the result is used for creating a new Vector. + * Note that the outermost array keeps its length to keep the + * Builder re-usable. + * + * example: + * a2 = Array(null, ..., null, Array(null, .., null, 0, 1, .., x), Array(x+1, .., x+32), ...) + * becomes + * a2 = Array(Array(0, 1, .., x), Array(x+1, .., x+32), ..., ?, ..., ?) + */ + private[this] def leftAlignPrefix(): Unit = { + @inline def shrinkOffsetIfToLarge(width: Int): Unit = { + val newOffset = offset % width + lenRest -= offset - newOffset + offset = newOffset + } + var a: Array[AnyRef] = null // the array we modify + var aParent: Array[AnyRef] = null // a's parent, so aParent(0) == a + if (depth >= 6) { + a = a6.asInstanceOf[Array[AnyRef]] + val i = offset >>> BITS5 + if (i > 0) System.arraycopy(a, i, a, 0, LASTWIDTH - i) + shrinkOffsetIfToLarge(WIDTH5) + if ((lenRest >>> BITS5) == 0) depth = 5 + aParent = a + a = a(0).asInstanceOf[Array[AnyRef]] + } + if (depth >= 5) { + if (a == null) a = a5.asInstanceOf[Array[AnyRef]] + val i = (offset >>> BITS4) & MASK + if (depth == 5) { + if (i > 0) System.arraycopy(a, i, a, 0, WIDTH - i) + a5 = a.asInstanceOf[Arr5] + shrinkOffsetIfToLarge(WIDTH4) + if ((lenRest >>> BITS4) == 0) depth = 4 + } else { + if (i > 0) a = copyOfRange(a, i, WIDTH) + aParent(0) = a + } + aParent = a + a = a(0).asInstanceOf[Array[AnyRef]] + } + if (depth >= 4) { + if (a == null) a = a4.asInstanceOf[Array[AnyRef]] + val i = (offset >>> BITS3) & MASK + if (depth == 4) { + if (i > 0) System.arraycopy(a, i, a, 0, WIDTH - i) + a4 = a.asInstanceOf[Arr4] + shrinkOffsetIfToLarge(WIDTH3) + if ((lenRest >>> BITS3) == 0) depth = 3 + } else { + if (i > 0) a = copyOfRange(a, i, WIDTH) + aParent(0) = a + } + aParent = a + a = a(0).asInstanceOf[Array[AnyRef]] + } + if (depth >= 3) { + if (a == null) a = a3.asInstanceOf[Array[AnyRef]] + val i = (offset >>> BITS2) & MASK + if (depth == 3) { + if (i > 0) System.arraycopy(a, i, a, 0, WIDTH - i) + a3 = a.asInstanceOf[Arr3] + shrinkOffsetIfToLarge(WIDTH2) + if ((lenRest >>> BITS2) == 0) depth = 2 + } else { + if (i > 0) a = copyOfRange(a, i, WIDTH) + aParent(0) = a + } + aParent = a + a = a(0).asInstanceOf[Array[AnyRef]] + } + if (depth >= 2) { + if (a == null) a = a2.asInstanceOf[Array[AnyRef]] + val i = (offset >>> BITS) & MASK + if (depth == 2) { + if (i > 0) System.arraycopy(a, i, a, 0, WIDTH - i) + a2 = a.asInstanceOf[Arr2] + shrinkOffsetIfToLarge(WIDTH) + if ((lenRest >>> BITS) == 0) depth = 1 + } else { + if (i > 0) a = copyOfRange(a, i, WIDTH) + aParent(0) = a + } + aParent = a + a = a(0).asInstanceOf[Array[AnyRef]] + } + if (depth >= 1) { + if (a == null) a = a1.asInstanceOf[Array[AnyRef]] + val i = offset & MASK + if (depth == 1) { + if (i > 0) System.arraycopy(a, i, a, 0, WIDTH - i) + a1 = a.asInstanceOf[Arr1] + len1 -= offset + offset = 0 + } else { + if (i > 0) a = copyOfRange(a, i, WIDTH) + aParent(0) = a + } + } + prefixIsRightAligned = false + } + def addOne(elem: A): this.type = { if(len1 == WIDTH) advance() a1(len1) = elem.asInstanceOf[AnyRef] @@ -1582,6 +1718,86 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { } } + private[this] def addArrN(slice: Array[AnyRef], dim: Int): Unit = { +// assert(dim >= 2) +// assert(lenRest % WIDTH == 0) +// assert(len1 == 0 || len1 == WIDTH) + if (slice.isEmpty) return + if (len1 == WIDTH) advance() + val sl = slice.length + (dim: @switch) match { + case 2 => + // lenRest is always a multiple of WIDTH + val copy1 = mmin(((WIDTH2 - lenRest) >>> BITS) & MASK, sl) + val copy2 = sl - copy1 + val destPos = (lenRest >>> BITS) & MASK + System.arraycopy(slice, 0, a2, destPos, copy1) + advanceN(WIDTH * copy1) + if (copy2 > 0) { + System.arraycopy(slice, copy1, a2, 0, copy2) + advanceN(WIDTH * copy2) + } + case 3 => + if (lenRest % WIDTH2 != 0) { + // lenRest is not multiple of WIDTH2, so this slice does not align, need to try lower dimension + slice.foreach(e => addArrN(e.asInstanceOf[Array[AnyRef]], 2)) + return + } + val copy1 = mmin(((WIDTH3 - lenRest) >>> BITS2) & MASK, sl) + val copy2 = sl - copy1 + val destPos = (lenRest >>> BITS2) & MASK + System.arraycopy(slice, 0, a3, destPos, copy1) + advanceN(WIDTH2 * copy1) + if (copy2 > 0) { + System.arraycopy(slice, copy1, a3, 0, copy2) + advanceN(WIDTH2 * copy2) + } + case 4 => + if (lenRest % WIDTH3 != 0) { + // lenRest is not multiple of WIDTH3, so this slice does not align, need to try lower dimensions + slice.foreach(e => addArrN(e.asInstanceOf[Array[AnyRef]], 3)) + return + } + val copy1 = mmin(((WIDTH4 - lenRest) >>> BITS3) & MASK, sl) + val copy2 = sl - copy1 + val destPos = (lenRest >>> BITS3) & MASK + System.arraycopy(slice, 0, a4, destPos, copy1) + advanceN(WIDTH3 * copy1) + if (copy2 > 0) { + System.arraycopy(slice, copy1, a4, 0, copy2) + advanceN(WIDTH3 * copy2) + } + case 5 => + if (lenRest % WIDTH4 != 0) { + // lenRest is not multiple of WIDTH4, so this slice does not align, need to try lower dimensions + slice.foreach(e => addArrN(e.asInstanceOf[Array[AnyRef]], 4)) + return + } + val copy1 = mmin(((WIDTH5 - lenRest) >>> BITS4) & MASK, sl) + val copy2 = sl - copy1 + val destPos = (lenRest >>> BITS4) & MASK + System.arraycopy(slice, 0, a5, destPos, copy1) + advanceN(WIDTH4 * copy1) + if (copy2 > 0) { + System.arraycopy(slice, copy1, a5, 0, copy2) + advanceN(WIDTH4 * copy2) + } + case 6 => // note width is now LASTWIDTH + if (lenRest % WIDTH5 != 0) { + // lenRest is not multiple of WIDTH5, so this slice does not align, need to try lower dimensions + slice.foreach(e => addArrN(e.asInstanceOf[Array[AnyRef]], 5)) + return + } + val copy1 = sl + // there is no copy2 because there can't be another a6 to copy to + val destPos = lenRest >>> BITS5 + if (destPos + copy1 > LASTWIDTH) + throw new IllegalArgumentException("exceeding 2^31 elements") + System.arraycopy(slice, 0, a6, destPos, copy1) + advanceN(WIDTH5 * copy1) + } + } + private[this] def addVector(xs: Vector[A]): this.type = { val sliceCount = xs.vectorSliceCount var sliceIdx = 0 @@ -1589,6 +1805,8 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { val slice = xs.vectorSlice(sliceIdx) vectorSliceDim(sliceCount, sliceIdx) match { case 1 => addArr1(slice.asInstanceOf[Arr1]) + case n if len1 == WIDTH || len1 == 0 => + addArrN(slice.asInstanceOf[Array[AnyRef]], n) case n => foreachRec(n-2, slice, addArr1) } sliceIdx += 1 @@ -1598,7 +1816,7 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { override def addAll(xs: IterableOnce[A]): this.type = xs match { case v: Vector[_] => - if(len1 == 0 && lenRest == 0) initFrom(v) + if(len1 == 0 && lenRest == 0 && !prefixIsRightAligned) initFrom(v) else addVector(v.asInstanceOf[Vector[A]]) case _ => super.addAll(xs) @@ -1612,19 +1830,30 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { advance1(idx, xor) } + private[this] def advanceN(n: Int): Unit = if (n > 0) { + // assert(n % 32 == 0) + val idx = lenRest + n + val xor = idx ^ lenRest + lenRest = idx + len1 = 0 + advance1(idx, xor) + } + private[this] def advance1(idx: Int, xor: Int): Unit = { - if (xor < WIDTH2) { // level = 1 - if (depth == 1) { a2 = new Array(WIDTH); a2(0) = a1; depth += 1 } + if (xor <= 0) { // level = 6 or something very unexpected happened + throw new IllegalArgumentException(s"advance1($idx, $xor): a1=$a1, a2=$a2, a3=$a3, a4=$a4, a5=$a5, a6=$a6, depth=$depth") + } else if (xor < WIDTH2) { // level = 1 + if (depth <= 1) { a2 = new Array(WIDTH); a2(0) = a1; depth = 2 } a1 = new Array(WIDTH) a2((idx >>> BITS) & MASK) = a1 } else if (xor < WIDTH3) { // level = 2 - if (depth == 2) { a3 = new Array(WIDTH); a3(0) = a2; depth += 1 } + if (depth <= 2) { a3 = new Array(WIDTH); a3(0) = a2; depth = 3 } a1 = new Array(WIDTH) a2 = new Array(WIDTH) a2((idx >>> BITS) & MASK) = a1 a3((idx >>> BITS2) & MASK) = a2 } else if (xor < WIDTH4) { // level = 3 - if (depth == 3) { a4 = new Array(WIDTH); a4(0) = a3; depth += 1 } + if (depth <= 3) { a4 = new Array(WIDTH); a4(0) = a3; depth = 4 } a1 = new Array(WIDTH) a2 = new Array(WIDTH) a3 = new Array(WIDTH) @@ -1632,7 +1861,7 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { a3((idx >>> BITS2) & MASK) = a2 a4((idx >>> BITS3) & MASK) = a3 } else if (xor < WIDTH5) { // level = 4 - if (depth == 4) { a5 = new Array(WIDTH); a5(0) = a4; depth += 1 } + if (depth <= 4) { a5 = new Array(WIDTH); a5(0) = a4; depth = 5 } a1 = new Array(WIDTH) a2 = new Array(WIDTH) a3 = new Array(WIDTH) @@ -1641,8 +1870,8 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { a3((idx >>> BITS2) & MASK) = a2 a4((idx >>> BITS3) & MASK) = a3 a5((idx >>> BITS4) & MASK) = a4 - } else if (xor < WIDTH6) { // level = 5 - if (depth == 5) { a6 = new Array(LASTWIDTH); a6(0) = a5; depth += 1 } + } else { // level = 5 + if (depth <= 5) { a6 = new Array(LASTWIDTH); a6(0) = a5; depth = 6 } a1 = new Array(WIDTH) a2 = new Array(WIDTH) a3 = new Array(WIDTH) @@ -1652,19 +1881,18 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { a3((idx >>> BITS2) & MASK) = a2 a4((idx >>> BITS3) & MASK) = a3 a5((idx >>> BITS4) & MASK) = a4 - a6((idx >>> BITS5) & MASK) = a5 - } else { // level = 6 - throw new IllegalArgumentException(s"advance1($idx, $xor): a1=$a1, a2=$a2, a3=$a3, a4=$a4, a5=$a5, a6=$a6, depth=$depth") + a6(idx >>> BITS5) = a5 } } def result(): Vector[A] = { + if (prefixIsRightAligned) leftAlignPrefix() val len = len1 + lenRest val realLen = len - offset if(realLen == 0) Vector.empty + else if(len < 0) throw new IndexOutOfBoundsException(s"Vector cannot have negative size $len") else if(len <= WIDTH) { - if(realLen == WIDTH) new Vector1(a1) - else new Vector1(copyOf(a1, realLen)) + new Vector1(copyIfDifferentSize(a1, realLen)) } else if(len <= WIDTH2) { val i1 = (len-1) & MASK val i2 = (len-1) >>> BITS @@ -1771,10 +1999,9 @@ private[immutable] object VectorInline { final val WIDTH4 = 1 << BITS4 final val BITS5 = BITS * 5 final val WIDTH5 = 1 << BITS5 - final val BITS6 = BITS * 6 - final val WIDTH6 = 1 << BITS6 final val LASTWIDTH = WIDTH << 1 // 1 extra bit in the last level to go up to Int.MaxValue (2^31-1) instead of 2^30: final val Log2ConcatFaster = 5 + final val AlignToFaster = 64 type Arr1 = Array[AnyRef] type Arr2 = Array[Array[AnyRef]] diff --git a/src/library/scala/collection/mutable/ArrayBuffer.scala b/src/library/scala/collection/mutable/ArrayBuffer.scala index bc402bd8a8e5..e3ddeb71ef8e 100644 --- a/src/library/scala/collection/mutable/ArrayBuffer.scala +++ b/src/library/scala/collection/mutable/ArrayBuffer.scala @@ -26,7 +26,7 @@ import scala.collection.generic.DefaultSerializable * access take constant time (amortized time). Prepends and removes are * linear in the buffer size. * - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-mutable-collection-classes.html#array-buffers "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-mutable-collection-classes.html#array-buffers "Scala's Collection Library overview"]] * section on `Array Buffers` for more information. * @@ -197,7 +197,8 @@ class ArrayBuffer[A] private (initialElements: Array[AnyRef], initialSize: Int) // - `copyElemsToArray` will call `System.arraycopy` // - `System.arraycopy` will effectively "read" all the values before // overwriting any of them when two arrays are the the same reference - IterableOnce.copyElemsToArray(elems, array.asInstanceOf[Array[Any]], index, elemsLength) + val actual = IterableOnce.copyElemsToArray(elems, array.asInstanceOf[Array[Any]], index, elemsLength) + if (actual != elemsLength) throw new IllegalStateException(s"Copied $actual of $elemsLength") size0 = len + elemsLength // update size AFTER the copy, in case we're inserting a proxy } case _ => insertAll(index, ArrayBuffer.from(elems)) @@ -295,7 +296,8 @@ object ArrayBuffer extends StrictOptimizedSeqFactory[ArrayBuffer] { if (k >= 0) { // Avoid reallocation of buffer if length is known val array = ensureSize(emptyArray, 0, k) // don't duplicate sizing logic, and check VM array size limit - IterableOnce.copyElemsToArray(coll, array.asInstanceOf[Array[Any]]) + val actual = IterableOnce.copyElemsToArray(coll, array.asInstanceOf[Array[Any]]) + if (actual != k) throw new IllegalStateException(s"Copied $actual of $k") new ArrayBuffer[B](array, k) } else new ArrayBuffer[B] ++= coll diff --git a/src/library/scala/collection/mutable/ArrayBuilder.scala b/src/library/scala/collection/mutable/ArrayBuilder.scala index 880744d8473f..454527bcdebd 100644 --- a/src/library/scala/collection/mutable/ArrayBuilder.scala +++ b/src/library/scala/collection/mutable/ArrayBuilder.scala @@ -61,7 +61,8 @@ sealed abstract class ArrayBuilder[T] val k = xs.knownSize if (k > 0) { ensureSize(this.size + k) - IterableOnce.copyElemsToArray(xs, elems, this.size) + val actual = IterableOnce.copyElemsToArray(xs, elems, this.size) + if (actual != k) throw new IllegalStateException(s"Copied $actual of $k") size += k } else if (k < 0) super.addAll(xs) this diff --git a/src/library/scala/collection/mutable/ArrayDeque.scala b/src/library/scala/collection/mutable/ArrayDeque.scala index 1d8b9fe597e4..205e1607f824 100644 --- a/src/library/scala/collection/mutable/ArrayDeque.scala +++ b/src/library/scala/collection/mutable/ArrayDeque.scala @@ -21,7 +21,7 @@ import scala.reflect.ClassTag /** An implementation of a double-ended queue that internally uses a resizable circular buffer. * - * Append, prepend, removeFirst, removeLast and random-access (indexed-lookup and indexed-replacement) + * Append, prepend, removeHead, removeLast and random-access (indexed-lookup and indexed-replacement) * take amortized constant time. In general, removals and insertions at i-th index are O(min(i, n-i)) * and thus insertions and removals from end/beginning are fast. * @@ -434,7 +434,7 @@ class ArrayDeque[A] protected ( override def isEmpty = start == end - protected override def klone(): ArrayDeque[A] = new ArrayDeque(array.clone(), start = start, end = end) + override protected def klone(): ArrayDeque[A] = new ArrayDeque(array.clone(), start = start, end = end) override def iterableFactory: SeqFactory[ArrayDeque] = ArrayDeque @@ -529,7 +529,8 @@ object ArrayDeque extends StrictOptimizedSeqFactory[ArrayDeque] { val s = coll.knownSize if (s >= 0) { val array = alloc(s) - IterableOnce.copyElemsToArray(coll, array.asInstanceOf[Array[Any]]) + val actual = IterableOnce.copyElemsToArray(coll, array.asInstanceOf[Array[Any]]) + if (actual != s) throw new IllegalStateException(s"Copied $actual of $s") new ArrayDeque[B](array, start = 0, end = s) } else new ArrayDeque[B]() ++= coll } diff --git a/src/library/scala/collection/mutable/BitSet.scala b/src/library/scala/collection/mutable/BitSet.scala index cf727972f0f3..69ecc122c1f9 100644 --- a/src/library/scala/collection/mutable/BitSet.scala +++ b/src/library/scala/collection/mutable/BitSet.scala @@ -23,7 +23,7 @@ import scala.annotation.implicitNotFound * * $bitsetinfo * - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-mutable-collection-classes.html#mutable-bitsets "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-mutable-collection-classes.html#mutable-bitsets "Scala's Collection Library overview"]] * section on `Mutable Bitsets` for more information. * * @define Coll `BitSet` diff --git a/src/library/scala/collection/mutable/CollisionProofHashMap.scala b/src/library/scala/collection/mutable/CollisionProofHashMap.scala index f7619cd1384f..8542b5b56a01 100644 --- a/src/library/scala/collection/mutable/CollisionProofHashMap.scala +++ b/src/library/scala/collection/mutable/CollisionProofHashMap.scala @@ -24,7 +24,7 @@ import scala.runtime.Statics * as determined by the `Ordering` has to be consistent with `equals` and `hashCode`. Universal equality * of numeric types is not supported (similar to `AnyRefMap`). * - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-mutable-collection-classes.html#hash-tables "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-mutable-collection-classes.html#hash-tables "Scala's Collection Library overview"]] * section on `Hash Tables` for more information. * * @define Coll `mutable.CollisionProofHashMap` diff --git a/src/library/scala/collection/mutable/HashMap.scala b/src/library/scala/collection/mutable/HashMap.scala index 3b0eaa817eb5..7ad3cf3869e8 100644 --- a/src/library/scala/collection/mutable/HashMap.scala +++ b/src/library/scala/collection/mutable/HashMap.scala @@ -20,7 +20,7 @@ import scala.util.hashing.MurmurHash3 /** This class implements mutable maps using a hashtable. * - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-mutable-collection-classes.html#hash-tables "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-mutable-collection-classes.html#hash-tables "Scala's Collection Library overview"]] * section on `Hash Tables` for more information. * * @tparam K the type of the keys contained in this hash map. @@ -108,6 +108,13 @@ class HashMap[K, V](initialCapacity: Int, loadFactor: Double) put0(next.key, next.value, next.hash, getOld = false) } this + case lhm: mutable.LinkedHashMap[K, V] => + val iter = lhm.entryIterator + while (iter.hasNext) { + val entry = iter.next() + put0(entry.key, entry.value, entry.hash, getOld = false) + } + this case thatMap: Map[K, V] => thatMap.foreachEntry { (key: K, value: V) => put0(key, value, improveHash(key.##), getOld = false) @@ -195,6 +202,14 @@ class HashMap[K, V](initialCapacity: Int, loadFactor: Double) if (size == 0) return this } this + case lhs: mutable.LinkedHashSet[K] => + val iter = lhs.entryIterator + while (iter.hasNext) { + val next = iter.next() + remove0(next.key, next.hash) + if (size == 0) return this + } + this case _ => super.subtractAll(xs) } } diff --git a/src/library/scala/collection/mutable/HashSet.scala b/src/library/scala/collection/mutable/HashSet.scala index bf9e93575bf8..425721a41626 100644 --- a/src/library/scala/collection/mutable/HashSet.scala +++ b/src/library/scala/collection/mutable/HashSet.scala @@ -20,7 +20,7 @@ import scala.util.hashing.MurmurHash3 /** This class implements mutable sets using a hashtable. * - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-mutable-collection-classes.html#hash-tables "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-mutable-collection-classes.html#hash-tables "Scala's Collection Library overview"]] * section on `Hash Tables` for more information. * * @define Coll `mutable.HashSet` @@ -93,11 +93,18 @@ final class HashSet[A](initialCapacity: Int, loadFactor: Double) override def addAll(xs: IterableOnce[A]): this.type = { sizeHint(xs.knownSize) xs match { - case hm: immutable.HashSet[A] => - hm.foreachWithHash((k, h) => addElem(k, improveHash(h))) + case hs: immutable.HashSet[A] => + hs.foreachWithHash((k, h) => addElem(k, improveHash(h))) + this + case hs: mutable.HashSet[A] => + val iter = hs.nodeIterator + while (iter.hasNext) { + val next = iter.next() + addElem(next.key, next.hash) + } this - case hm: mutable.HashSet[A] => - val iter = hm.nodeIterator + case lhs: mutable.LinkedHashSet[A] => + val iter = lhs.entryIterator while (iter.hasNext) { val next = iter.next() addElem(next.key, next.hash) @@ -127,6 +134,14 @@ final class HashSet[A](initialCapacity: Int, loadFactor: Double) if (size == 0) return this } this + case lhs: mutable.LinkedHashSet[A] => + val iter = lhs.entryIterator + while (iter.hasNext) { + val next = iter.next() + remove(next.key, next.hash) + if (size == 0) return this + } + this case _ => super.subtractAll(xs) } } diff --git a/src/library/scala/collection/mutable/HashTable.scala b/src/library/scala/collection/mutable/HashTable.scala index dc559e86ce98..4153bd532163 100644 --- a/src/library/scala/collection/mutable/HashTable.scala +++ b/src/library/scala/collection/mutable/HashTable.scala @@ -35,8 +35,8 @@ import java.lang.Integer * * @tparam A type of the elements contained in this hash table. */ -// Was an abstract class, but to simplify the upgrade of the parallel collections I’ve made it a trait -private[collection] /*abstract class*/ trait HashTable[A, B, Entry >: Null <: HashEntry[A, Entry]] extends HashTable.HashUtils[A] { +// Not used in the standard library, but used in scala-parallel-collections +private[collection] trait HashTable[A, B, Entry >: Null <: HashEntry[A, Entry]] extends HashTable.HashUtils[A] { // Replacing Entry type parameter by abstract type member here allows to not expose to public // implementation-specific entry classes such as `DefaultEntry` or `LinkedEntry`. // However, I'm afraid it's too late now for such breaking change. diff --git a/src/library/scala/collection/mutable/LinkedHashMap.scala b/src/library/scala/collection/mutable/LinkedHashMap.scala index 22e9b4c53a1d..bc663f1d37d8 100644 --- a/src/library/scala/collection/mutable/LinkedHashMap.scala +++ b/src/library/scala/collection/mutable/LinkedHashMap.scala @@ -14,35 +14,10 @@ package scala package collection package mutable -import scala.annotation.nowarn +import scala.annotation.{nowarn, tailrec} import scala.collection.generic.DefaultSerializable +import scala.util.hashing.MurmurHash3 -/** $factoryInfo - * @define Coll `LinkedHashMap` - * @define coll linked hash map - */ -@SerialVersionUID(3L) -object LinkedHashMap extends MapFactory[LinkedHashMap] { - - def empty[K, V] = new LinkedHashMap[K, V] - - def from[K, V](it: collection.IterableOnce[(K, V)]) = - it match { - case lhm: LinkedHashMap[K, V] => lhm - case _ => Growable.from(empty[K, V], it) - } - - def newBuilder[K, V] = new GrowableBuilder(empty[K, V]) - - /** Class for the linked hash map entry, used internally. - */ - private[mutable] final class LinkedEntry[K, V](val key: K, var value: V) - extends HashEntry[K, LinkedEntry[K, V]] { - var earlier: LinkedEntry[K, V] = null - var later: LinkedEntry[K, V] = null - } - -} /** This class implements mutable maps using a hashtable. * The iterator and all traversal methods of this class visit elements in the order they were inserted. @@ -57,6 +32,7 @@ object LinkedHashMap extends MapFactory[LinkedHashMap] { * @define orderDependent * @define orderDependentFold */ +@deprecatedInheritance("LinkedHashMap will be made final; use .withDefault for the common use case of computing a default value", "2.13.11") class LinkedHashMap[K, V] extends AbstractMap[K, V] with SeqMap[K, V] @@ -72,34 +48,23 @@ class LinkedHashMap[K, V] // would not return the elements in insertion order private[collection] type Entry = LinkedHashMap.LinkedEntry[K, V] + private[collection] def _firstEntry: Entry = firstEntry - @transient protected var firstEntry: Entry = null - @transient protected var lastEntry: Entry = null - @transient private[this] var table: HashTable[K, V, Entry] = newHashTable - - // Used by scala-java8-compat (private[mutable] erases to public, so Java code can access it) - private[mutable] def getTable: HashTable[K, V, Entry] = table - - private def newHashTable = - new HashTable[K, V, Entry] { - def createNewEntry(key: K, value: V): Entry = { - val e = new Entry(key, value) - if (firstEntry eq null) firstEntry = e - else { lastEntry.later = e; e.earlier = lastEntry } - lastEntry = e - e - } + protected var firstEntry: Entry = null - override def foreachEntry[U](f: Entry => U): Unit = { - var cur = firstEntry - while (cur ne null) { - f(cur) - cur = cur.later - } - } + protected var lastEntry: Entry = null - } + /* Uses the same implementation as mutable.HashMap. The hashtable holds the following invariant: + * - For each i between 0 and table.length, the bucket at table(i) only contains keys whose hash-index is i. + * - Every bucket is sorted in ascendant hash order + * - The sum of the lengths of all buckets is equal to contentSize. + */ + private[this] var table = new Array[Entry](tableSizeFor(LinkedHashMap.defaultinitialSize)) + + private[this] var threshold: Int = newThreshold(table.length) + + private[this] var contentSize = 0 override def last: (K, V) = if (size > 0) (lastEntry.key, lastEntry.value) @@ -117,110 +82,237 @@ class LinkedHashMap[K, V] if (size > 0) Some((firstEntry.key, firstEntry.value)) else None - override def size = table.tableSize + override def size = contentSize override def knownSize: Int = size - override def isEmpty: Boolean = table.tableSize == 0 + override def isEmpty: Boolean = size == 0 + def get(key: K): Option[V] = { - val e = table.findEntry(key) + val e = findEntry(key) if (e == null) None else Some(e.value) } + override def sizeHint(size: Int): Unit = { + val target = tableSizeFor(((size + 1).toDouble / LinkedHashMap.defaultLoadFactor).toInt) + if (target > table.length) growTable(target) + } override def contains(key: K): Boolean = { if (getClass eq classOf[LinkedHashMap[_, _]]) - table.findEntry(key) != null + findEntry(key) != null else super.contains(key) // A subclass might override `get`, use the default implementation `contains`. } - override def put(key: K, value: V): Option[V] = { - val e = table.findOrAddEntry(key, value) - if (e eq null) None - else { val v = e.value; e.value = value; Some(v) } + override def put(key: K, value: V): Option[V] = put0(key, value, true) match { + case null => None + case sm => sm } - override def update(key: K, value: V): Unit = { - val e = table.findOrAddEntry(key, value) - if (e ne null) e.value = value + override def update(key: K, value: V): Unit = put0(key, value, false) + + override def remove(key: K): Option[V] = removeEntry0(key) match { + case null => None + case nd => Some(nd.value) } - override def remove(key: K): Option[V] = { - val e = table.removeEntry(key) - if (e eq null) None - else Some(remove0(e)) + override def getOrElse[V1 >: V](key: K, default: => V1): V1 = { + if (getClass != classOf[LinkedHashMap[_, _]]) { + // subclasses of LinkedHashMap might customise `get` ... + super.getOrElse(key, default) + } else { + // .. but in the common case, we can avoid the Option boxing. + val nd = findEntry(key) + if (nd eq null) default else nd.value + } } - private[this] def remove0(e: Entry): V = { - if (e.earlier eq null) firstEntry = e.later - else e.earlier.later = e.later - if (e.later eq null) lastEntry = e.earlier - else e.later.earlier = e.earlier - e.earlier = null // Null references to prevent nepotism - e.later = null - e.value + override def getOrElseUpdate(key: K, defaultValue: => V): V = { + if (getClass != classOf[LinkedHashMap[_, _]]) { + // subclasses of LinkedHashMap might customise `get` ... + super.getOrElseUpdate(key, defaultValue) + } else { + val hash = computeHash(key) + val idx = index(hash) + val nd = table(idx) match { + case null => null + case nd => nd.findEntry(key, hash) + } + if (nd != null) nd.value + else { + val table0 = table + val default = defaultValue + if (contentSize + 1 >= threshold) growTable(table.length * 2) + // Avoid recomputing index if the `defaultValue()` or new element hasn't triggered a table resize. + val newIdx = if (table0 eq table) idx else index(hash) + put0(key, default, false, hash, newIdx) + default + } + } } - def addOne(kv: (K, V)): this.type = { put(kv._1, kv._2); this } + private[this] def removeEntry0(elem: K): Entry = removeEntry0(elem, computeHash(elem)) + + /** Removes a key from this map if it exists + * + * @param elem the element to remove + * @param hash the **improved** hashcode of `element` (see computeHash) + * @return the node that contained element if it was present, otherwise null + */ + private[this] def removeEntry0(elem: K, hash: Int): Entry = { + val idx = index(hash) + table(idx) match { + case null => null + case nd if nd.hash == hash && nd.key == elem => + // first element matches + table(idx) = nd.next + deleteEntry(nd) + contentSize -= 1 + nd + case nd => + // find an element that matches + var prev = nd + var next = nd.next + while ((next ne null) && next.hash <= hash) { + if (next.hash == hash && next.key == elem) { + prev.next = next.next + deleteEntry(next) + contentSize -= 1 + return next + } + prev = next + next = next.next + } + null + } + } + + /** Computes the improved hash of an original (`any.##`) hash. */ + @`inline` private[this] def improveHash(originalHash: Int): Int = { + originalHash ^ (originalHash >>> 16) + } + @`inline` private[collection] def unimproveHash(improvedHash: Int): Int = improveHash(improvedHash) - def subtractOne(key: K): this.type = { remove(key); this } + /** Computes the improved hash of this key */ + @`inline` private[this] def computeHash(o: K): Int = improveHash(o.##) - def iterator: Iterator[(K, V)] = new AbstractIterator[(K, V)] { + @`inline` private[this] def index(hash: Int) = hash & (table.length - 1) + + @`inline` private[this] def findEntry(key: K): Entry = { + val hash = computeHash(key) + table(index(hash)) match { + case null => null + case nd => nd.findEntry(key, hash) + } + } + + def addOne(kv: (K, V)): this.type = { + put(kv._1, kv._2) + this + } + + def subtractOne(key: K): this.type = { + remove(key) + this + } + + private[this] abstract class LinkedHashMapIterator[T] extends AbstractIterator[T] { private[this] var cur = firstEntry - def hasNext = cur ne null - def next() = - if (hasNext) { val res = (cur.key, cur.value); cur = cur.later; res } + def extract(nd: Entry): T + def hasNext: Boolean = cur ne null + def next(): T = + if (hasNext) { val r = extract(cur); cur = cur.later; r } else Iterator.empty.next() } + def iterator: Iterator[(K, V)] = + if (size == 0) Iterator.empty + else new LinkedHashMapIterator[(K, V)] { + def extract(nd: Entry): (K, V) = (nd.key, nd.value) + } + protected class LinkedKeySet extends KeySet { override def iterableFactory: IterableFactory[collection.Set] = LinkedHashSet } override def keySet: collection.Set[K] = new LinkedKeySet - override def keysIterator: Iterator[K] = new AbstractIterator[K] { - private[this] var cur = firstEntry - def hasNext = cur ne null - def next() = - if (hasNext) { val res = cur.key; cur = cur.later; res } - else Iterator.empty.next() - } + override def keysIterator: Iterator[K] = + if (size == 0) Iterator.empty + else new LinkedHashMapIterator[K] { + def extract(nd: Entry): K = nd.key + } + + private[collection] def entryIterator: Iterator[Entry] = + if (size == 0) Iterator.empty + else new LinkedHashMapIterator[Entry] { + def extract(nd: Entry): Entry = nd + } + // Override updateWith for performance, so we can do the update while hashing // the input key only once and performing one lookup into the hash table override def updateWith(key: K)(remappingFunction: Option[V] => Option[V]): Option[V] = { - val keyIndex = table.index(table.elemHashCode(key)) - val entry = table.findEntry0(key, keyIndex) + if (getClass != classOf[LinkedHashMap[_, _]]) { + // subclasses of LinkedHashMap might customise `get` ... + super.updateWith(key)(remappingFunction) + } else { + val hash = computeHash(key) + val indexedHash = index(hash) + + var foundEntry: Entry = null + var previousEntry: Entry = null + table(indexedHash) match { + case null => + case nd => + @tailrec + def findEntry(prev: Entry, nd: Entry, k: K, h: Int): Unit = { + if (h == nd.hash && k == nd.key) { + previousEntry = prev + foundEntry = nd + } + else if ((nd.next eq null) || (nd.hash > h)) () + else findEntry(nd, nd.next, k, h) + } + + findEntry(null, nd, key, hash) + } - val previousValue = - if (entry == null) None - else Some(entry.value) + val previousValue = foundEntry match { + case null => None + case nd => Some(nd.value) + } - val nextValue = remappingFunction(previousValue) + val nextValue = remappingFunction(previousValue) - (previousValue, nextValue) match { - case (None, None) => // do nothing - case (Some(_), None) => - remove0(entry) - table.removeEntry0(key, keyIndex) + (previousValue, nextValue) match { + case (None, None) => // do nothing - case (None, Some(value)) => - table.addEntry0(table.createNewEntry(key, value), keyIndex) + case (Some(_), None) => + if (previousEntry != null) previousEntry.next = foundEntry.next + else table(indexedHash) = foundEntry.next + deleteEntry(foundEntry) + contentSize -= 1 - case (Some(_), Some(value)) => - entry.value = value - } + case (None, Some(value)) => + val newIndexedHash = + if (contentSize + 1 >= threshold) { + growTable(table.length * 2) + index(hash) + } else indexedHash + put0(key, value, false, hash, newIndexedHash) - nextValue + case (Some(_), Some(newValue)) => foundEntry.value = newValue + } + nextValue + } } - override def valuesIterator: Iterator[V] = new AbstractIterator[V] { - private[this] var cur = firstEntry - def hasNext = cur ne null - def next() = - if (hasNext) { val res = cur.value; cur = cur.later; res } - else Iterator.empty.next() - } + override def valuesIterator: Iterator[V] = + if (size == 0) Iterator.empty + else new LinkedHashMapIterator[V] { + def extract(nd: Entry): V = nd.value + } + override def foreach[U](f: ((K, V)) => U): Unit = { var cur = firstEntry @@ -239,26 +331,179 @@ class LinkedHashMap[K, V] } override def clear(): Unit = { - table.clearTable() + java.util.Arrays.fill(table.asInstanceOf[Array[AnyRef]], null) + contentSize = 0 firstEntry = null lastEntry = null } - private def writeObject(out: java.io.ObjectOutputStream): Unit = { - out.defaultWriteObject() - table.serializeTo(out, { entry => - out.writeObject(entry.key) - out.writeObject(entry.value) - }) + private[this] def tableSizeFor(capacity: Int) = + (Integer.highestOneBit((capacity - 1).max(4)) * 2).min(1 << 30) + + private[this] def newThreshold(size: Int) = (size.toDouble * LinkedHashMap.defaultLoadFactor).toInt + + /*create a new entry. If table is empty(firstEntry is null), then the + * new entry will be the firstEntry. If not, just set the new entry to + * be the lastEntry. + * */ + private[this] def createNewEntry(key: K, hash: Int, value: V): Entry = { + val e = new Entry(key, hash, value) + if (firstEntry eq null) firstEntry = e + else { + lastEntry.later = e + e.earlier = lastEntry + } + lastEntry = e + e } - private def readObject(in: java.io.ObjectInputStream): Unit = { - in.defaultReadObject() - table = newHashTable - table.init(in, table.createNewEntry(in.readObject().asInstanceOf[K], in.readObject().asInstanceOf[V])) + /** Delete the entry from the LinkedHashMap, set the `earlier` and `later` pointers correctly */ + private[this] def deleteEntry(e: Entry): Unit = { + if (e.earlier eq null) firstEntry = e.later + else e.earlier.later = e.later + if (e.later eq null) lastEntry = e.earlier + else e.later.earlier = e.earlier + e.earlier = null + e.later = null + e.next = null + } + + private[this] def put0(key: K, value: V, getOld: Boolean): Some[V] = { + if (contentSize + 1 >= threshold) growTable(table.length * 2) + val hash = computeHash(key) + val idx = index(hash) + put0(key, value, getOld, hash, idx) + } + + private[this] def put0(key: K, value: V, getOld: Boolean, hash: Int, idx: Int): Some[V] = { + table(idx) match { + case null => + table(idx) = createNewEntry(key, hash, value) + case old => + var prev: Entry = null + var n = old + while ((n ne null) && n.hash <= hash) { + if (n.hash == hash && key == n.key) { + val old = n.value + n.value = value + return if (getOld) Some(old) else null + } + prev = n + n = n.next + } + val nnode = createNewEntry(key, hash, value) + if (prev eq null) { + nnode.next = old + table(idx) = nnode + } else { + nnode.next = prev.next + prev.next = nnode + } + } + contentSize += 1 + null } + private[this] def growTable(newlen: Int): Unit = { + if (newlen < 0) + throw new RuntimeException(s"new hash table size $newlen exceeds maximum") + var oldlen = table.length + threshold = newThreshold(newlen) + if (size == 0) table = new Array(newlen) + else { + table = java.util.Arrays.copyOf(table, newlen) + val preLow = new Entry(null.asInstanceOf[K], 0, null.asInstanceOf[V]) + val preHigh = new Entry(null.asInstanceOf[K], 0, null.asInstanceOf[V]) + // Split buckets until the new length has been reached. This could be done more + // efficiently when growing an already filled table to more than double the size. + while (oldlen < newlen) { + var i = 0 + while (i < oldlen) { + val old = table(i) + if (old ne null) { + preLow.next = null + preHigh.next = null + var lastLow = preLow + var lastHigh = preHigh + var n = old + while (n ne null) { + val next = n.next + if ((n.hash & oldlen) == 0) { // keep low + lastLow.next = n + lastLow = n + } else { // move to high + lastHigh.next = n + lastHigh = n + } + n = next + } + lastLow.next = null + if (old ne preLow.next) table(i) = preLow.next + if (preHigh.next ne null) { + table(i + oldlen) = preHigh.next + lastHigh.next = null + } + } + i += 1 + } + oldlen *= 2 + } + } + } + + override def hashCode: Int = { + if (isEmpty) MurmurHash3.emptyMapHash + else { + val tupleHashIterator = new LinkedHashMapIterator[Any] { + var hash: Int = 0 + override def hashCode: Int = hash + override def extract(nd: Entry): Any = { + hash = MurmurHash3.tuple2Hash(unimproveHash(nd.hash), nd.value.##) + this + } + } + MurmurHash3.unorderedHash(tupleHashIterator, MurmurHash3.mapSeed) + } + } @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") override protected[this] def stringPrefix = "LinkedHashMap" } +/** $factoryInfo + * @define Coll `LinkedHashMap` + * @define coll linked hash map + */ +@SerialVersionUID(3L) +object LinkedHashMap extends MapFactory[LinkedHashMap] { + + def empty[K, V] = new LinkedHashMap[K, V] + + def from[K, V](it: collection.IterableOnce[(K, V)]) = { + val newlhm = empty[K, V] + newlhm.sizeHint(it.knownSize) + newlhm.addAll(it) + newlhm + } + + def newBuilder[K, V] = new GrowableBuilder(empty[K, V]) + + /** Class for the linked hash map entry, used internally. + */ + private[mutable] final class LinkedEntry[K, V](val key: K, val hash: Int, var value: V) { + var earlier: LinkedEntry[K, V] = null + var later: LinkedEntry[K, V] = null + var next: LinkedEntry[K, V] = null + + @tailrec + final def findEntry(k: K, h: Int): LinkedEntry[K, V] = + if (h == hash && k == key) this + else if ((next eq null) || (hash > h)) null + else next.findEntry(k, h) + } + + /** The default load factor for the hash table */ + private[collection] final def defaultLoadFactor: Double = 0.75 + + /** The default initial capacity for the hash table */ + private[collection] final def defaultinitialSize: Int = 16 +} diff --git a/src/library/scala/collection/mutable/LinkedHashSet.scala b/src/library/scala/collection/mutable/LinkedHashSet.scala index 3c190a141dd6..0c01f8ea79ea 100644 --- a/src/library/scala/collection/mutable/LinkedHashSet.scala +++ b/src/library/scala/collection/mutable/LinkedHashSet.scala @@ -14,8 +14,9 @@ package scala package collection package mutable -import scala.annotation.nowarn +import scala.annotation.{nowarn, tailrec} import scala.collection.generic.DefaultSerializable +import scala.util.hashing.MurmurHash3 /** This class implements mutable sets using a hashtable. * The iterator and all traversal methods of this class visit elements in the order they were inserted. @@ -29,49 +30,40 @@ import scala.collection.generic.DefaultSerializable * @define orderDependent * @define orderDependentFold */ +@deprecatedInheritance("LinkedHashSet will be made final", "2.13.11") class LinkedHashSet[A] extends AbstractSet[A] with SetOps[A, LinkedHashSet, LinkedHashSet[A]] with StrictOptimizedIterableOps[A, LinkedHashSet, LinkedHashSet[A]] with IterableFactoryDefaults[A, LinkedHashSet] - with DefaultSerializable { + with DefaultSerializable { override def iterableFactory: IterableFactory[LinkedHashSet] = LinkedHashSet // stepper is not overridden to use XTableStepper because that stepper would not return the // elements in insertion order - type Entry = LinkedHashSet.Entry[A] + /*private*/ type Entry = LinkedHashSet.Entry[A] - @transient protected var firstEntry: Entry = null - @transient protected var lastEntry: Entry = null - @transient private[this] var table: HashTable[A, AnyRef, Entry] = newHashTable + protected var firstEntry: Entry = null - // Used by scala-java8-compat (private[mutable] erases to public, so Java code can access it) - private[mutable] def getTable: HashTable[A, AnyRef, Entry] = table + protected var lastEntry: Entry = null - private def newHashTable = - new HashTable[A, AnyRef, Entry] { - def createNewEntry(key: A, value: AnyRef) = { - val e = new Entry(key) - if (firstEntry eq null) firstEntry = e - else { lastEntry.later = e; e.earlier = lastEntry } - lastEntry = e - e - } - override def foreachEntry[U](f: Entry => U): Unit = { - var cur = firstEntry - while (cur ne null) { - f(cur) - cur = cur.later - } - } - } + /* Uses the same implementation as mutable.HashSet. The hashtable holds the following invariant: + * - For each i between 0 and table.length, the bucket at table(i) only contains keys whose hash-index is i. + * - Every bucket is sorted in ascendant hash order + * - The sum of the lengths of all buckets is equal to contentSize. + */ + private[this] var table = new Array[Entry](tableSizeFor(LinkedHashSet.defaultinitialSize)) + + private[this] var threshold: Int = newThreshold(table.length) + + private[this] var contentSize = 0 override def last: A = if (size > 0) lastEntry.key else throw new NoSuchElementException("Cannot call .last on empty LinkedHashSet") - + override def lastOption: Option[A] = if (size > 0) Some(lastEntry.key) else None @@ -79,18 +71,30 @@ class LinkedHashSet[A] override def head: A = if (size > 0) firstEntry.key else throw new NoSuchElementException("Cannot call .head on empty LinkedHashSet") - + override def headOption: Option[A] = if (size > 0) Some(firstEntry.key) else None - override def size: Int = table.tableSize + override def size: Int = contentSize override def knownSize: Int = size override def isEmpty: Boolean = size == 0 - def contains(elem: A): Boolean = table.findEntry(elem) ne null + + def contains(elem: A): Boolean = findEntry(elem) ne null + + override def sizeHint(size: Int): Unit = { + val target = tableSizeFor(((size + 1).toDouble / LinkedHashSet.defaultLoadFactor).toInt) + if (target > table.length) growTable(target) + } + + override def add(elem: A): Boolean = { + if (contentSize + 1 >= threshold) growTable(table.length * 2) + val hash = computeHash(elem) + put0(elem, hash, index(hash)) + } def addOne(elem: A): this.type = { - table.findOrAddEntry(elem, null) + add(elem) this } @@ -99,28 +103,25 @@ class LinkedHashSet[A] this } - override def remove(elem: A): Boolean = { - val e = table.removeEntry(elem) - if (e eq null) false - else { - if (e.earlier eq null) firstEntry = e.later - else e.earlier.later = e.later - if (e.later eq null) lastEntry = e.earlier - else e.later.earlier = e.earlier - e.earlier = null // Null references to prevent nepotism - e.later = null - true - } - } + override def remove(elem: A): Boolean = remove0(elem, computeHash(elem)) - def iterator: Iterator[A] = new AbstractIterator[A] { + private[this] abstract class LinkedHashSetIterator[T] extends AbstractIterator[T] { private[this] var cur = firstEntry - def hasNext = cur ne null - def next() = - if (hasNext) { val res = cur.key; cur = cur.later; res } + def extract(nd: Entry): T + def hasNext: Boolean = cur ne null + def next(): T = + if (hasNext) { val r = extract(cur); cur = cur.later; r } else Iterator.empty.next() } + def iterator: Iterator[A] = new LinkedHashSetIterator[A] { + override def extract(nd: Entry): A = nd.key + } + + private[collection] def entryIterator: Iterator[Entry] = new LinkedHashSetIterator[Entry] { + override def extract(nd: Entry): Entry = nd + } + override def foreach[U](f: A => U): Unit = { var cur = firstEntry while (cur ne null) { @@ -130,20 +131,176 @@ class LinkedHashSet[A] } override def clear(): Unit = { - table.clearTable() + java.util.Arrays.fill(table.asInstanceOf[Array[AnyRef]], null) + contentSize = 0 firstEntry = null lastEntry = null } - private def writeObject(out: java.io.ObjectOutputStream): Unit = { - out.defaultWriteObject() - table.serializeTo(out, { e => out.writeObject(e.key) }) + private[this] def tableSizeFor(capacity: Int) = + (Integer.highestOneBit((capacity - 1).max(4)) * 2).min(1 << 30) + + private[this] def newThreshold(size: Int) = (size.toDouble * LinkedHashSet.defaultLoadFactor).toInt + + @`inline` private[this] def improveHash(originalHash: Int): Int = { + originalHash ^ (originalHash >>> 16) } - private def readObject(in: java.io.ObjectInputStream): Unit = { - in.defaultReadObject() - table = newHashTable - table.init(in, table.createNewEntry(in.readObject().asInstanceOf[A], null)) + @`inline` private[collection] def unimproveHash(improvedHash: Int): Int = improveHash(improvedHash) + + /** Computes the improved hash of this key */ + @`inline` private[this] def computeHash(o: A): Int = improveHash(o.##) + + @`inline` private[this] def index(hash: Int) = hash & (table.length - 1) + + @`inline` private[this] def findEntry(key: A): Entry = { + val hash = computeHash(key) + table(index(hash)) match { + case null => null + case nd => nd.findEntry(key, hash) + } + } + + /*create a new entry. If table is empty(firstEntry is null), then the + * new entry will be the firstEntry. If not, just set the new entry to + * be the lastEntry. + * */ + private[this] def createNewEntry(key: A, hash: Int): Entry = { + val e = new Entry(key, hash) + if (firstEntry eq null) firstEntry = e + else { + lastEntry.later = e + e.earlier = lastEntry + } + lastEntry = e + e + } + + /** Delete the entry from the LinkedHashSet, set the `earlier` and `later` pointers correctly */ + private[this] def deleteEntry(e: Entry): Unit = { + if (e.earlier eq null) firstEntry = e.later + else e.earlier.later = e.later + if (e.later eq null) lastEntry = e.earlier + else e.later.earlier = e.earlier + e.earlier = null + e.later = null + e.next = null + } + + private[this] def put0(elem: A, hash: Int, idx: Int): Boolean = { + table(idx) match { + case null => + table(idx) = createNewEntry(elem, hash) + case old => + var prev: Entry = null + var n = old + while ((n ne null) && n.hash <= hash) { + if (n.hash == hash && elem == n.key) return false + prev = n + n = n.next + } + val nnode = createNewEntry(elem, hash) + if (prev eq null) { + nnode.next = old + table(idx) = nnode + } else { + nnode.next = prev.next + prev.next = nnode + } + } + contentSize += 1 + true + } + + private[this] def remove0(elem: A, hash: Int): Boolean = { + val idx = index(hash) + table(idx) match { + case null => false + case nd if nd.hash == hash && nd.key == elem => + // first element matches + table(idx) = nd.next + deleteEntry(nd) + contentSize -= 1 + true + case nd => + // find an element that matches + var prev = nd + var next = nd.next + while ((next ne null) && next.hash <= hash) { + if (next.hash == hash && next.key == elem) { + prev.next = next.next + deleteEntry(next) + contentSize -= 1 + return true + } + prev = next + next = next.next + } + false + } + } + + private[this] def growTable(newlen: Int): Unit = { + if (newlen < 0) + throw new RuntimeException(s"new hash table size $newlen exceeds maximum") + var oldlen = table.length + threshold = newThreshold(newlen) + if (size == 0) table = new Array(newlen) + else { + table = java.util.Arrays.copyOf(table, newlen) + val preLow = new Entry(null.asInstanceOf[A], 0) + val preHigh = new Entry(null.asInstanceOf[A], 0) + // Split buckets until the new length has been reached. This could be done more + // efficiently when growing an already filled table to more than double the size. + while (oldlen < newlen) { + var i = 0 + while (i < oldlen) { + val old = table(i) + if (old ne null) { + preLow.next = null + preHigh.next = null + var lastLow = preLow + var lastHigh = preHigh + var n = old + while (n ne null) { + val next = n.next + if ((n.hash & oldlen) == 0) { // keep low + lastLow.next = n + lastLow = n + } else { // move to high + lastHigh.next = n + lastHigh = n + } + n = next + } + lastLow.next = null + if (old ne preLow.next) table(i) = preLow.next + if (preHigh.next ne null) { + table(i + oldlen) = preHigh.next + lastHigh.next = null + } + } + i += 1 + } + oldlen *= 2 + } + } + } + + override def hashCode: Int = { + val setHashIterator = + if (isEmpty) this.iterator + else { + new LinkedHashSetIterator[Any] { + var hash: Int = 0 + override def hashCode: Int = hash + override def extract(nd: Entry): Any = { + hash = unimproveHash(nd.hash) + this + } + } + } + MurmurHash3.unorderedHash(setHashIterator, MurmurHash3.setSeed) } @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") @@ -159,19 +316,33 @@ object LinkedHashSet extends IterableFactory[LinkedHashSet] { override def empty[A]: LinkedHashSet[A] = new LinkedHashSet[A] - def from[E](it: collection.IterableOnce[E]) = - it match { - case lhs: LinkedHashSet[E] => lhs - case _ => Growable.from(empty[E], it) - } + def from[E](it: collection.IterableOnce[E]) = { + val newlhs = empty[E] + newlhs.sizeHint(it.knownSize) + newlhs.addAll(it) + newlhs + } def newBuilder[A] = new GrowableBuilder(empty[A]) /** Class for the linked hash set entry, used internally. */ - private[mutable] final class Entry[A](val key: A) extends HashEntry[A, Entry[A]] { + private[mutable] final class Entry[A](val key: A, val hash: Int) { var earlier: Entry[A] = null var later: Entry[A] = null + var next: Entry[A] = null + + @tailrec + final def findEntry(k: A, h: Int): Entry[A] = + if (h == hash && k == key) this + else if ((next eq null) || (hash > h)) null + else next.findEntry(k, h) } + + /** The default load factor for the hash table */ + private[collection] final def defaultLoadFactor: Double = 0.75 + + /** The default initial capacity for the hash table */ + private[collection] final def defaultinitialSize: Int = 16 } diff --git a/src/library/scala/collection/mutable/ListBuffer.scala b/src/library/scala/collection/mutable/ListBuffer.scala index 15cba2e663db..d66525763163 100644 --- a/src/library/scala/collection/mutable/ListBuffer.scala +++ b/src/library/scala/collection/mutable/ListBuffer.scala @@ -23,7 +23,7 @@ import scala.runtime.Statics.releaseFence /** A `Buffer` implementation backed by a list. It provides constant time * prepend and append. Most other operations are linear. * - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-mutable-collection-classes.html#list-buffers "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-mutable-collection-classes.html#list-buffers "Scala's Collection Library overview"]] * section on `List Buffers` for more information. * * @tparam A the type of this list buffer's elements. diff --git a/src/library/scala/collection/mutable/OpenHashMap.scala b/src/library/scala/collection/mutable/OpenHashMap.scala index 481317337663..22e99d4650d1 100644 --- a/src/library/scala/collection/mutable/OpenHashMap.scala +++ b/src/library/scala/collection/mutable/OpenHashMap.scala @@ -13,8 +13,8 @@ package scala.collection package mutable +import java.lang.Integer.numberOfLeadingZeros import java.util.ConcurrentModificationException - import scala.collection.generic.DefaultSerializable /** @@ -41,6 +41,8 @@ object OpenHashMap extends MapFactory[OpenHashMap] { final private class OpenEntry[Key, Value](var key: Key, var hash: Int, var value: Option[Value]) + + private[mutable] def nextPositivePowerOfTwo(target: Int): Int = 1 << -numberOfLeadingZeros(target - 1) } /** A mutable hash map based on an open addressing method. The precise scheme is @@ -75,7 +77,7 @@ class OpenHashMap[Key, Value](initialSize : Int) override def mapFactory: MapFactory[OpenHashMap] = OpenHashMap - private[this] val actualInitialSize = HashTable.nextPositivePowerOfTwo(initialSize) + private[this] val actualInitialSize = OpenHashMap.nextPositivePowerOfTwo(initialSize) private[this] var mask = actualInitialSize - 1 diff --git a/src/library/scala/collection/mutable/Queue.scala b/src/library/scala/collection/mutable/Queue.scala index 43e5e15faba6..18cce0bd3852 100644 --- a/src/library/scala/collection/mutable/Queue.scala +++ b/src/library/scala/collection/mutable/Queue.scala @@ -110,7 +110,7 @@ class Queue[A] protected (array: Array[AnyRef], start: Int, end: Int) */ @`inline` final def front: A = head - protected override def klone(): Queue[A] = { + override protected def klone(): Queue[A] = { val bf = newSpecificBuilder bf ++= this bf.result() diff --git a/src/library/scala/collection/mutable/Stack.scala b/src/library/scala/collection/mutable/Stack.scala index fd711567a822..675666bc805c 100644 --- a/src/library/scala/collection/mutable/Stack.scala +++ b/src/library/scala/collection/mutable/Stack.scala @@ -114,7 +114,7 @@ class Stack[A] protected (array: Array[AnyRef], start: Int, end: Int) */ @`inline` final def top: A = head - protected override def klone(): Stack[A] = { + override protected def klone(): Stack[A] = { val bf = newSpecificBuilder bf ++= this bf.result() diff --git a/src/library/scala/collection/mutable/StringBuilder.scala b/src/library/scala/collection/mutable/StringBuilder.scala index e5b271061e6a..1d8b9563e917 100644 --- a/src/library/scala/collection/mutable/StringBuilder.scala +++ b/src/library/scala/collection/mutable/StringBuilder.scala @@ -24,17 +24,30 @@ import scala.Predef.{ // unimport char-related implicit conversions to avoid tri //_ } -/** A builder for mutable sequence of characters. This class provides an API - * mostly compatible with `java.lang.StringBuilder`, except where there are - * conflicts with the Scala collections API (such as the `reverse` method.) - * - * $multipleResults - * - * @define Coll `mutable.IndexedSeq` - * @define coll string builder - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-mutable-collection-classes.html#stringbuilders "Scala's Collection Library overview"]] - * section on `StringBuilders` for more information. - */ +/** A builder of `String` which is also a mutable sequence of characters. + * + * This class provides an API mostly compatible with `java.lang.StringBuilder`, + * except where there are conflicts with the Scala collections API, such as the `reverse` method: + * [[reverse]] produces a new `StringBuilder`, and [[reverseInPlace]] mutates this builder. + * + * Mutating operations return either `this.type`, i.e., the current builder, or `Unit`. + * + * Other methods extract data or information from the builder without mutating it. + * + * The distinction is also reflected in naming conventions used by collections, + * such as `append`, which mutates, and `appended`, which does not, or `reverse`, + * which does not mutate, and `reverseInPlace`, which does. + * + * The `String` result may be obtained using either `result()` or `toString`. + * + * $multipleResults + * + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-mutable-collection-classes.html#stringbuilders "Scala's Collection Library overview"]] + * section on `StringBuilders` for more information. + * + * @define Coll `mutable.IndexedSeq` + * @define coll string builder + */ @SerialVersionUID(3L) final class StringBuilder(val underlying: java.lang.StringBuilder) extends AbstractSeq[Char] with ReusableBuilder[Char, String] diff --git a/src/library/scala/collection/mutable/UnrolledBuffer.scala b/src/library/scala/collection/mutable/UnrolledBuffer.scala index 3799c7bced48..489f2a1b0387 100644 --- a/src/library/scala/collection/mutable/UnrolledBuffer.scala +++ b/src/library/scala/collection/mutable/UnrolledBuffer.scala @@ -438,5 +438,5 @@ object UnrolledBuffer extends StrictOptimizedClassTagSeqFactory[UnrolledBuffer] // Todo -- revisit whether inheritance is the best way to achieve this functionality private[collection] class DoublingUnrolledBuffer[T](implicit t: ClassTag[T]) extends UnrolledBuffer[T]()(t) { override def calcNextLength(sz: Int) = if (sz < 10000) sz * 2 else sz - protected override def newUnrolled = new UnrolledBuffer.Unrolled[T](0, new Array[T](4), null, this) + override protected def newUnrolled = new UnrolledBuffer.Unrolled[T](0, new Array[T](4), null, this) } diff --git a/src/library/scala/collection/mutable/WeakHashMap.scala b/src/library/scala/collection/mutable/WeakHashMap.scala index 7f08253a106a..7286a318e1f9 100644 --- a/src/library/scala/collection/mutable/WeakHashMap.scala +++ b/src/library/scala/collection/mutable/WeakHashMap.scala @@ -24,7 +24,7 @@ import scala.collection.convert.JavaCollectionWrappers.{JMapWrapper, JMapWrapper * @tparam K type of keys contained in this map * @tparam V type of values associated with the keys * - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-mutable-collection-classes.html#weak-hash-maps "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-mutable-collection-classes.html#weak-hash-maps "Scala's Collection Library overview"]] * section on `Weak Hash Maps` for more information. * * @define Coll `WeakHashMap` diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 3bcedc53a84a..1be6130db645 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -24,11 +24,12 @@ import scala.reflect.ClassTag import scala.concurrent.ExecutionContext.parasitic -/** A `Future` represents a value which may or may not *currently* be available, +/** A `Future` represents a value which may or may not be currently available, * but will be available at some point, or an exception if that value could not be made available. * - * Asynchronous computations that yield futures are created with the `Future.apply` call and are computed using a supplied `ExecutionContext`, - * which can be backed by a Thread pool. + * Asynchronous computations are created by calling `Future.apply`, which yields instances of `Future`. + * Computations are executed using an `ExecutionContext`, which is usually supplied implicitly, + * and which is commonly backed by a thread pool. * * {{{ * import ExecutionContext.Implicits.global @@ -41,6 +42,15 @@ import scala.concurrent.ExecutionContext.parasitic * } * }}} * + * Note that the `global` context is convenient but restricted: + * "fatal" exceptions are reported only by printing a stack trace, + * and the underlying thread pool may be shared by a mix of jobs. + * For any nontrivial application, see the caveats explained at [[ExecutionContext]] + * and also the overview linked below, which explains + * [[https://docs.scala-lang.org/overviews/core/futures.html#exceptions exception handling]] + * in depth. + * + * * @see [[https://docs.scala-lang.org/overviews/core/futures.html Futures and Promises]] * * @define multipleCallbacks @@ -55,8 +65,7 @@ import scala.concurrent.ExecutionContext.parasitic * - `InterruptedException` - not contained within futures * - all `scala.util.control.ControlThrowable` except `NonLocalReturnControl` - not contained within futures * - * Instead, the future is completed with a ExecutionException with one of the exceptions above - * as the cause. + * Instead, the future is completed with an ExecutionException that has one of the exceptions above as its cause. * If a future is failed with a `scala.runtime.NonLocalReturnControl`, * it is completed with a value from that throwable instead. * diff --git a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala index e59e723e8752..d662df8927a8 100644 --- a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala +++ b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala @@ -35,6 +35,7 @@ private[concurrent] object ExecutionContextImpl { private final val blockerPermits = new Semaphore(maxBlockers) + @annotation.nowarn("cat=deprecation") def wire[T <: Thread](thread: T): T = { thread.setDaemon(daemonic) thread.setUncaughtExceptionHandler(uncaught) diff --git a/src/library/scala/concurrent/impl/FutureConvertersImpl.scala b/src/library/scala/concurrent/impl/FutureConvertersImpl.scala index e73b4bd5bfca..0980ade5a5a7 100644 --- a/src/library/scala/concurrent/impl/FutureConvertersImpl.scala +++ b/src/library/scala/concurrent/impl/FutureConvertersImpl.scala @@ -91,8 +91,8 @@ private[scala] object FutureConvertersImpl { override def toString(): String = super[CompletableFuture].toString } - final class P[T](val wrapped: CompletionStage[T]) extends DefaultPromise[T] with BiConsumer[T, Throwable] { - override def accept(v: T, e: Throwable): Unit = { + final class P[T](val wrapped: CompletionStage[T]) extends DefaultPromise[T] with BiFunction[T, Throwable, Unit] { + override def apply(v: T, e: Throwable): Unit = { if (e == null) success(v) else failure(e) } diff --git a/src/library/scala/io/Position.scala b/src/library/scala/io/Position.scala index 14cabcc98854..719976c10ded 100644 --- a/src/library/scala/io/Position.scala +++ b/src/library/scala/io/Position.scala @@ -13,6 +13,8 @@ package scala package io +import annotation.nowarn + /** The object Position provides convenience methods to encode * line and column number in one single integer. The encoded line * (column) numbers range from 0 to `LINE_MASK` (`COLUMN_MASK`), @@ -71,13 +73,12 @@ private[scala] abstract class Position { def toString(pos: Int): String = line(pos) + ":" + column(pos) } +@nowarn private[scala] object Position extends Position { def checkInput(line: Int, column: Int): Unit = { if (line < 0) - throw new IllegalArgumentException(line + " < 0") - if ((line == 0) && (column != 0)) - throw new IllegalArgumentException(line + "," + column + " not allowed") - if (column < 0) - throw new IllegalArgumentException(line + "," + column + " not allowed") + throw new IllegalArgumentException(s"$line < 0") + if (line == 0 && column != 0 || column < 0) + throw new IllegalArgumentException(s"$line,$column not allowed") } } diff --git a/src/library/scala/io/Source.scala b/src/library/scala/io/Source.scala index 4ccb419e856e..02f8d50dbf3c 100644 --- a/src/library/scala/io/Source.scala +++ b/src/library/scala/io/Source.scala @@ -132,7 +132,7 @@ object Source { /** same as fromURL(new URL(s)) */ def fromURL(s: String)(implicit codec: Codec): BufferedSource = - fromURL(new URL(s))(codec) + fromURL(new URI(s).toURL)(codec) /** same as fromInputStream(url.openStream())(Codec(enc)) */ diff --git a/src/library/scala/jdk/javaapi/FutureConverters.scala b/src/library/scala/jdk/javaapi/FutureConverters.scala index 132b2feb03fb..d8888d207b4b 100644 --- a/src/library/scala/jdk/javaapi/FutureConverters.scala +++ b/src/library/scala/jdk/javaapi/FutureConverters.scala @@ -12,10 +12,10 @@ package scala.jdk.javaapi -import java.util.concurrent.CompletionStage - +import java.util.concurrent.{CompletableFuture, CompletionStage} import scala.concurrent.impl.FutureConvertersImpl.{CF, P} import scala.concurrent.{ExecutionContext, Future} +import scala.util.Success /** This object contains methods that convert between Scala [[scala.concurrent.Future]] and Java [[java.util.concurrent.CompletionStage]]. * @@ -70,9 +70,13 @@ object FutureConverters { case cf: CF[T] => cf.wrapped // in theory not safe (could be `class C extends Future[A] with CompletionStage[B]`): case f: Future[T @unchecked] => f + case cf: CompletableFuture[T @unchecked] if cf.isDone && !cf.isCompletedExceptionally => + val p = new P[T](cs) + p.tryComplete(Success(cf.join())) + p.future case _ => val p = new P[T](cs) - cs whenComplete p + cs.handle(p) p.future } } diff --git a/src/library/scala/languageFeature.scala b/src/library/scala/languageFeature.scala index aea8a1fbaddc..5d80e7c406d9 100644 --- a/src/library/scala/languageFeature.scala +++ b/src/library/scala/languageFeature.scala @@ -35,6 +35,7 @@ object languageFeature { @deprecated("scala.language.higherKinds no longer needs to be imported explicitly", "2.13.1") @meta.languageFeature("higher-kinded type", enableRequired = false) sealed trait higherKinds + @deprecated("scala.language.higherKinds no longer needs to be imported explicitly", "2.13.1") object higherKinds extends higherKinds @meta.languageFeature("#, which cannot be expressed by wildcards,", enableRequired = false) diff --git a/src/library/scala/math/BigInt.scala b/src/library/scala/math/BigInt.scala index c48bab4445ee..f4f248debde2 100644 --- a/src/library/scala/math/BigInt.scala +++ b/src/library/scala/math/BigInt.scala @@ -278,6 +278,7 @@ final class BigInt private (private var _bigInteger: BigInteger, private val _lo (shifted.signum != 0) && !(shifted equals BigInt.minusOne) } + @deprecated("isWhole on an integer type is always true", "2.12.15") def isWhole: Boolean = true def underlying: BigInteger = bigInteger diff --git a/src/library/scala/math/Numeric.scala b/src/library/scala/math/Numeric.scala index 935d26df2962..9dca0e4f0199 100644 --- a/src/library/scala/math/Numeric.scala +++ b/src/library/scala/math/Numeric.scala @@ -177,9 +177,20 @@ object Numeric { implicit object DoubleIsFractional extends DoubleIsFractional with Ordering.Double.IeeeOrdering trait BigDecimalIsConflicted extends Numeric[BigDecimal] { - def plus(x: BigDecimal, y: BigDecimal): BigDecimal = x + y - def minus(x: BigDecimal, y: BigDecimal): BigDecimal = x - y - def times(x: BigDecimal, y: BigDecimal): BigDecimal = x * y + // works around pollution of math context by ignoring identity element + def plus(x: BigDecimal, y: BigDecimal): BigDecimal = { + import BigDecimalIsConflicted._0 + if (x eq _0) y else x + y + } + def minus(x: BigDecimal, y: BigDecimal): BigDecimal = { + import BigDecimalIsConflicted._0 + if (x eq _0) -y else x - y + } + // works around pollution of math context by ignoring identity element + def times(x: BigDecimal, y: BigDecimal): BigDecimal = { + import BigDecimalIsConflicted._1 + if (x eq _1) y else x * y + } def negate(x: BigDecimal): BigDecimal = -x def fromInt(x: Int): BigDecimal = BigDecimal(x) def parseString(str: String): Option[BigDecimal] = Try(BigDecimal(str)).toOption @@ -188,6 +199,10 @@ object Numeric { def toFloat(x: BigDecimal): Float = x.floatValue def toDouble(x: BigDecimal): Double = x.doubleValue } + private object BigDecimalIsConflicted { + private val _0 = BigDecimal(0) // cached zero is ordinarily cached for default math context + private val _1 = BigDecimal(1) // cached one is ordinarily cached for default math context + } trait BigDecimalIsFractional extends BigDecimalIsConflicted with Fractional[BigDecimal] { def div(x: BigDecimal, y: BigDecimal): BigDecimal = x / y diff --git a/src/library/scala/runtime/RichInt.scala b/src/library/scala/runtime/RichInt.scala index 5538ba700ff1..fbe9aecd1b70 100644 --- a/src/library/scala/runtime/RichInt.scala +++ b/src/library/scala/runtime/RichInt.scala @@ -31,6 +31,7 @@ final class RichInt(val self: Int) extends AnyVal with ScalaNumberProxy[Int] wit /** Returns `'''true'''` if this number has no decimal component. * Always `'''true'''` for `RichInt`. */ + @deprecated("isWhole on an integer type is always true", "2.12.15") def isWhole = true override def isValidInt = true diff --git a/src/library/scala/runtime/ScalaNumberProxy.scala b/src/library/scala/runtime/ScalaNumberProxy.scala index f5afdf0225ec..cf3e21460ca7 100644 --- a/src/library/scala/runtime/ScalaNumberProxy.scala +++ b/src/library/scala/runtime/ScalaNumberProxy.scala @@ -50,6 +50,7 @@ trait ScalaNumberProxy[T] extends Any with ScalaNumericAnyConversions with Typed @deprecated("use `sign` method instead", since = "2.13.0") def signum: Int = num.signum(self) } trait ScalaWholeNumberProxy[T] extends Any with ScalaNumberProxy[T] { + @deprecated("isWhole on an integer type is always true", "2.12.15") def isWhole = true } trait IntegralProxy[T] extends Any with ScalaWholeNumberProxy[T] with RangedProxy[T] { diff --git a/src/library/scala/util/Properties.scala b/src/library/scala/util/Properties.scala index 0d3fb7b34f46..0703f43521d5 100644 --- a/src/library/scala/util/Properties.scala +++ b/src/library/scala/util/Properties.scala @@ -92,7 +92,7 @@ private[scala] trait PropertiesTrait { /** A verbose alternative to [[versionNumberString]]. */ val versionString = s"version ${scalaPropOrElse("version.number", "(unknown)")}" - val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2022, LAMP/EPFL and Lightbend, Inc.") + val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2023, LAMP/EPFL and Lightbend, Inc.") /** This is the encoding to use reading in source files, overridden with -encoding. * Note that it uses "prop" i.e. looks in the scala jar, not the system properties. diff --git a/src/library/scala/util/Using.scala b/src/library/scala/util/Using.scala index bf0ed0cfbe34..e90c651b471c 100644 --- a/src/library/scala/util/Using.scala +++ b/src/library/scala/util/Using.scala @@ -226,6 +226,7 @@ object Using { } private def preferentiallySuppress(primary: Throwable, secondary: Throwable): Throwable = { + @annotation.nowarn("cat=deprecation") // avoid warning on mention of ThreadDeath def score(t: Throwable): Int = t match { case _: VirtualMachineError => 4 case _: LinkageError => 3 diff --git a/src/library/scala/util/control/NonFatal.scala b/src/library/scala/util/control/NonFatal.scala index eb5d82fcd511..3bce19e8b2be 100644 --- a/src/library/scala/util/control/NonFatal.scala +++ b/src/library/scala/util/control/NonFatal.scala @@ -36,6 +36,7 @@ object NonFatal { /** * Returns true if the provided `Throwable` is to be considered non-fatal, or false if it is to be considered fatal */ + @annotation.nowarn("cat=deprecation") // avoid warning on mention of ThreadDeath def apply(t: Throwable): Boolean = t match { // VirtualMachineError includes OutOfMemoryError and other fatal errors case _: VirtualMachineError | _: ThreadDeath | _: InterruptedException | _: LinkageError | _: ControlThrowable => false diff --git a/src/manual/scala/tools/docutil/ManPage.scala b/src/manual/scala/tools/docutil/ManPage.scala index 853c17b94c0b..cfd9844629f2 100644 --- a/src/manual/scala/tools/docutil/ManPage.scala +++ b/src/manual/scala/tools/docutil/ManPage.scala @@ -24,7 +24,7 @@ object ManPage { case class Emph(contents: AbstractText) extends AbstractText case class Mono(contents: AbstractText) extends AbstractText case class Quote(contents: AbstractText) extends AbstractText - implicit def str2text(str: String) = Text(str) + implicit def str2text(str: String): Text = Text(str) case class Definition(term: AbstractText, description: AbstractText) case class DefinitionList(definitions: Definition*) extends AbstractText @@ -37,14 +37,14 @@ object ManPage { case class CodeSample(text: String) extends Paragraph case class BlockQuote(text: AbstractText) extends Paragraph implicit def text2para(text: AbstractText): Paragraph = TextParagraph(text) - implicit def str2para(str: String) = text2para(str2text(str)) + implicit def str2para(str: String): Paragraph = text2para(str2text(str)) case class BulletList(items: AbstractText*) extends Paragraph case class NumberedList(items: AbstractText*) extends Paragraph case class TitledPara(title: String, text: AbstractText) extends Paragraph case class EmbeddedSection(section: Section) extends Paragraph - implicit def section2Para(section: Section) = EmbeddedSection(section) + implicit def section2Para(section: Section): EmbeddedSection = EmbeddedSection(section) case class Section(title: String, paragraphs: Paragraph*) diff --git a/src/partest/scala/tools/partest/CompilerTest.scala b/src/partest/scala/tools/partest/CompilerTest.scala index f45846848d83..8114d071c460 100644 --- a/src/partest/scala/tools/partest/CompilerTest.scala +++ b/src/partest/scala/tools/partest/CompilerTest.scala @@ -12,6 +12,7 @@ package scala.tools.partest +import scala.language.implicitConversions import scala.reflect.runtime.{universe => ru} import scala.tools.nsc._ @@ -46,7 +47,7 @@ abstract class CompilerTest extends DirectTest { if (sym eq NoSymbol) NoType else appliedType(sym, compilerTypeFromTag(t)) } - implicit def mkMkType(sym: Symbol) = new MkType(sym) + implicit def mkMkType(sym: Symbol): MkType = new MkType(sym) def allMembers(root: Symbol): List[Symbol] = { def loop(seen: Set[Symbol], roots: List[Symbol]): List[Symbol] = { diff --git a/src/partest/scala/tools/partest/ReplTest.scala b/src/partest/scala/tools/partest/ReplTest.scala index c37aa976b072..8c273eee4908 100644 --- a/src/partest/scala/tools/partest/ReplTest.scala +++ b/src/partest/scala/tools/partest/ReplTest.scala @@ -75,7 +75,7 @@ trait Lambdaless extends ReplTest { override def normalize(s: String) = stripLambdaClassName(super.normalize(s)) } object Lambdaless { - private val lambdaless = """\$Lambda\$\d+/(?:0x[a-f0-9]{16}|\d+)(@[a-fA-F0-9]+)?""".r + private val lambdaless = """\$Lambda(?:\$\d+)?/(?:0x[a-f0-9]{16}|\d+)(?:@[a-fA-F0-9]+)?""".r private def stripLambdaClassName(s: String): String = lambdaless.replaceAllIn(s, Regex.quoteReplacement("")) } diff --git a/src/partest/scala/tools/partest/nest/AbstractRunner.scala b/src/partest/scala/tools/partest/nest/AbstractRunner.scala index cb1a2db35c19..e6473ed074a7 100644 --- a/src/partest/scala/tools/partest/nest/AbstractRunner.scala +++ b/src/partest/scala/tools/partest/nest/AbstractRunner.scala @@ -18,6 +18,7 @@ import utils.Properties._ import scala.tools.nsc.Properties.{propOrFalse, setProp, versionMsg} import scala.collection.mutable import scala.reflect.internal.util.Collections.distinctBy +import scala.sys.process.Process import scala.util.{Try, Success, Failure} import java.util.concurrent.Executors import java.util.concurrent.TimeUnit @@ -33,9 +34,11 @@ class AbstractRunner(val config: RunnerSpec.Config, protected final val testSour val debug: Boolean = config.optDebug || propOrFalse("partest.debug") val verbose: Boolean = config.optVerbose val terse: Boolean = config.optTerse + val realeasy: Boolean = config.optDev + val testBranch: Boolean = config.optBranch protected val printSummary = true - protected val partestCmd = "test/partest" + protected val partestCmd = "partest" private[this] var totalTests = 0 private[this] val passedTests = mutable.ListBuffer[TestState]() @@ -65,6 +68,12 @@ class AbstractRunner(val config: RunnerSpec.Config, protected final val testSour private[this] val realSysErr = System.err + val gitRunner = List("/usr/local/bin/git", "/usr/bin/git").map(f => new java.io.File(f)).find(_.canRead) + def runGit[R](cmd: String)(f: LazyList[String] => R): Option[R] = + Try { + gitRunner.map(git => f(Process(s"$git $cmd").lazyLines_!)) + }.toOption.flatten + def statusLine(state: TestState, durationMs: Long) = { import state._ import TestState._ @@ -163,16 +172,28 @@ class AbstractRunner(val config: RunnerSpec.Config, protected final val testSour } } - val bslash = "\\" - def files_s = failed0.map(_.testFile).mkString(s" ${bslash}\n ") - echo("# Failed test paths (this command will update checkfiles)") - echo(s"$partestCmd --update-check ${bslash}\n $files_s\n") + if (failed0.size == 1) { + echo("# A test failed. To update the check file:") + echo(s"$partestCmd --update-check ${failed0.head.testIdent}") + } + else { + val bslash = "\\" + def files_s = failed0.map(_.testFile).mkString(s" ${bslash}\n ") + echo("# Failed test paths (this command will update checkfiles)") + echo(s"$partestCmd --update-check ${bslash}\n $files_s\n") + } } if (printSummary) { echo(message) levyJudgment() } + if (realeasy) + for (lines <- runGit("status --porcelain")(_.filter(_.endsWith(".check")).map(_.drop(3))) if lines.nonEmpty) { + echo(bold(red("# There are uncommitted check files!"))) + for (file <- lines) + echo(s"$file\n") + } } } @@ -187,7 +208,7 @@ class AbstractRunner(val config: RunnerSpec.Config, protected final val testSour } else { val norm = Function.chain(Seq(testIdentToTestPath, checkFileToTestFile, testFileToTestDir, testDirToTestFile)) - val (individualTests, invalid) = config.parsed.residualArgs map (p => norm(Path(p))) partition denotesTestPath + val (individualTests, invalid) = config.parsed.residualArgs.map(p => norm(Path(p))).partition(denotesTestPath) if (invalid.nonEmpty) { if (verbose) invalid foreach (p => echoWarning(s"Discarding invalid test path " + p)) @@ -212,9 +233,71 @@ class AbstractRunner(val config: RunnerSpec.Config, protected final val testSour paths.sortBy(_.toString) } + // tests touched on this branch + val branchedTests: List[Path] = if (!testBranch) Nil else { + import scala.util.chaining._ + //* issue/12494 8dfd7f015d [upstream/2.13.x: ahead 1] Allow companion access boundary + //git rev-parse --abbrev-ref HEAD + //git branch -vv --list issue/1234 + //git diff --name-only upstream/2.13.x + val parseVerbose = raw"\* \S+ \S+ \[([^:]+): .*\] .*".r + def parseTracking(line: String) = line match { + case parseVerbose(tracking) => tracking.tap(ref => echo(s"Tracking $ref")) + case _ => "upstream/2.13.x".tap(default => echoWarning(s"Tracking default $default, failed to understand '$line'")) + } + case class MADFile(path: String, status: Int) + // D test/files/neg/t12590.scala + def madden(line: String): MADFile = + line.split("\\s+") match { + case Array(mad, p) => + val score = mad match { case "M" => 0 case "A" => 1 case "D" => -1 } + MADFile(p, score) + case _ => + echoWarning(s"diff --name-status, failed to understand '$line'") + MADFile("NOPATH", -1) + } + def isPresent(mad: MADFile) = mad.status >= 0 + def isTestFiles(path: String) = path.startsWith("test/files/") + val maybeFiles = + for { + current <- runGit("rev-parse --abbrev-ref HEAD")(_.head).tap(_.foreach(b => echo(s"Testing on branch $b"))) + tracking <- runGit(s"branch -vv --list $current")(lines => parseTracking(lines.head)) + files <- runGit(s"diff --name-status $tracking")(lines => lines.map(madden).filter(isPresent).map(_.path).filter(isTestFiles).toList) + } + yield files + //test/files/neg/t12349.check + //test/files/neg/t12349/t12349a.java + //test/files/neg/t12349/t12349b.scala + //test/files/neg/t12349/t12349c.scala + //test/files/neg/t12494.check + //test/files/neg/t12494.scala + maybeFiles.getOrElse(Nil).flatMap { s => + val path = Path(s) + val segs = path.segments + if (segs.length < 4 || !standardKinds.contains(segs(2))) Nil + else if (segs.length > 4) { + val prefix = Path(path.segments.take(4).mkString("/")) + List(pathSettings.testParent / prefix) + } + else { + // p.check -> p.scala or p + val norm = + if (!path.hasExtension("scala") && !path.isDirectory) { + val asDir = Path(path.path.stripSuffix(s".${path.extension}")) + if (asDir.exists) asDir + else asDir.addExtension("scala") + } + else path + List(pathSettings.testParent / norm) + } + } + .distinct.filter(denotesTestPath) + } + val isRerun = config.optFailed val rerunTests = if (isRerun) testKinds.failedTests else Nil - def miscTests = individualTests ++ greppedTests ++ rerunTests + val specialTests = if (realeasy) List(Path("test/files/run/t6240-universe-code-gen.scala")) else Nil + def miscTests = List(individualTests, greppedTests, branchedTests, rerunTests, specialTests).flatten val givenKinds = standardKinds filter config.parsed.isSet val kinds = ( @@ -230,15 +313,21 @@ class AbstractRunner(val config: RunnerSpec.Config, protected final val testSour def testContributors = { List( - if (rerunTests.isEmpty) "" else "previously failed tests", - if (kindsTests.isEmpty) "" else s"${kinds.size} named test categories", - if (greppedTests.isEmpty) "" else s"${greppedTests.size} tests matching '$grepExpr'", - if (individualTests.isEmpty) "" else "specified tests" - ) filterNot (_ == "") mkString ", " + (rerunTests, "previously failed tests"), + (kindsTests, s"${kinds.size} named test categories"), + (greppedTests, s"${greppedTests.size} tests matching '$grepExpr'"), + (branchedTests, s"${branchedTests.size} tests modified on this branch"), + (individualTests, "specified tests"), + (specialTests, "other tests you might have forgotten"), + ).filterNot(_._1.isEmpty).map(_._2) match { + case Nil => "the well of despair. I see you're not in a testing mood." + case one :: Nil => one + case all => all.init.mkString("", ", ", s", and ${all.last}") + } } - val allTests: Array[Path] = distinctBy(miscTests ++ kindsTests)(_.toCanonical).sortBy(_.toString).toArray - val grouped = (allTests groupBy kindOf).toArray sortBy (x => standardKinds indexOf x._1) + val allTests: Array[Path] = distinctBy(miscTests ::: kindsTests)(_.toCanonical).sortBy(_.toString).toArray + val grouped = allTests.groupBy(kindOf).toArray.sortBy(x => standardKinds.indexOf(x._1)) onlyIndividualTests = individualTests.nonEmpty && rerunTests.isEmpty && kindsTests.isEmpty && greppedTests.isEmpty totalTests = allTests.size diff --git a/src/partest/scala/tools/partest/nest/DirectCompiler.scala b/src/partest/scala/tools/partest/nest/DirectCompiler.scala index f46e5bd53a80..4368bf6fd87a 100644 --- a/src/partest/scala/tools/partest/nest/DirectCompiler.scala +++ b/src/partest/scala/tools/partest/nest/DirectCompiler.scala @@ -122,7 +122,7 @@ class DirectCompiler(val runner: Runner) { if (command.files.nonEmpty) reportError(command.files.mkString("flags file may only contain compiler options, found: ", space, "")) } - suiteRunner.verbose(s"% compiling ${ sources.map(_.testIdent).mkString(space) }${ if (suiteRunner.debug) " -d " + outDir else ""}") + suiteRunner.verbose(sources.map(_.testIdent).mkString("% compiling ", space, if (suiteRunner.debug) s" -d $outDir" else "")) def execCompile() = if (command.shouldStopWithInfo) { diff --git a/src/partest/scala/tools/partest/nest/Instance.scala b/src/partest/scala/tools/partest/nest/Instance.scala index 7c0827e99a05..21da651e7031 100644 --- a/src/partest/scala/tools/partest/nest/Instance.scala +++ b/src/partest/scala/tools/partest/nest/Instance.scala @@ -28,5 +28,5 @@ trait Instance extends Spec { def residualArgs = parsed.residualArgs // only args which were not options or args to options type OptionMagic = Opt.Instance - protected implicit def optionMagicAdditions(name: String) = new Opt.Instance(programInfo, parsed, name) + protected implicit def optionMagicAdditions(name: String): Opt.Instance = new Opt.Instance(programInfo, parsed, name) } diff --git a/src/partest/scala/tools/partest/nest/Reference.scala b/src/partest/scala/tools/partest/nest/Reference.scala index f068c7d785f6..ecf07319f8a5 100644 --- a/src/partest/scala/tools/partest/nest/Reference.scala +++ b/src/partest/scala/tools/partest/nest/Reference.scala @@ -45,7 +45,7 @@ trait Reference extends Spec { final def apply(args: String*): ThisCommandLine = creator(propertyArgs ++ args flatMap expandArg) type OptionMagic = Opt.Reference - protected implicit def optionMagicAdditions(name: String) = new Opt.Reference(programInfo, options, name) + protected implicit def optionMagicAdditions(name: String): Opt.Reference = new Opt.Reference(programInfo, options, name) } object Reference { diff --git a/src/partest/scala/tools/partest/nest/Runner.scala b/src/partest/scala/tools/partest/nest/Runner.scala index 9ec1153fd011..a44cea9b3635 100644 --- a/src/partest/scala/tools/partest/nest/Runner.scala +++ b/src/partest/scala/tools/partest/nest/Runner.scala @@ -16,25 +16,26 @@ package nest import java.io.{Console => _, _} import java.lang.reflect.InvocationTargetException import java.nio.charset.Charset -import java.nio.file.{Files, StandardOpenOption} +import java.nio.file.{Files, Path, StandardOpenOption} import scala.annotation.nowarn -import scala.collection.mutable.ListBuffer +import scala.collection.mutable, mutable.ListBuffer import scala.concurrent.duration.Duration import scala.reflect.internal.FatalError -import scala.reflect.internal.util.ScalaClassLoader +import scala.reflect.internal.util.ScalaClassLoader, ScalaClassLoader.URLClassLoader import scala.sys.process.{Process, ProcessLogger} import scala.tools.nsc.Properties.{isWin, propOrEmpty} import scala.tools.nsc.{CompilerCommand, Global, Settings} import scala.tools.nsc.reporters.ConsoleReporter import scala.tools.nsc.util.stackTraceString -import ClassPath.join -import TestState.{Crash, Fail, Pass, Skip, Updated} -import FileManager.{compareContents, joinPaths, withTempFile} -import scala.reflect.internal.util.ScalaClassLoader.URLClassLoader import scala.util.{Failure, Success, Try, Using} +import scala.util.Properties.isJavaAtLeast import scala.util.chaining._ import scala.util.control.ControlThrowable +import scala.util.matching.Regex +import ClassPath.join +import FileManager.{compareContents, joinPaths, withTempFile} +import TestState.{Crash, Fail, Pass, Skip, Updated} /** pos/t1234.scala or pos/t1234 if dir */ case class TestInfo(testFile: File) { @@ -112,7 +113,7 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { joinPaths(outDir :: testClassPath), "-J-Duser.language=en", "-J-Duser.country=US" - ) ++ (toolArgsFor(files)("javac") + ) ++ (toolArgsFor(files)(ToolName.javac) ) ++ (files.map(_.getAbsolutePath) ) @@ -332,18 +333,10 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { compareContents(original = checked, revised = logged, originalName = checkname, revisedName = logFile.getName) } - val gitRunner = List("/usr/local/bin/git", "/usr/bin/git") map (f => new java.io.File(f)) find (_.canRead) - val gitDiffOptions = "--ignore-space-at-eol --no-index " + propOrEmpty("partest.git_diff_options") - // --color=always --word-diff - def gitDiff(f1: File, f2: File): Option[String] = { - try gitRunner map { git => - val cmd = s"$git diff $gitDiffOptions $f1 $f2" - val diff = Process(cmd).lazyLines_!.drop(4).map(_ + "\n").mkString - - "\n" + diff - } - catch { case t: Exception => None } + val gitDiffOptions = "--ignore-space-at-eol --no-index " + propOrEmpty("partest.git_diff_options") + // --color=always --word-diff + runGit(s"diff $gitDiffOptions $f1 $f2")(_.drop(4).map(_ + "\n").mkString).map("\n" + _) } /** Normalize the log output by applying test-specific filters @@ -359,37 +352,39 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { * any Windows backslashes with the one true file separator char. */ def normalizeLog(): Unit = { - import scala.util.matching.Regex - // Apply judiciously; there are line comments in the "stub implementations" error output. - val slashes = """[/\\]+""".r + val slashes = """[/\\]+""".r def squashSlashes(s: String) = slashes.replaceAllIn(s, "/") // this string identifies a path and is also snipped from log output. - val elided = parentFile.getAbsolutePath + val elided = parentFile.getAbsolutePath // something to mark the elision in the log file (disabled) - val ellipsis = "" //".../" // using * looks like a comment + val ellipsis = "" //".../" // using * looks like a comment // no spaces in test file paths below root, because otherwise how to detect end of path string? val pathFinder = raw"""(?i)\Q${elided}${File.separator}\E([\${File.separator}\S]*)""".r - def canonicalize(s: String): String = - pathFinder.replaceAllIn(s, m => Regex.quoteReplacement(ellipsis + squashSlashes(m group 1))) + def canonicalize: String => String = { + val hiders = toolArgs(ToolName.hide).map(_.r) + (s: String) => { + val pathless = pathFinder.replaceAllIn(s, m => Regex.quoteReplacement(ellipsis + squashSlashes(m.group(1)))) + if (hiders.isEmpty) pathless + else hiders.foldLeft(pathless)((s, r) => r.replaceAllIn(s, m => "***")) + } + } - def masters = { + def masters = { val files = List(new File(parentFile, "filters"), new File(suiteRunner.pathSettings.srcDir.path, "filters")) - files.filter(_.exists).flatMap(_.fileLines).map(_.trim).filter(s => !(s startsWith "#")) + files.filter(_.exists).flatMap(_.fileLines).map(_.trim).filterNot(_.startsWith("#")) } - val filters = toolArgs("filter", split = false) ++ masters - val elisions = ListBuffer[String]() - //def lineFilter(s: String): Boolean = !(filters exists (s contains _)) - def lineFilter(s: String): Boolean = ( - filters map (_.r) forall { r => - val res = (r findFirstIn s).isEmpty - if (!res) elisions += s - res + val filters = toolArgs(ToolName.filter) ++ masters + lazy val elisions = ListBuffer[String]() + def lineFilter(s: String): Boolean = + filters.map(_.r).forall { r => + val unfiltered = r.findFirstIn(s).isEmpty + if (!unfiltered && suiteRunner.verbose) elisions += s + unfiltered } - ) logFile.mapInPlace(canonicalize)(lineFilter) if (suiteRunner.verbose && elisions.nonEmpty) { @@ -425,18 +420,15 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { * 2. Runs script function, providing log file and output directory as arguments. * 2b. or, just run the script without context and return a new context */ - def runInContext(body: => TestState): TestState = { - body - } + def runInContext(body: => TestState): TestState = body /** Grouped files in group order, and lex order within each group. */ - def groupedFiles(sources: List[File]): List[List[File]] = ( + def groupedFiles(sources: List[File]): List[List[File]] = if (sources.sizeIs > 1) { - val grouped = sources groupBy (_.group) - grouped.keys.toList.sorted map (k => grouped(k) sortBy (_.getName)) + val grouped = sources.groupBy(_.group) + grouped.keys.toList.sorted.map(grouped(_).sortBy(_.getName)) } else List(sources) - ) /** Source files for the given test file. */ def sources(file: File): List[File] = if (file.isDirectory) file.listFiles.toList.filter(_.isJavaOrScala) else List(file) @@ -453,70 +445,85 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { } // all sources in a round may contribute flags via // scalac: -flags + // under --realeasy, if a javaVersion isn't specified, require the minimum viable using -release 8 + // to avoid accidentally committing a test that requires a later JVM. def flagsForCompilation(sources: List[File]): List[String] = { - val perFile = toolArgsFor(sources)("scalac") - if (parentFile.getParentFile.getName == "macro-annot") "-Ymacro-annotations" :: perFile - else perFile + var perFile = toolArgsFor(sources)(ToolName.scalac) + if (parentFile.getParentFile.getName == "macro-annot") + perFile ::= "-Ymacro-annotations" + if (realeasy && isJavaAtLeast(9) && !perFile.exists(releaseFlag.matches) && toolArgsFor(sources)(ToolName.javaVersion).isEmpty) + perFile ::= "-release:8" + perFile } + private val releaseFlag = raw"--?release(?::\d+)?".r // inspect sources for tool args - def toolArgs(tool: String, split: Boolean = true): List[String] = - toolArgsFor(sources(testFile))(tool, split) + def toolArgs(tool: ToolName): List[String] = toolArgsFor(sources(testFile))(tool) + + // for each file, cache the args for each tool + private val fileToolArgs = new mutable.HashMap[Path, Map[ToolName, List[String]]] // inspect given files for tool args of the form `tool: args` // if args string ends in close comment, drop the `*` `/` - // if split, parse the args string as command line. + // if filter, return entire line as if quoted, else parse the args string as command line. + // Currently, we look for scalac, javac, java, javaVersion, filter, hide. // - def toolArgsFor(files: List[File])(tool: String, split: Boolean = true): List[String] = { - def argsFor(f: File): List[String] = { - import scala.jdk.OptionConverters._ - import scala.sys.process.{Parser => CommandLineParser}, CommandLineParser.tokenize - val max = 10 - val tag = s"$tool:" + def toolArgsFor(files: List[File])(tool: ToolName): List[String] = { + def argsFor(f: File): List[String] = fileToolArgs.getOrElseUpdate(f.toPath, readToolArgs(f)).apply(tool) + def readToolArgs(f: File): Map[ToolName, List[String]] = { + val header = readHeaderFrom(f) + ToolName.values.toList + .map(name => name -> fromHeader(name, header)) + .filterNot(_._2.isEmpty) + .toMap[ToolName, List[String]] + .withDefaultValue(List.empty[String]) + } + def fromHeader(name: ToolName, header: List[String]) = { + import scala.sys.process.Parser.tokenize + val tag = s"$name:" val endc = "*" + "/" // be forgiving of /* scalac: ... */ def stripped(s: String) = s.substring(s.indexOf(tag) + tag.length).stripSuffix(endc) - def argsplitter(s: String) = if (split) tokenize(s) else List(s.trim()) - val args = Using.resource(Files.lines(f.toPath, codec.charSet))( - _.limit(max).filter(_.contains(tag)).map(stripped).findAny.toScala - ) - args.map(argsplitter).getOrElse(Nil) + val lines = header.filter(_.contains(tag)).map(line => stripped(line).trim()) + if (name == ToolName.filter) lines else lines.flatMap(tokenize) } + def readHeaderFrom(f: File): List[String] = + Using.resource(Files.lines(f.toPath, codec.charSet))(stream => stream.limit(10).toArray()).toList.map(_.toString) files.flatMap(argsFor) } - abstract class CompileRound { - def fs: List[File] - def result: TestState + sealed abstract class CompileRound { + def files: List[File] def description: String + protected def computeResult: TestState + + final lazy val result: TestState = { pushTranscript(description); computeResult } - def fsString = fs map (_.toString stripPrefix parentFile.toString + "/") mkString " " - def isOk = result.isOk - def mkScalacString(): String = s"""scalac $fsString""" - override def toString = description + ( if (result.isOk) "" else "\n" + result.status ) + final protected def fsString = files.map(_.toString.stripPrefix(s"$parentFile/")).mkString(" ") + final override def toString = description + ( if (result.isOk) "" else "\n" + result.status ) } - case class OnlyJava(fs: List[File]) extends CompileRound { + final case class OnlyJava(files: List[File]) extends CompileRound { def description = s"""javac $fsString""" - lazy val result = { pushTranscript(description) ; javac(fs) } + override protected def computeResult = javac(files) } - case class OnlyScala(fs: List[File]) extends CompileRound { - def description = mkScalacString() - lazy val result = { pushTranscript(description) ; attemptCompile(fs) } + final case class OnlyScala(files: List[File]) extends CompileRound { + def description = s"""scalac $fsString""" + override protected def computeResult = attemptCompile(files) } - case class ScalaAndJava(fs: List[File]) extends CompileRound { - def description = mkScalacString() - lazy val result = { pushTranscript(description) ; attemptCompile(fs) } + final case class ScalaAndJava(files: List[File]) extends CompileRound { + def description = s"""scalac $fsString""" + override protected def computeResult = attemptCompile(files) } - case class SkipRound(fs: List[File], state: TestState) extends CompileRound { + final case class SkipRound(files: List[File], state: TestState) extends CompileRound { def description: String = state.status - lazy val result = { pushTranscript(description); state } + override protected def computeResult = state } def compilationRounds(file: File): List[CompileRound] = { import scala.util.Properties.javaSpecVersion - val Range = """(\d+)(?:(\+)|(?: *\- *(\d+)))?""".r + val Range = """(\d+)(?:(\+)|(?:-(\d+)))?""".r lazy val currentJavaVersion = javaSpecVersion.stripPrefix("1.").toInt val allFiles = sources(file) - val skipStates = toolArgsFor(allFiles)("javaVersion", split = false).flatMap({ + val skipStates = toolArgsFor(allFiles)(ToolName.javaVersion).flatMap { case v @ Range(from, plus, to) => val ok = if (plus == null) @@ -524,11 +531,12 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { else from.toInt <= currentJavaVersion && currentJavaVersion <= to.toInt else currentJavaVersion >= from.toInt - if (ok) None + if (ok && suiteRunner.realeasy && from.toInt > 8) Some(genSkip(s"skipped on Java $javaSpecVersion, compiling against JDK8 but must run on $v")) + else if (ok) None else Some(genSkip(s"skipped on Java $javaSpecVersion, only running on $v")) case v => Some(genFail(s"invalid javaVersion range in test comment: $v")) - }) + } skipStates.headOption match { case Some(state) => List(SkipRound(List(file), state)) case _ => groupedFiles(allFiles).flatMap(mixedCompileGroup) @@ -536,7 +544,7 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { } def mixedCompileGroup(allFiles: List[File]): List[CompileRound] = { - val (scalaFiles, javaFiles) = allFiles partition (_.isScala) + val (scalaFiles, javaFiles) = allFiles.partition(_.isScala) val round1 = if (scalaFiles.isEmpty) None else Some(ScalaAndJava(allFiles)) val round2 = if (javaFiles.isEmpty) None else Some(OnlyJava(javaFiles)) @@ -658,25 +666,20 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { } private def runRunTest(): TestState = { - val javaopts = toolArgs("java") + val javaopts = toolArgs(ToolName.java) val execInProcess = PartestDefaults.execInProcess && javaopts.isEmpty && !Set("specialized", "instrumented").contains(testFile.getParentFile.getName) def exec() = if (execInProcess) execTestInProcess(outDir, logFile) else execTest(outDir, logFile, javaopts) def noexec() = genSkip("no-exec: tests compiled but not run") runTestCommon(if (suiteRunner.config.optNoExec) noexec() else exec().andAlso(diffIsOk)) } - private def decompileClass(clazz: Class[_], isPackageObject: Boolean): String = { - import scala.tools.scalap - import scalap.scalax.rules.scalasig.ByteCode - - scalap.Main.decompileScala(ByteCode.forClass(clazz).bytes, isPackageObject) - } - def runScalapTest(): TestState = runTestCommon { - val isPackageObject = testFile.getName startsWith "package" + import scala.tools.scalap, scalap.scalax.rules.scalasig.ByteCode, scalap.Main.decompileScala + val isPackageObject = testFile.getName.startsWith("package") val className = testFile.getName.stripSuffix(".scala").capitalize + (if (!isPackageObject) "" else ".package") val loader = ScalaClassLoader.fromURLs(List(outDir.toURI.toURL), this.getClass.getClassLoader) - logFile writeAll decompileClass(loader loadClass className, isPackageObject) + def decompileClass(clazz: Class[_]): String = decompileScala(ByteCode.forClass(clazz).bytes, isPackageObject) + logFile.writeAll(decompileClass(loader.loadClass(className))) diffIsOk } @@ -774,3 +777,16 @@ final class TestTranscript { def append(text: String): Unit = { val s = buf.last ; buf.dropRightInPlace(1) ; buf += (s + text) } def toList = buf.toList } + +// Tool names in test file header: scalac, javac, java, javaVersion, filter, hide. +sealed trait ToolName +object ToolName { + case object scalac extends ToolName + case object javac extends ToolName + case object java extends ToolName + case object javaVersion extends ToolName + case object filter extends ToolName + case object hide extends ToolName + val values = Array(scalac, javac, java, javaVersion, filter, hide) + def named(s: String): ToolName = values.find(_.toString.equalsIgnoreCase(s)).getOrElse(throw new IllegalArgumentException(s)) +} diff --git a/src/partest/scala/tools/partest/nest/RunnerSpec.scala b/src/partest/scala/tools/partest/nest/RunnerSpec.scala index 80c1bae94c02..37dc5ca433de 100644 --- a/src/partest/scala/tools/partest/nest/RunnerSpec.scala +++ b/src/partest/scala/tools/partest/nest/RunnerSpec.scala @@ -56,8 +56,10 @@ trait RunnerSpec extends Spec with Meta.StdOpts with Interpolation { val optDebug = "debug" / "enable debugging output, preserve generated files" --? heading("Other options:") - val optVersion = "version" / "show Scala version and exit" --? - val optHelp = "help" / "show this page and exit" --? + val optDev = "realeasy" / "real easy way to test --release 8 and check uncommitted checks" --? + val optBranch = "branch" / "test changes on this branch" --? + val optVersion = "version" / "show Scala version and exit" --? + val optHelp = "help" / "show this page and exit" --? } diff --git a/src/partest/scala/tools/partest/package.scala b/src/partest/scala/tools/partest/package.scala index 5484b5dc8b94..1f949bc8e303 100644 --- a/src/partest/scala/tools/partest/package.scala +++ b/src/partest/scala/tools/partest/package.scala @@ -18,6 +18,7 @@ import java.util.concurrent.{Callable, ExecutorService} import scala.concurrent.duration.Duration import scala.io.Codec import scala.jdk.CollectionConverters._ +import scala.language.implicitConversions import scala.tools.nsc.util.Exceptional package object partest { @@ -57,6 +58,12 @@ package object partest { /** Sources have a numerical group, specified by name_7 and so on. */ private val GroupPattern = """.*_(\d+)""".r + private object IntOf { + def unapply(ds: String): Some[Int] = Some { + try ds.toInt + catch { case _: NumberFormatException => -1 } + } + } implicit class `special string ops`(private val s: String) extends AnyVal { def linesIfNonEmpty: Iterator[String] = if (!s.isEmpty) s.linesIterator else Iterator.empty @@ -84,11 +91,11 @@ package object partest { def hasExtension(ext: String) = sf hasExtension ext def changeExtension(ext: String): File = (sf changeExtension ext).jfile - /** The group number for this source file, or -1 for no group. */ + /** The group number for this source file, or -1 for no group or out of range. */ def group: Int = sf.stripExtension match { - case GroupPattern(g) if g.toInt >= 0 => g.toInt - case _ => -1 + case GroupPattern(IntOf(g)) => g + case _ => -1 } // Files.readString on jdk 11 @@ -123,8 +130,6 @@ package object partest { implicit def temporaryPath2File(x: Path): File = x.jfile implicit def stringPathToJavaFile(path: String): File = new File(path) - implicit lazy val implicitConversions = scala.language.implicitConversions - def fileSeparator = java.io.File.separator def pathSeparator = java.io.File.pathSeparator diff --git a/src/reflect/scala/reflect/api/Internals.scala b/src/reflect/scala/reflect/api/Internals.scala index 1478f55f897f..3e2f5f4fe736 100644 --- a/src/reflect/scala/reflect/api/Internals.scala +++ b/src/reflect/scala/reflect/api/Internals.scala @@ -1098,7 +1098,7 @@ trait Internals { self: Universe => trait CompatApi { /** @see [[CompatToken]] */ @deprecated("compatibility with Scala 2.10 EOL", "2.13.0") - implicit val token = new CompatToken + implicit val token: CompatToken = new CompatToken /** Scala 2.10 compatibility enrichments for BuildApi. */ @deprecated("compatibility with Scala 2.10 EOL", "2.13.0") diff --git a/src/reflect/scala/reflect/api/Printers.scala b/src/reflect/scala/reflect/api/Printers.scala index 28ad8e05718d..fef8321b6a3f 100644 --- a/src/reflect/scala/reflect/api/Printers.scala +++ b/src/reflect/scala/reflect/api/Printers.scala @@ -188,12 +188,14 @@ trait Printers { self: Universe => val buffer = new StringWriter() val writer = new PrintWriter(buffer) val printer = mkPrinter(writer) - printTypes.value.map(printTypes => if (printTypes) printer.withTypes else printer.withoutTypes) - printIds.value.map(printIds => if (printIds) printer.withIds else printer.withoutIds) - printOwners.value.map(printOwners => if (printOwners) printer.withOwners else printer.withoutOwners) - printKinds.value.map(printKinds => if (printKinds) printer.withKinds else printer.withoutKinds) - printMirrors.value.map(printMirrors => if (printMirrors) printer.withMirrors else printer.withoutMirrors) - printPositions.value.map(printPositions => if (printPositions) printer.withPositions else printer.withoutPositions) + + printTypes.value.foreach(if (_) printer.withTypes else printer.withoutTypes) + printIds.value.foreach(if (_) printer.withIds else printer.withoutIds) + printOwners.value.foreach(if (_) printer.withOwners else printer.withoutOwners) + printKinds.value.foreach(if (_) printer.withKinds else printer.withoutKinds) + printMirrors.value.foreach(if (_) printer.withMirrors else printer.withoutMirrors) + printPositions.value.foreach(if (_) printer.withPositions else printer.withoutPositions) + printer.print(what) writer.flush() buffer.toString diff --git a/src/reflect/scala/reflect/internal/AnnotationInfos.scala b/src/reflect/scala/reflect/internal/AnnotationInfos.scala index 5d67ec388214..c8cbfd5b9a50 100644 --- a/src/reflect/scala/reflect/internal/AnnotationInfos.scala +++ b/src/reflect/scala/reflect/internal/AnnotationInfos.scala @@ -140,8 +140,7 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable => def original = orig def setOriginal(t: Tree): this.type = { orig = t - this setPos t.pos - this + setPos(t.pos) } override def toString = completeAnnotationToString(this) diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index ef7a73c87258..ac7c6353aa1a 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -19,6 +19,7 @@ import scala.collection.mutable import Flags._ import scala.reflect.api.{Universe => ApiUniverse} import PartialFunction.cond +import util.StringContextStripMarginOps trait Definitions extends api.StandardDefinitions { self: SymbolTable => @@ -103,6 +104,7 @@ trait Definitions extends api.StandardDefinitions { lazy val lazyHolders = symbolsMap(ScalaValueClasses, x => getClassIfDefined("scala.runtime.Lazy" + x)) lazy val LazyRefClass = getClassIfDefined("scala.runtime.LazyRef") lazy val LazyUnitClass = getClassIfDefined("scala.runtime.LazyUnit") + lazy val RichFloatClass = getClassIfDefined("scala.runtime.RichFloat") lazy val allRefClasses: Set[Symbol] = { refClass.values.toSet ++ volatileRefClass.values.toSet ++ Set(VolatileObjectRefClass, ObjectRefClass) @@ -181,6 +183,8 @@ trait Definitions extends api.StandardDefinitions { } def ScalaPrimitiveValueClasses: List[ClassSymbol] = ScalaValueClasses + lazy val ScalaIntegralValueClasses: Set[Symbol] = Set(CharClass, ByteClass, ShortClass, IntClass, LongClass) + def underlyingOfValueClass(clazz: Symbol): Type = clazz.derivedValueClassUnbox.tpe.resultType @@ -855,7 +859,7 @@ trait Definitions extends api.StandardDefinitions { | was: $restpe | now""")(methodToExpressionTp(restpe)) case mt @ MethodType(_, restpe) if mt.isImplicit => methodToExpressionTp(restpe) - case mt @ MethodType(_, restpe) if !mt.isDependentMethodType => + case mt @ MethodType(_, restpe) => if (phase.erasedTypes) FunctionClass(mt.params.length).tpe else functionType(mt.paramTypes, methodToExpressionTp(restpe)) case NullaryMethodType(restpe) => methodToExpressionTp(restpe) diff --git a/src/reflect/scala/reflect/internal/ReificationSupport.scala b/src/reflect/scala/reflect/internal/ReificationSupport.scala index 4a02e4eba0c3..dbc4a82b4e03 100644 --- a/src/reflect/scala/reflect/internal/ReificationSupport.scala +++ b/src/reflect/scala/reflect/internal/ReificationSupport.scala @@ -124,7 +124,7 @@ trait ReificationSupport { self: SymbolTable => def mkAnnotation(trees: List[Tree]): List[Tree] = trees.map(mkAnnotation) def mkParam(argss: List[List[Tree]], extraFlags: FlagSet = NoFlags, excludeFlags: FlagSet = DEFERRED): List[List[ValDef]] = - argss.map { args => args.map { mkParam(_, extraFlags, excludeFlags) } } + argss.map(_.map(mkParam(_, extraFlags, excludeFlags))) def mkParam(tree: Tree, extraFlags: FlagSet, excludeFlags: FlagSet): ValDef = tree match { case Typed(Ident(name: TermName), tpt) => @@ -344,8 +344,12 @@ trait ReificationSupport { self: SymbolTable => def apply(mods: Modifiers, name: TypeName, tparams: List[Tree], constrMods: Modifiers, vparamss: List[List[Tree]], earlyDefs: List[Tree], parents: List[Tree], selfType: Tree, body: List[Tree]): ClassDef = { - val extraFlags = PARAMACCESSOR | (if (mods.isCase) CASEACCESSOR else 0L) - val vparamss0 = mkParam(vparamss, extraFlags, excludeFlags = DEFERRED | PARAM) + val extraCaseFlags = if (mods.isCase) CASEACCESSOR else 0L + val excludeFlags = DEFERRED | PARAM + val vparamss0 = + if (vparamss.isEmpty) vparamss.asInstanceOf[List[List[ValDef]]] + else mkParam(vparamss.head :: Nil, PARAMACCESSOR | extraCaseFlags, excludeFlags) ++ + mkParam(vparamss.tail, PARAMACCESSOR, excludeFlags) val tparams0 = mkTparams(tparams) val parents0 = gen.mkParents(mods, if (mods.isCase) parents.filter { diff --git a/src/reflect/scala/reflect/internal/Scopes.scala b/src/reflect/scala/reflect/internal/Scopes.scala index 97fe98712ab1..92e03eb4314b 100644 --- a/src/reflect/scala/reflect/internal/Scopes.scala +++ b/src/reflect/scala/reflect/internal/Scopes.scala @@ -153,12 +153,12 @@ trait Scopes extends api.Scopes { self: SymbolTable => /** enter a symbol */ - def enter[T <: Symbol](sym: T): T = { + def enter[T <: Symbol](sym: T): sym.type = { enterEntry(newScopeEntry(sym, this)) sym } - final def enterBefore(sym: Symbol, next: ScopeEntry): Symbol = { + final def enterBefore(sym: Symbol, next: ScopeEntry): sym.type = { assert(this != EmptyScope, sym) require(sym.name.hashCode() == next.sym.name.hashCode(), (sym, next.sym)) require(sym != next.sym, (sym, next.sym)) @@ -529,7 +529,7 @@ trait Scopes extends api.Scopes { self: SymbolTable => def newScopeWith(elems: Symbol*): Scope = { val startTime = if (settings.areStatisticsEnabled) statistics.startTimer(statistics.scopePopulationTime) else null val scope = newScope - elems foreach scope.enter + elems.foreach(scope.enter(_)) if (settings.areStatisticsEnabled) statistics.stopTimer(statistics.scopePopulationTime, startTime) scope } diff --git a/src/reflect/scala/reflect/internal/StdAttachments.scala b/src/reflect/scala/reflect/internal/StdAttachments.scala index 6045c42adec3..3089d24483c8 100644 --- a/src/reflect/scala/reflect/internal/StdAttachments.scala +++ b/src/reflect/scala/reflect/internal/StdAttachments.scala @@ -149,4 +149,13 @@ trait StdAttachments { /** Marks a Typed tree with Unit tpt. */ case object TypedExpectingUnitAttachment def explicitlyUnit(tree: Tree): Boolean = tree.hasAttachment[TypedExpectingUnitAttachment.type] + + /** For `val i = 42`, marks field as inferred so accessor (getter) can warn if implicit. */ + case object FieldTypeInferred extends PlainAttachment + + case class LookupAmbiguityWarning(msg: String) extends PlainAttachment + + /** Java sealed classes may be qualified with a permits clause specifying allowed subclasses. */ + case class PermittedSubclasses(permits: List[Tree]) extends PlainAttachment + case class PermittedSubclassSymbols(permits: List[Symbol]) extends PlainAttachment } diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index c41d05c658dd..566a352797b9 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -320,6 +320,7 @@ trait StdNames { final val SignatureATTR: NameType = nameType("Signature") final val SourceFileATTR: NameType = nameType("SourceFile") final val SyntheticATTR: NameType = nameType("Synthetic") + final val PermittedSubclassesATTR: NameType = nameType("PermittedSubclasses") final val scala_ : NameType = nameType("scala") @@ -1277,11 +1278,16 @@ trait StdNames { final val keywords = kw.result } - // "The identifiers var, yield, and record are restricted identifiers because they are not allowed in some contexts" - // A type identifier is an identifier that is not the character sequence var, yield, or record. - // An unqualified method identifier is an identifier that is not the character sequence yield. + // The identifiers non-sealed, permits, record, sealed, var, and yield are restricted identifiers + // because they are not allowed in some contexts. + // A type identifier is an identifier that is not the character sequence permits, record, sealed, var, or yield. + // An unqualified method identifier is an identifier that is not the character sequence yield. (JLS 3.8) class JavaRestrictedIdentifiers { + final val PERMITS: TermName = TermName("permits") final val RECORD: TermName = TermName("record") + final val SEALED: TermName = TermName("sealed") + final val UNSEALED: TermName = TermName("non-sealed") + final val NON: TermName = TermName("non") final val VAR: TermName = TermName("var") final val YIELD: TermName = TermName("yield") } diff --git a/src/reflect/scala/reflect/internal/SymbolPairs.scala b/src/reflect/scala/reflect/internal/SymbolPairs.scala index 7d2f1f895550..824fc1acb766 100644 --- a/src/reflect/scala/reflect/internal/SymbolPairs.scala +++ b/src/reflect/scala/reflect/internal/SymbolPairs.scala @@ -14,7 +14,7 @@ package scala package reflect package internal -import util.HashSet +import util.{HashSet, StringContextStripMarginOps} import scala.annotation.tailrec /** An abstraction for considering symbol pairs. diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala index f0fcae031058..c67fe257bf60 100644 --- a/src/reflect/scala/reflect/internal/SymbolTable.scala +++ b/src/reflect/scala/reflect/internal/SymbolTable.scala @@ -509,12 +509,6 @@ abstract class SymbolTable extends macros.Universe @deprecated("use enteringPhase", "2.10.0") // Used in sbt 0.12.4 @inline final def atPhase[T](ph: Phase)(op: => T): T = enteringPhase(ph)(op) - - /** - * Adds the `sm` String interpolator to a [[scala.StringContext]]. - */ - implicit val StringContextStripMarginOps: StringContext => StringContextStripMarginOps = util.StringContextStripMarginOps - protected[scala] def currentRunProfilerBeforeCompletion(root: Symbol, associatedFile: AbstractFile): Unit = () protected[scala] def currentRunProfilerAfterCompletion(root: Symbol, associatedFile: AbstractFile): Unit = () } diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index cffd4c8f1dcb..016c7ed6c245 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -1011,25 +1011,24 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def isStaticOwner: Boolean = isPackageClass || isModuleClass && isStatic - /** A helper function for isEffectivelyFinal. */ - private def isNotOverridden = ( - owner.isClass && ( - owner.isEffectivelyFinal - || (owner.isSealed && owner.sealedChildren.forall(c => c.isEffectivelyFinal && (overridingSymbol(c) == NoSymbol))) - ) - ) - /** Is this symbol effectively final? I.e, it cannot be overridden */ final def isEffectivelyFinal: Boolean = ( - ((this hasFlag FINAL | PACKAGE) && this != SingletonClass) + hasFlag(FINAL | PACKAGE) && this != SingletonClass || isModuleOrModuleClass - || isTerm && (isPrivate || isLocalToBlock || (hasAllFlags(notPRIVATE | METHOD) && !hasFlag(DEFERRED))) + || isTerm && (isPrivate || isLocalToBlock || hasAllFlags(notPRIVATE | METHOD) && !hasFlag(DEFERRED)) // We track known subclasses of term-owned classes, use that to infer finality. // However, don't look at owner for refinement classes (it's basically arbitrary). || isClass && !isRefinementClass && originalOwner.isTerm && children.isEmpty ) /** Is this symbol effectively final or a concrete term member of sealed class whose children do not override it */ - final def isEffectivelyFinalOrNotOverridden: Boolean = isEffectivelyFinal || (isTerm && !isDeferred && isNotOverridden) + final def isEffectivelyFinalOrNotOverridden: Boolean = { + def isNotOverridden = + owner.isClass && ( + owner.isEffectivelyFinal + || owner.isSealed && owner.sealedChildren.forall(c => c.isEffectivelyFinal && overridingSymbol(c) == NoSymbol) + ) + isEffectivelyFinal || isTerm && !isDeferred && isNotOverridden + } /** Is this symbol owned by a package? */ final def isTopLevel = owner.isPackageClass @@ -1096,21 +1095,21 @@ trait Symbols extends api.Symbols { self: SymbolTable => } def exists: Boolean = !isTopLevel || { - val isSourceLoader = rawInfo match { - case sl: SymLoader => sl.fromSource - case _ => false - } - def warnIfSourceLoader(): Unit = { + def warnIfSourceLoader(): false = { + val isSourceLoader = rawInfo match { + case sl: SymLoader => sl.fromSource + case _ => false + } + // Predef is completed early due to its autoimport; we used to get here when type checking its + // parent LowPriorityImplicits. See comment in c5441dc for more elaboration. + // Since the fix for scala/bug#7335 Predef parents must be defined in Predef.scala, and we should not + // get here anymore. if (isSourceLoader) - // Predef is completed early due to its autoimport; we used to get here when type checking its - // parent LowPriorityImplicits. See comment in c5441dc for more elaboration. - // Since the fix for scala/bug#7335 Predef parents must be defined in Predef.scala, and we should not - // get here anymore. devWarning(s"calling Symbol#exists with sourcefile based symbol loader may give incorrect results.") + false } - - rawInfo load this - rawInfo != NoType || { warnIfSourceLoader(); false } + rawInfo.load(this) + rawInfo != NoType || warnIfSourceLoader() } final def isInitialized: Boolean = @@ -3874,7 +3873,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => private[scala] final def argsDependOnPrefix(sym: Symbol): Boolean = { val tt = sym.owner.thisType - @annotation.tailrec + @tailrec def loop(mt: Type): Boolean = { mt match { case MethodType(params, restpe) => params.exists(_.info.dealias.exists(_ == tt)) || loop(restpe) diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 5a07f3ccd75c..fff71d710f77 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -19,7 +19,7 @@ import scala.annotation.{nowarn, tailrec} import scala.collection.mutable import scala.collection.mutable.ListBuffer import scala.reflect.macros.Attachments -import util.{ReusableInstance, Statistics} +import util.{ReusableInstance, Statistics, StringContextStripMarginOps} trait Trees extends api.Trees { self: SymbolTable => @@ -1426,7 +1426,7 @@ trait Trees extends api.Trees { else Modifiers(flags, privateWithin, newAnns) setPositions positions } - override def toString = "Modifiers(%s, %s, %s)".format(flagString, annotations mkString ", ", positions) + override def toString = s"Modifiers($flagString, ${annotations.mkString(",")}, $positions)" } object Modifiers extends ModifiersExtractor @@ -1452,7 +1452,7 @@ trait Trees extends api.Trees { super.setType(NoType) override def canHaveAttrs = false - override def setPos(pos: Position) = { requireLegal(pos, NoPosition, "pos"); this } + override def setPos(pos: Position): this.type = { requireLegal(pos, NoPosition, "pos"); this } override def pos_=(pos: Position) = setPos(pos) override def setType(t: Type) = { requireLegal(t, NoType, "tpe"); this } override def tpe_=(t: Type) = setType(t) @@ -1464,15 +1464,13 @@ trait Trees extends api.Trees { override def removeAttachment[T: ClassTag]: this.type = attachmentWarning() private def attachmentWarning(): this.type = {devWarning(s"Attempt to mutate attachments on $self ignored"); this} - private def requireLegal(value: Any, allowed: Any, what: String) = ( - if (value != allowed) { + private def requireLegal(value: Any, allowed: Any, what: String): Unit = + if (value != allowed && this != pendingSuperCall) { log(s"can't set $what for $self to value other than $allowed") if (settings.isDebug && settings.isDeveloper) - (new Throwable).printStackTrace + new Throwable(s"can't set $what for $self to value other than $allowed").printStackTrace } - ) - override def traverse(traverser: Traverser): Unit = - () + override def traverse(traverser: Traverser): Unit = () } case object EmptyTree extends TermTree with CannotHaveAttrs { diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index f68ac6ddec46..990bb8a438b6 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -772,8 +772,8 @@ trait Types def withFilter(p: Type => Boolean) = new FilterMapForeach(p) class FilterMapForeach(p: Type => Boolean) extends FilterTypeCollector(p){ - def foreach[U](f: Type => U): Unit = collect(Type.this) foreach f - def map[T](f: Type => T): List[T] = collect(Type.this) map f + def foreach[U](f: Type => U): Unit = this.collect(Type.this).foreach(f) + def map[T](f: Type => T): List[T] = this.collect(Type.this).map(f) } @inline final def orElse(alt: => Type): Type = if (this ne NoType) this else alt @@ -1769,9 +1769,9 @@ trait Types } } } - //Console.println("baseTypeSeq(" + typeSymbol + ") = " + baseTypeSeqCache.toList);//DEBUG + //Console.println(s"baseTypeSeq(${tpe.typeSymbol}) = ${tpe.baseTypeSeqCache.toList}") //DEBUG if (tpe.baseTypeSeqCache eq undetBaseTypeSeq) - throw new TypeError("illegal cyclic inheritance involving " + tpe.typeSymbol) + throw new TypeError(s"illegal cyclic inheritance involving ${tpe.typeSymbol}") } protected def defineBaseClassesOfCompoundType(tpe: CompoundType): Unit = { @@ -2792,8 +2792,9 @@ trait Types } } } + //Console.println(s"baseTypeSeq(${tpe.typeSymbol}) = ${tpe.baseTypeSeqCache.toList}") //DEBUG if (tpe.baseTypeSeqCache == undetBaseTypeSeq) - throw new TypeError("illegal cyclic inheritance involving " + tpe.sym) + throw new TypeError(s"illegal cyclic inheritance involving ${tpe.sym}") } /** A class representing a method type with parameters. diff --git a/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala index 92357d0e0e19..414da1c386f1 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala @@ -15,9 +15,9 @@ package reflect package internal package tpe -import scala.collection.mutable -import util.TriState import scala.annotation.tailrec +import scala.collection.mutable +import util.{StringContextStripMarginOps, TriState} trait TypeComparers { self: SymbolTable => diff --git a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala index 96684ffe9f3e..a8997fa04486 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala @@ -15,11 +15,12 @@ package reflect package internal package tpe +import scala.annotation.{nowarn, tailrec} import scala.collection.{immutable, mutable} +import scala.collection.mutable.ListBuffer import Flags._ -import scala.annotation.{nowarn, tailrec} import Variance._ -import scala.collection.mutable.ListBuffer +import util.StringContextStripMarginOps private[internal] trait TypeMaps { self: SymbolTable => diff --git a/src/reflect/scala/reflect/internal/transform/UnCurry.scala b/src/reflect/scala/reflect/internal/transform/UnCurry.scala index d8f50f9d3a9f..a6cfbed7ea8d 100644 --- a/src/reflect/scala/reflect/internal/transform/UnCurry.scala +++ b/src/reflect/scala/reflect/internal/transform/UnCurry.scala @@ -107,7 +107,7 @@ trait UnCurry { if ((parents1 eq parents) && varargOverloads.isEmpty) tp else { val newDecls = decls.cloneScope - varargOverloads.foreach(newDecls.enter) + varargOverloads.foreach(newDecls.enter(_)) ClassInfoType(parents1, newDecls, clazz) } // @MAT normalize in decls?? diff --git a/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala b/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala index 2c50d5cf9443..28062740a315 100644 --- a/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala +++ b/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala @@ -16,7 +16,7 @@ package reflect.internal.util import scala.collection.mutable import scala.collection.immutable.ArraySeq import scala.reflect.io.AbstractFile -import java.net.{URL, URLConnection, URLStreamHandler} +import java.net.{URI, URL, URLConnection, URLStreamHandler} import java.security.cert.Certificate import java.security.{CodeSource, ProtectionDomain} import java.util.{Collections => JCollections, Enumeration => JEnumeration} @@ -76,6 +76,10 @@ class AbstractFileClassLoader(val root: AbstractFile, parent: ClassLoader) else defineClass(name, bytes, 0, bytes.length, protectionDomain) } + + // on JDK 20 the URL constructor we're using is deprecated, but the recommended + // replacement, URL.of, doesn't exist on JDK 8 + @annotation.nowarn("cat=deprecation") override protected def findResource(name: String): URL = findAbstractFile(name) match { case null => null case file => new URL(null, s"memory:${file.path}", new URLStreamHandler { @@ -85,6 +89,7 @@ class AbstractFileClassLoader(val root: AbstractFile, parent: ClassLoader) } }) } + override protected def findResources(name: String): JEnumeration[URL] = findResource(name) match { case null => JCollections.enumeration(JCollections.emptyList[URL]) //JCollections.emptyEnumeration[URL] case url => JCollections.enumeration(JCollections.singleton(url)) @@ -98,7 +103,7 @@ class AbstractFileClassLoader(val root: AbstractFile, parent: ClassLoader) val n = s.lastIndexOf('!') if (n < 0) null else { val path = s.substring(0, n) - new ProtectionDomain(new CodeSource(new URL(path), null.asInstanceOf[Array[Certificate]]), null, this, null) + new ProtectionDomain(new CodeSource(new URI(path).toURL, null.asInstanceOf[Array[Certificate]]), null, this, null) } } } diff --git a/src/reflect/scala/reflect/internal/util/ChromeTrace.scala b/src/reflect/scala/reflect/internal/util/ChromeTrace.scala index 8b3c533648ed..d77bb46fa876 100644 --- a/src/reflect/scala/reflect/internal/util/ChromeTrace.scala +++ b/src/reflect/scala/reflect/internal/util/ChromeTrace.scala @@ -43,6 +43,7 @@ final class ChromeTrace(f: Path) extends Closeable { private val traceWriter = FileUtils.newAsyncBufferedWriter(f) private val context = mutable.Stack[JsonContext](TopContext) private val tidCache = new ThreadLocal[String]() { + @annotation.nowarn("cat=deprecation") override def initialValue(): String = f"${Thread.currentThread().getId}%05d" } objStart() diff --git a/src/reflect/scala/reflect/internal/util/StripMarginInterpolator.scala b/src/reflect/scala/reflect/internal/util/StripMarginInterpolator.scala index 16a265dd4677..5ef032b94cc5 100644 --- a/src/reflect/scala/reflect/internal/util/StripMarginInterpolator.scala +++ b/src/reflect/scala/reflect/internal/util/StripMarginInterpolator.scala @@ -23,7 +23,7 @@ trait StripMarginInterpolator { * and [[scala.StringContext#raw]]. * * The margin of each line is defined by whitespace leading up to a '|' character. - * This margin is stripped '''before''' the arguments are interpolated into to string. + * This margin is stripped '''before''' the arguments are interpolated into the string. * * String escape sequences are '''not''' processed; this interpolator is designed to * be used with triple quoted Strings. @@ -31,23 +31,30 @@ trait StripMarginInterpolator { * {{{ * scala> val foo = "f|o|o" * foo: String = f|o|o - * scala> sm"""|${foo} + * scala> sm"""|${foo}| * |""" * res0: String = - * "f|o|o + * "f|o|o| * " * }}} */ - final def sm(args: Any*): String = { + final def sm(args: Any*): String = impl('|', args: _*) + + private final def impl(sep: Char, args: Any*): String = { def isLineBreak(c: Char) = c == '\n' || c == '\f' // compatible with StringOps#isLineBreak def stripTrailingPart(s: String) = { val (pre, post) = s.span(c => !isLineBreak(c)) - pre + post.stripMargin + pre + post.stripMargin(sep) } val stripped: List[String] = stringContext.parts.toList match { - case head :: tail => head.stripMargin :: (tail map stripTrailingPart) + case head :: tail => head.stripMargin(sep) :: tail.map(stripTrailingPart) case Nil => Nil } new StringContext(stripped: _*).raw(args: _*) } + + /** Like the `sm` interpolator, but strips quotation-style delimiter `>` + * and merges the resulting lines into a single line string. + */ + final def sq(args: Any*): String = impl('>', args: _*).linesIterator.mkString } diff --git a/src/reflect/scala/reflect/io/ZipArchive.scala b/src/reflect/scala/reflect/io/ZipArchive.scala index 5f2c988b32c8..b8cdbbacc1b1 100644 --- a/src/reflect/scala/reflect/io/ZipArchive.scala +++ b/src/reflect/scala/reflect/io/ZipArchive.scala @@ -392,7 +392,7 @@ final class ManifestResources(val url: URL) extends ZipArchive(null) { if (!zipEntry.isDirectory) { class FileEntry() extends Entry(zipEntry.getName) { override def lastModified = zipEntry.getTime() - override def input = resourceInputStream(path) + override def input = resourceInputStream(this.path) override def sizeOption = None } val f = new FileEntry() diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index e468c68f8697..505a4b02fc97 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -14,12 +14,6 @@ package scala package reflect package runtime -import scala.language.existentials - -import scala.ref.WeakReference -import scala.collection.mutable.WeakHashMap -import scala.collection.immutable.ArraySeq - import java.io.IOException import java.lang.{ Class => jClass, Package => jPackage } import java.lang.annotation.{ Annotation => jAnnotation } @@ -31,15 +25,19 @@ import java.lang.reflect.{ ParameterizedType, WildcardType, AnnotatedElement } import java.nio.charset.StandardCharsets.UTF_8 +import scala.annotation.nowarn +import scala.collection.immutable.ArraySeq +import scala.collection.mutable.{ListBuffer, WeakHashMap} +import scala.language.existentials +import scala.ref.WeakReference +import scala.reflect.api.TypeCreator import scala.reflect.internal.{ JavaAccFlags, MissingRequirementError } +import scala.runtime.{BoxesRunTime, ClassValueCompat, ScalaRunTime} +import internal.Flags._ import internal.pickling.ByteCodecs import internal.pickling.UnPickler -import scala.collection.mutable.ListBuffer -import internal.Flags._ +import internal.util.StringContextStripMarginOps import ReflectionUtils._ -import scala.annotation.nowarn -import scala.reflect.api.TypeCreator -import scala.runtime.{BoxesRunTime, ClassValueCompat, ScalaRunTime} private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse with TwoWayCaches { thisUniverse: SymbolTable => diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala index ead2054e9092..36a67706e249 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -78,6 +78,10 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => this.InterpolatedString this.RootSelection this.TypedExpectingUnitAttachment + this.FieldTypeInferred + this.LookupAmbiguityWarning + this.PermittedSubclasses + this.PermittedSubclassSymbols this.noPrint this.typeDebug // inaccessible: this.posAssigner @@ -504,6 +508,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => definitions.lazyHolders definitions.LazyRefClass definitions.LazyUnitClass + definitions.RichFloatClass definitions.allRefClasses definitions.UnitClass definitions.ByteClass @@ -528,6 +533,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => definitions.ScalaValueClasses definitions.ScalaValueClassesSet definitions.ScalaNumericValueClassesSet + definitions.ScalaIntegralValueClasses uncurry.VarargsSymbolAttachment uncurry.DesugaredParameterType diff --git a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala index 7d108b355a24..4dd9976f1f51 100644 --- a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala +++ b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala @@ -111,7 +111,7 @@ private[reflect] trait SymbolLoaders { self: SymbolTable => // materializing multiple copies of the same symbol in PackageScope is a very popular bug // this override does its best to guard against it - override def enter[T <: Symbol](sym: T): T = { + override def enter[T <: Symbol](sym: T): sym.type = { // workaround for scala/bug#7728 if (isCompilerUniverse) super.enter(sym) else { diff --git a/src/reflect/scala/reflect/runtime/SynchronizedOps.scala b/src/reflect/scala/reflect/runtime/SynchronizedOps.scala index 3ce1330008f5..2922f13798da 100644 --- a/src/reflect/scala/reflect/runtime/SynchronizedOps.scala +++ b/src/reflect/scala/reflect/runtime/SynchronizedOps.scala @@ -59,7 +59,7 @@ private[reflect] trait SynchronizedOps extends internal.SymbolTable def syncLockSynchronized[T](body: => T): T = if (isCompilerUniverse) body else syncLock.synchronized { body } override def isEmpty: Boolean = syncLockSynchronized { super.isEmpty } override def size: Int = syncLockSynchronized { super.size } - override def enter[T <: Symbol](sym: T): T = syncLockSynchronized { super.enter(sym) } + override def enter[T <: Symbol](sym: T): sym.type = syncLockSynchronized { super.enter(sym) } override def rehash(sym: Symbol, newname: Name) = syncLockSynchronized { super.rehash(sym, newname) } override def unlink(e: ScopeEntry) = syncLockSynchronized { super.unlink(e) } override def unlink(sym: Symbol) = syncLockSynchronized { super.unlink(sym) } diff --git a/src/repl-frontend/scala/tools/nsc/MainGenericRunner.scala b/src/repl-frontend/scala/tools/nsc/MainGenericRunner.scala index edb000bfc88f..96151010f317 100644 --- a/src/repl-frontend/scala/tools/nsc/MainGenericRunner.scala +++ b/src/repl-frontend/scala/tools/nsc/MainGenericRunner.scala @@ -44,6 +44,7 @@ class MainGenericRunner { def process(args: Array[String]): Boolean = { val command = new GenericRunnerCommand(args.toList, (x: String) => errorFn(x)) import command.{settings, howToRun, thingToRun, shortUsageMsg} + import MainGenericRunner.CommandFailure // only created for info message def sampleCompiler = new Global(settings) @@ -75,7 +76,7 @@ class MainGenericRunner { case AsJar => JarRunner.runJar(settings, thingToRun, command.arguments) case Error => - None + Some(CommandFailure) case _ => // We start the repl when no arguments are given. if (settings.Wconf.isDefault && settings.lint.isDefault) { @@ -90,6 +91,7 @@ class MainGenericRunner { runTarget() match { case Some(ScriptCompileError) => false + case Some(CommandFailure) => false case e @ Some(ex) => errorFn("", e) case _ => true } @@ -105,5 +107,8 @@ class MainGenericRunner { } object MainGenericRunner extends MainGenericRunner { + // control indicating command ran but non-zero exit + object CommandFailure extends scala.util.control.ControlThrowable("Command failed") + def main(args: Array[String]): Unit = if (!process(args)) System.exit(1) } diff --git a/src/repl-frontend/scala/tools/nsc/interpreter/jline/Reader.scala b/src/repl-frontend/scala/tools/nsc/interpreter/jline/Reader.scala index 6f3f518205a9..0bc37ec1f374 100644 --- a/src/repl-frontend/scala/tools/nsc/interpreter/jline/Reader.scala +++ b/src/repl-frontend/scala/tools/nsc/interpreter/jline/Reader.scala @@ -21,7 +21,7 @@ import org.jline.reader.impl.{CompletionMatcherImpl, DefaultParser, LineReaderIm import org.jline.terminal.Terminal import java.io.{ByteArrayInputStream, File} -import java.net.{MalformedURLException, URL} +import java.net.{MalformedURLException, URI, URL} import java.util.{List => JList} import scala.io.Source import scala.reflect.internal.Chars @@ -80,7 +80,7 @@ object Reader { sys.props .get("jline.inputrc") .flatMap { path => - try Some(new URL(path)) + try Some(new URI(path).toURL) catch { case _: MalformedURLException => Some(new File(path).toURI.toURL) diff --git a/src/repl/scala/tools/nsc/interpreter/IMain.scala b/src/repl/scala/tools/nsc/interpreter/IMain.scala index b21ea2a9459b..cc9374f761a5 100644 --- a/src/repl/scala/tools/nsc/interpreter/IMain.scala +++ b/src/repl/scala/tools/nsc/interpreter/IMain.scala @@ -347,15 +347,23 @@ class IMain(val settings: Settings, parentClassLoaderOverride: Option[ClassLoade override def translateEnclosingClass(n: String): Option[String] = symbolOfTerm(n).enclClass.toOption map flatPath /** If unable to find a resource foo.class, try taking foo as a symbol in scope - * and use its java class name as a resource to load. - * - * \$intp.classLoader classBytes "Bippy" or \$intp.classLoader getResource "Bippy.class" just work. - */ + * and use its java class name as a resource to load. + * + * \$intp.classLoader classBytes "Bippy" or \$intp.classLoader getResource "Bippy.class" just work. + */ private class TranslatingClassLoader(parent: ClassLoader) extends AbstractFileClassLoader(replOutput.dir, parent) { override protected def findAbstractFile(name: String): AbstractFile = super.findAbstractFile(name) match { case null if _initializeComplete => translateSimpleResource(name).map(super.findAbstractFile).orNull case file => file } + // if the name was mapped by findAbstractFile, supply null name to avoid name check in defineClass + override protected def findClass(name: String): Class[_] = { + val bytes = classBytes(name) + if (bytes.length == 0) + throw new ClassNotFoundException(name) + else + defineClass(/*name=*/null, bytes, 0, bytes.length, protectionDomain) + } } private def makeClassLoader(): AbstractFileClassLoader = new TranslatingClassLoader({ diff --git a/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala b/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala index 73639213cc6f..5208ae1797a5 100644 --- a/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala +++ b/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala @@ -34,7 +34,7 @@ trait MemberHandlers { val front = if (leadingPlus) "+ " else "" front + (xs map string2codeQuoted mkString " + ") } - private implicit def name2string(name: Name) = name.toString + private implicit def name2string(name: Name): String = name.toString /** A traverser that finds all mentioned identifiers, i.e. things * that need to be imported. It might return extra names. diff --git a/src/repl/scala/tools/nsc/interpreter/Power.scala b/src/repl/scala/tools/nsc/interpreter/Power.scala index bf0996559a2e..a0199a407220 100644 --- a/src/repl/scala/tools/nsc/interpreter/Power.scala +++ b/src/repl/scala/tools/nsc/interpreter/Power.scala @@ -13,7 +13,7 @@ package scala.tools.nsc.interpreter import java.io.InputStream -import java.net.URL +import java.net.{URI, URL} import scala.collection.mutable import scala.io.Codec @@ -248,9 +248,9 @@ class Power[ReplValsImpl <: ReplVals : ru.TypeTag: ClassTag](val intp: IMain, re class RichReplString(s: String) { // make an url out of the string def u: URL = ( - if (s contains ":") new URL(s) + if (s contains ":") new URI(s).toURL else if (new java.io.File(s).exists) new java.io.File(s).toURI.toURL - else new URL("http://" + s) + else new URI("http://" + s).toURL ) } class RichInputStream(in: InputStream)(implicit codec: Codec) { @@ -264,7 +264,7 @@ class Power[ReplValsImpl <: ReplVals : ru.TypeTag: ClassTag](val intp: IMain, re trait Implicits1 { // fallback - implicit def replPrinting[T](x: T)(implicit pretty: Prettifier[T] = Prettifier.default[T]) = + implicit def replPrinting[T](x: T)(implicit pretty: Prettifier[T] = Prettifier.default[T]): PrettifierClass[T] = new SinglePrettifierClass[T](x) } trait Implicits2 extends Implicits1 { @@ -288,7 +288,7 @@ class Power[ReplValsImpl <: ReplVals : ru.TypeTag: ClassTag](val intp: IMain, re implicit def replPrettifier[T] : Prettifier[T] = Prettifier.default[T] implicit def replTypeApplication(sym: Symbol): RichSymbol = new RichSymbol(sym) - implicit def replInputStream(in: InputStream)(implicit codec: Codec) = new RichInputStream(in) + implicit def replInputStream(in: InputStream)(implicit codec: Codec): RichInputStream = new RichInputStream(in) implicit def replEnhancedURLs(url: URL)(implicit codec: Codec): RichReplURL = new RichReplURL(url)(codec) } diff --git a/src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala b/src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala index 7111a31c88b6..4c3b8ef1e97f 100644 --- a/src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala +++ b/src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala @@ -98,12 +98,13 @@ trait ReplGlobal extends Global { } override def optimizerClassPath(base: ClassPath): ClassPath = { + val base1 = super.optimizerClassPath(base) settings.outputDirs.getSingleOutput match { - case None => base + case None => base1 case Some(out) => // Make bytecode of previous lines available to the inliner val replOutClasspath = ClassPathFactory.newClassPath(settings.outputDirs.getSingleOutput.get, settings, closeableRegistry) - AggregateClassPath.createAggregate(platform.classPath, replOutClasspath) + AggregateClassPath.createAggregate(base1, replOutClasspath) } } diff --git a/src/repl/scala/tools/nsc/interpreter/ReplVals.scala b/src/repl/scala/tools/nsc/interpreter/ReplVals.scala index 2e485d2ac787..1213c99c117f 100644 --- a/src/repl/scala/tools/nsc/interpreter/ReplVals.scala +++ b/src/repl/scala/tools/nsc/interpreter/ReplVals.scala @@ -47,13 +47,17 @@ class StdReplVals(final val intp: IMain) extends ReplVals { import intp.global.Symbol private val tagFn = ReplVals.mkCompilerTypeFromTag[intp.global.type](global) - implicit def mkCompilerTypeFromTag(sym: Symbol) = tagFn(sym) + implicit def mkCompilerTypeFromTag(sym: Symbol): ATFT[global.type] = tagFn(sym) } final lazy val replImplicits = new ReplImplicits def typed[T <: analyzer.global.Tree](tree: T): T = typer.typed(tree).asInstanceOf[T] } +trait ATFT[G <: Global] { + def apply[M](implicit m1: ru.TypeTag[M]): G#Type + def apply[M1, M2](implicit m1: ru.TypeTag[M1], m2: ru.TypeTag[M2]): G#Type +} object ReplVals { /** Latest attempt to work around the challenge of foo.global.Type @@ -72,7 +76,7 @@ object ReplVals { def compilerTypeFromTag(t: ApiUniverse # WeakTypeTag[_]): Global#Type = definitions.compilerTypeFromTag(t) - class AppliedTypeFromTags(sym: Symbol) { + class AppliedTypeFromTags(sym: Symbol) extends ATFT[T] { def apply[M](implicit m1: ru.TypeTag[M]): Type = if (sym eq NoSymbol) NoType else appliedType(sym, compilerTypeFromTag(m1).asInstanceOf[Type]) diff --git a/src/scaladoc/scala/tools/nsc/doc/Uncompilable.scala b/src/scaladoc/scala/tools/nsc/doc/Uncompilable.scala index df9b5c679ffb..6494a3d49008 100644 --- a/src/scaladoc/scala/tools/nsc/doc/Uncompilable.scala +++ b/src/scaladoc/scala/tools/nsc/doc/Uncompilable.scala @@ -29,7 +29,7 @@ trait Uncompilable { import global.definitions.{ AnyRefClass, AnyRefTpe } import global.rootMirror.RootClass - private implicit def translateName(name: Global#Name) = + private implicit def translateName(name: Global#Name): global.Name = if (name.isTypeName) newTypeName("" + name) else newTermName("" + name) val WaitNames = List("scala", "AnyRef", "wait") diff --git a/src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala b/src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala index 0599bf2e7c79..c6a68e4292dc 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala @@ -95,7 +95,7 @@ class HtmlFactory(val universe: doc.Universe, val reporter: Reporter) { ) final def webjarResources = List( - ("jquery.min.js", "/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=") + ("jquery.min.js", "oP6HI9z1XaZNBrJURtCoUT5SUnxFr8s3BzRl+cbzUq8=") ) /** Generates the Scaladoc site for a model into the site root. diff --git a/src/scaladoc/scala/tools/nsc/doc/html/page/Entity.scala b/src/scaladoc/scala/tools/nsc/doc/html/page/Entity.scala index 91c60fc2104d..66991f9a5bb2 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/page/Entity.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/page/Entity.scala @@ -341,7 +341,7 @@ trait EntityPage extends HtmlPage { val postamble = List(Div(id = "tooltip"), if (Set("epfl", "EPFL").contains(tpl.universe.settings.docfooter.value)) - Div(id = "footer", elems = Txt("Scala programming documentation. Copyright (c) 2002-2022 ") :: A(href = "https://www.epfl.ch", target = "_top", elems = Txt("EPFL")) :: Txt(" and ") :: A(href = "https://www.lightbend.com", target = "_top", elems = Txt("Lightbend")) :: Txt(".")) + Div(id = "footer", elems = Txt("Scala programming documentation. Copyright (c) 2002-2023 ") :: A(href = "https://www.epfl.ch", target = "_top", elems = Txt("EPFL")) :: Txt(" and ") :: A(href = "https://www.lightbend.com", target = "_top", elems = Txt("Lightbend")) :: Txt(".")) else Div(id = "footer", elems = Txt(tpl.universe.settings.docfooter.value))) diff --git a/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala b/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala index b482885ba684..7f44270a7128 100644 --- a/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala +++ b/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala @@ -331,7 +331,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { case "TPL_NAME" => tplName } val patchedString = patches.replaceAllIn(settings.docsourceurl.value, m => java.util.regex.Matcher.quoteReplacement(substitute(m.group(1))) ) - new java.net.URL(patchedString) + new java.net.URI(patchedString).toURL } else None } @@ -927,7 +927,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { } else None def resultType = - makeTypeInTemplateContext(aSym.tpe, inTpl, aSym) + makeTypeInTemplateContext(aSym.tpe, this.inTpl, aSym) def isImplicit = aSym.isImplicit } diff --git a/src/scalap/decoder.properties b/src/scalap/decoder.properties index 290fe83e11bd..b5e00dc083fd 100644 --- a/src/scalap/decoder.properties +++ b/src/scalap/decoder.properties @@ -1,2 +1,2 @@ version.number=2.0.1 -copyright.string=(c) 2002-2022 LAMP/EPFL +copyright.string=(c) 2002-2023 LAMP/EPFL diff --git a/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSig.scala b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSig.scala index e571649454a8..cbc0b53960f5 100644 --- a/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSig.scala +++ b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSig.scala @@ -114,7 +114,7 @@ case class ScalaSig(majorVersion: Int, minorVersion: Int, table: Seq[Int ~ ByteC def parseEntry(index: Int) = applyRule(ScalaSigParsers.parseEntry(ScalaSigEntryParsers.entry)(index)) - implicit def applyRule[A](parser: ScalaSigParsers.Parser[A]) = ScalaSigParsers.expect(parser)(this) + implicit def applyRule[A](parser: ScalaSigParsers.Parser[A]): A = ScalaSigParsers.expect(parser)(this) override def toString = "ScalaSig version " + majorVersion + "." + minorVersion + { for (i <- 0 until table.size) yield "" + i + ":\t" + parseEntry(i) // + "\n\t" + getEntry(i) @@ -165,7 +165,8 @@ object ScalaSigEntryParsers extends RulesWithState with MemoisableRules { def parseEntry[A](parser: EntryParser[A])(index: Int) = (toEntry(index) -~ parser) - implicit def entryType(code: Int) = key filter (_ == code) + type R = scala.tools.scalap.scalax.rules.Rule[ScalaSigEntryParsers.S, ScalaSigEntryParsers.S, Int, Nothing] + implicit def entryType(code: Int): R = key.filter(_ == code) val index = read(_.index) val key = read(_.entryType) diff --git a/src/testkit/scala/tools/testkit/AllocationTest.scala b/src/testkit/scala/tools/testkit/AllocationTest.scala index 37b943215fd7..85d9bbd54edf 100644 --- a/src/testkit/scala/tools/testkit/AllocationTest.scala +++ b/src/testkit/scala/tools/testkit/AllocationTest.scala @@ -132,6 +132,7 @@ trait AllocationTest { private[AllocationTest] def calcAllocationInfo[T](fn: => T, cost: Long, text: String, ignoreEqualCheck: Boolean)(implicit execution: AllocationExecution = AllocationExecution()): AllocationInfo[T] = { val expected = fn val extraText = if (text.isEmpty) "" else s" -- $text" + @annotation.nowarn("cat=deprecation") val id = Thread.currentThread().getId val counts = new Array[Long](execution.executionCount) diff --git a/src/testkit/scala/tools/testkit/AssertUtil.scala b/src/testkit/scala/tools/testkit/AssertUtil.scala index aee72fda791b..041cd077ab45 100644 --- a/src/testkit/scala/tools/testkit/AssertUtil.scala +++ b/src/testkit/scala/tools/testkit/AssertUtil.scala @@ -12,22 +12,24 @@ package scala.tools.testkit -import org.junit.Assert.{assertEquals, assertFalse, assertTrue} +import org.junit.Assert.{assertEquals, assertNotEquals} +import org.junit.Assert.{assertFalse, assertTrue} -import scala.reflect.ClassTag -import scala.runtime.ScalaRunTime.stringOf +import scala.annotation.nowarn import scala.collection.mutable import scala.concurrent.{Await, Awaitable} -import scala.util.chaining._ +import scala.reflect.ClassTag +import scala.runtime.BoxesRunTime +import scala.runtime.ScalaRunTime.stringOf import scala.util.{Failure, Success, Try} +import scala.util.chaining._ import scala.util.control.{ControlThrowable, NonFatal} +import java.lang.ref._ +import java.lang.reflect.{Array => _, _} import java.time.Duration import java.util.concurrent.{CountDownLatch, TimeUnit} import java.util.concurrent.atomic.AtomicReference -import java.lang.ref._ -import java.lang.reflect.{Array => _, _} import java.util.IdentityHashMap -import scala.annotation.nowarn /** This module contains additional higher-level assert statements * that are ultimately based on junit.Assert primitives. @@ -62,6 +64,18 @@ object AssertUtil { def assertEqualStrings(expected: String)(actual: String) = assert(expected == actual, s"Expected:\n${dump(expected)}\nActual:\n${dump(actual)}") + // assertEquals but use BoxesRunTime.equals + // let junit format a message on failure + def assertEqualsAny(expected: Any, actual: Any): Unit = + if (!BoxesRunTime.equals(expected, actual)) assertEquals(expected, actual) + // as a bonus, message is by-name, though retains junit parameter order + def assertEqualsAny(message: => String, expected: Any, actual: Any): Unit = + if (!BoxesRunTime.equals(expected, actual)) assertEquals(message, expected, actual) + def assertNotEqualsAny(expected: Any, actual: Any): Unit = + if (BoxesRunTime.equals(expected, actual)) assertNotEquals(expected, actual) + def assertNotEqualsAny(message: => String, expected: Any, actual: Any): Unit = + if (BoxesRunTime.equals(expected, actual)) assertNotEquals(message, expected, actual) + private final val timeout = 60 * 1000L // wait a minute private implicit class `ref helper`[A](val r: Reference[A]) extends AnyVal { @@ -145,18 +159,34 @@ object AssertUtil { def assertFails[U](checkMessage: String => Boolean)(body: => U): Unit = assertThrows[AssertionError](body, checkMessage) - /** JUnit-style assertion for `IterableLike.sameElements`. + private def orEmpty(b: Boolean)(text: => String): String = if (b) text else "" + + /** JUnit-style assertion for `Seq#sameElements`. + * The `actual` is iterated twice if failure is reported. */ - def assertSameElements[A, B >: A](expected: Iterable[A], actual: Iterable[B], message: String = ""): Unit = - if (!expected.iterator.sameElements(actual)) - fail( - f"${ if (message.nonEmpty) s"$message " else "" }expected:<${ stringOf(expected) }> but was:<${ stringOf(actual) }>" - ) + def assertSameElements[A, B >: A](expected: Seq[A], actual: Iterable[B], message: String = ""): Unit = + if (!expected.sameElements(actual)) + fail(f"${orEmpty(message.nonEmpty)(s"$message ")}expected:<${stringOf(expected)}> but was:<${stringOf(actual)}>") - /** Convenient for testing iterators. + /** Convenience for testing iterators and non-Seqs. + * The `actual` is collected to a `List` for reporting errors. */ - def assertSameElements[A, B >: A](expected: Iterable[A], actual: IterableOnce[B]): Unit = - assertSameElements(expected, actual.iterator.to(List), "") + def assertSameElements[A, B >: A](expected: Seq[A], actual: IterableOnce[B]): Unit = + assertSameElements(expected, actual.iterator.to(Iterable), message = "") + + /** Convenience for testing iterators and non-Seqs. + * The `expected` is collected to a `List` for reporting errors. + */ + def assertSameElements[A, B >: A](expected: IterableOnce[A], actual: IterableOnce[B]): Unit = + assertSameElements(expected.iterator.to(Seq), actual) + + /** Convenience for testing arrays. Avoids warning about implicit conversion to Seq. + */ + def assertSameElements[A, B >: A](expected: Array[A], actual: Array[B]): Unit = + assertSameElements(expected, actual, message = "") + + def assertSameElements[A, B >: A](expected: Array[A], actual: Array[B], message: String): Unit = + assertSameElements(expected.toIndexedSeq, actual.toIndexedSeq, message) /** Value is not strongly reachable from roots after body is evaluated. */ diff --git a/test/async/jvm/toughtype.scala b/test/async/jvm/toughtype.scala index 939d0647e985..9587afa35f19 100644 --- a/test/async/jvm/toughtype.scala +++ b/test/async/jvm/toughtype.scala @@ -199,7 +199,7 @@ package scala.async.run.toughtype { } object FunDep { - implicit def `Something to do with List`[W, S, R](implicit funDep: FunDep[W, S, R]) = + implicit def `Something to do with List`[W, S, R](implicit funDep: FunDep[W, S, R]): FunDep[W,List[S],W] = new FunDep[W, List[S], W] { def method(w: W, l: List[S]) = async { val it = l.iterator diff --git a/test/async/run/string-switch-async.scala b/test/async/run/string-switch-async.scala new file mode 100644 index 000000000000..466f7e3abac4 --- /dev/null +++ b/test/async/run/string-switch-async.scala @@ -0,0 +1,19 @@ +// scalac: -Xasync + +import scala.tools.partest.async.OptionAwait._ +import org.junit.Assert._ + +object Test { + def main(args: Array[String]): Unit = { + assertEquals(Some(""), testSwitch("")) + assertEquals(Some("aa"), testSwitch("a")) + } + + private def testSwitch(s: String) = optionally { + s match { + case "" => "" + case p => + value(Some(p)) + p + } + } +} diff --git a/test/async/run/string-switch-bug.scala b/test/async/run/string-switch-bug.scala new file mode 100644 index 000000000000..daa0020925dc --- /dev/null +++ b/test/async/run/string-switch-bug.scala @@ -0,0 +1,29 @@ +// scalac: -Xasync +import scala.tools.partest.async.OptionAwait._ +import org.junit.Assert._ + +// Scala.js compatible test suite for -Xasync that doesn't use Scala futures +object Test { + def main(args: Array[String]): Unit = { + stringSwitchBug() + } + + private def stringSwitchBug() = { + assertEquals(Some(true), optionally { + val x: String = "" + val as = List("x") + val it = as.iterator + var okay = false + while (it.hasNext) { + val x = it.next() + val res = (x match { + case "x" => + okay = value(Some(1)) == 1 + () + case _ => () + }) + } + okay + }) + } +} diff --git a/test/benchmarks/src/main/scala/scala/collection/immutable/VectorConcatAlignToWorstCaseBenchmark.scala b/test/benchmarks/src/main/scala/scala/collection/immutable/VectorConcatAlignToWorstCaseBenchmark.scala new file mode 100644 index 000000000000..51c269f78712 --- /dev/null +++ b/test/benchmarks/src/main/scala/scala/collection/immutable/VectorConcatAlignToWorstCaseBenchmark.scala @@ -0,0 +1,39 @@ +package scala.collection.immutable + +import org.openjdk.jmh.annotations._ +import org.openjdk.jmh.infra.Blackhole + +import java.util.concurrent.TimeUnit + +@BenchmarkMode(Array(Mode.AverageTime)) +@Fork(1) +@Threads(1) +@Warmup(iterations = 4) +@Measurement(iterations = 5) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) +class VectorConcatAlignToWorstCaseBenchmark { + @Param(Array("1", "15", "31", "33", "63", "65", "127", "255", "513", "1023", "1025", "2047")) // should not be divisible by 32 + var size: Int = _ + @Param(Array("1", "32", "64", "128", "256")) + var sizeDifference: Int = _ + + val o = new AnyRef + + var shorter: Vector[String] = _ + var longer: Vector[String] = _ + + @Setup(Level.Trial) def init(): Unit = { + shorter = Vector.fill(size)("s") + longer = Vector.fill(size + sizeDifference)("l") + } + + @Benchmark def withoutAlignTo(bh: Blackhole): Any = + bh.consume(new VectorBuilder[String]().addAll(shorter).addAll(longer).result()) + + @Benchmark def withAlignTo(bh: Blackhole): Any = + bh.consume(new VectorBuilder[String]().alignTo(shorter.length, longer).addAll(shorter).addAll(longer).result()) + + @Benchmark def concat(bh: Blackhole): Any = + bh.consume(shorter ++ longer) +} diff --git a/test/benchmarks/src/main/scala/scala/collection/immutable/VectorConcatBenchmark.scala b/test/benchmarks/src/main/scala/scala/collection/immutable/VectorConcatBenchmark.scala new file mode 100644 index 000000000000..ad950cb0f865 --- /dev/null +++ b/test/benchmarks/src/main/scala/scala/collection/immutable/VectorConcatBenchmark.scala @@ -0,0 +1,52 @@ +package scala.collection.immutable + +import org.openjdk.jmh.annotations._ +import org.openjdk.jmh.infra.Blackhole + +import java.util.concurrent.TimeUnit + +@BenchmarkMode(Array(Mode.AverageTime)) +@Fork(1) +@Threads(1) +@Warmup(iterations = 4) +@Measurement(iterations = 5) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) +class VectorConcatBenchmark { + @Param(Array("30", "32", "1000", "1024", "30000", "32000", "30720", "32768", "1048576", "33554432")) + var size: Int = _ + + val o = new AnyRef + + var vAligned: Vector[AnyRef] = _ + var vShifted: Vector[AnyRef] = _ + + @Setup(Level.Trial) def init(): Unit = { + vAligned = Vector.fillSparse(size)(o) + vShifted = Vector.fillSparse(size + 5)(o).drop(5) + } + + def concat(bh: Blackhole, a: Vector[AnyRef], b: Vector[AnyRef], times: Int = 10): Any = { + var coll = a + val coll1 = b + var i = 0 + while(i < times) { + coll = coll.appendedAll(coll1) + i += 1 + } + bh.consume(coll) + } + + @Benchmark def concatAlignedAligned(bh: Blackhole): Any = + concat(bh, vAligned, vAligned) + + @Benchmark def concatAlignedShifted(bh: Blackhole): Any = + concat(bh, vShifted, vShifted) + + + @Benchmark def concatMisalignedAligned(bh: Blackhole): Any = + concat(bh, vAligned, vShifted) + + @Benchmark def concatMisalignedShifted(bh: Blackhole): Any = + concat(bh, vShifted, vAligned) +} diff --git a/test/benchmarks/src/main/scala/scala/collection/immutable/VectorConcatBigVectorsBenchmark.scala b/test/benchmarks/src/main/scala/scala/collection/immutable/VectorConcatBigVectorsBenchmark.scala new file mode 100644 index 000000000000..72b3122acc48 --- /dev/null +++ b/test/benchmarks/src/main/scala/scala/collection/immutable/VectorConcatBigVectorsBenchmark.scala @@ -0,0 +1,45 @@ +package scala.collection.immutable + +import org.openjdk.jmh.annotations._ +import org.openjdk.jmh.infra.Blackhole + +import java.util.concurrent.TimeUnit + +@BenchmarkMode(Array(Mode.AverageTime)) +@Fork(1) +@Threads(1) +@Warmup(iterations = 4) +@Measurement(iterations = 5) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) +class VectorConcatBigVectorsBenchmark { + val size: Int = 1000000 + + @Param(Array("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10")) + var tenthsSplit: Int = _ + val o = new AnyRef + + var l: Vector[AnyRef] = _ + var lShifted: Vector[AnyRef] = _ + var r: Vector[AnyRef] = _ + var rNew: Vector[AnyRef] = _ + + @Setup(Level.Trial) def init(): Unit = { + val split = size * tenthsSplit / 10 + + val (a, b) = Vector.fillSparse(size)(o).splitAt(split) + l = a; r = b + rNew = Vector.fillSparse(size - split)(o) + lShifted = Vector.fillSparse(split + 5)(o).drop(5) + } + + @Benchmark def concatAligned(bh: Blackhole): Any = + bh.consume(l ++ r) + + @Benchmark def concatSemiAligned(bh: Blackhole): Any = + bh.consume(l ++ rNew) + + @Benchmark def concatMisaligned(bh: Blackhole): Any = + bh.consume(lShifted ++ r) + +} diff --git a/test/benchmarks/src/main/scala/scala/collection/mutable/LinkedHashMapBenchmark2.scala b/test/benchmarks/src/main/scala/scala/collection/mutable/LinkedHashMapBenchmark2.scala new file mode 100644 index 000000000000..6725141f3b41 --- /dev/null +++ b/test/benchmarks/src/main/scala/scala/collection/mutable/LinkedHashMapBenchmark2.scala @@ -0,0 +1,216 @@ +package scala.collection.mutable + +import org.openjdk.jmh.annotations._ +import org.openjdk.jmh.infra._ +import org.openjdk.jmh.runner.IterationType +import benchmark._ +import java.util.concurrent.TimeUnit +import java.util.{ LinkedHashMap => JLHashMap, LinkedHashSet => JLHashSet } + +@BenchmarkMode(Array(Mode.AverageTime)) +@Fork(2) +@Threads(1) +@Warmup(iterations = 20) +@Measurement(iterations = 20) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) +class LinkedHashMapBenchmark2 { + @Param(Array(/*"0", "1",*/ "10", "100", "1000", "10000")) + var size: Int = _ + @Param(Array("true")) + var stringsOnly = false + + class Collider(val x: Any, val h: Int) { + override def hashCode: Int = h + override def equals(o: Any): Boolean = o match { + case o: Collider => x == o.x + case _ => false + } + } + + var existingKeys: Array[Any] = _ + var existingKVs: ArrayBuffer[(Any, Any)] = _ + var missingKeys: Array[Any] = _ + var s1: LinkedHashSet[Any] = _ + var m1: LinkedHashMap[Any, Any] = _ + var j1: JLHashMap[Any, Any] = new JLHashMap[Any, Any] + var j2: JLHashSet[Any] = new JLHashSet[Any] + var colliders: Array[Collider] = _ + + @Setup(Level.Trial) def init: Unit = { + existingKeys = (0 until size).map(i => (i % 4) match { + case _ if stringsOnly => i.toString + case 0 => i.toString + case 1 => i.toChar + case 2 => i.toDouble + case 3 => i.toInt + }).toArray + existingKVs = ArrayBuffer.from(existingKeys.iterator.map(k => (k, k))) + missingKeys = (size until (2 * size.max(100))).toArray.map(_.toString) + s1 = LinkedHashSet.from(existingKeys) + m1 = LinkedHashMap.from(existingKVs) + m1.foreach { case (k, v) => j1.put(k, v) } + s1.foreach({case k => j2.add(k)}) + colliders = existingKeys.map(k => new Collider(k, k.hashCode & 0x1111)) + } + + @Benchmark def lhsFillRegular(bh: Blackhole): Unit = { + val h = new LinkedHashSet[Any] + existingKeys.foreach(k => h.addOne(k)) + bh.consume(h) + } + + @Benchmark def lhsFillColliding(bh: Blackhole): Unit = { + val h = new LinkedHashSet[Any] + colliders.foreach(k => h.addOne(k)) + bh.consume(h) + } + + @Benchmark def lhsBuild(bh: Blackhole): Unit = + bh.consume(LinkedHashSet.from(existingKeys)) + + @Benchmark def lhsIterate(bh: Blackhole): Unit = { + val it = s1.iterator + while(it.hasNext) bh.consume(it.next()) + } + + @Benchmark def lhsContainsTrue(bh: Blackhole): Unit = { + var i = 0 + while (i < size) { + bh.consume(s1.contains(existingKeys(i))) + i += 1 + } + } + + @Benchmark def lhsContainsFalse(bh: Blackhole): Unit = { + var i = 0 + while (i < size.max(100)) { + bh.consume(s1.contains(missingKeys(i))) + i += 1 + } + } + + @Benchmark def lhmFillRegular(bh: Blackhole): Unit = { + val h = new LinkedHashMap[Any, Any] + existingKeys.foreach(k => h.put(k, k)) + bh.consume(h) + } + + @Benchmark def lhmFillColliding(bh: Blackhole): Unit = { + val h = new LinkedHashMap[Any, Any] + colliders.foreach(k => h.put(k, k)) + bh.consume(h) + } + + @Benchmark def lhmBuild(bh: Blackhole): Unit = + bh.consume(LinkedHashMap.from(existingKVs)) + + @Benchmark def lhmIterateKeys(bh: Blackhole): Unit = { + val it = m1.keysIterator + while(it.hasNext) bh.consume(it.next()) + } + + @Benchmark def lhmIterateEntries(bh: Blackhole): Unit = { + val it = m1.iterator + while(it.hasNext) bh.consume(it.next()) + } + + @Benchmark def lhmGetExisting(bh: Blackhole): Unit = { + var i = 0 + while (i < size) { + bh.consume(m1.apply(existingKeys(i))) + i += 1 + } + } + + @Benchmark def lhmGetNone(bh: Blackhole): Unit = { + var i = 0 + while (i < size.max(100)) { + bh.consume(m1.get(missingKeys(i))) + i += 1 + } + } + + @Benchmark def javalhmFillRegular(bh: Blackhole): Unit = { + val h = new JLHashMap[Any, Any] + existingKeys.foreach(k => h.put(k, k)) + bh.consume(h) + } + + @Benchmark def javalhmFillColliding(bh: Blackhole): Unit = { + val h = new JLHashMap[Any, Any] + colliders.foreach(k => h.put(k, k)) + bh.consume(h) + } + + @Benchmark def javalhmBuild(bh: Blackhole): Unit = { + val h = new JLHashMap[Any, Any](((existingKeys.length+1).toDouble/0.75).toInt, 0.75f) + existingKeys.foreach(k => h.put(k, k)) + bh.consume(h) + } + + @Benchmark def javalhmIterateKeys(bh: Blackhole): Unit = { + val it = j1.keySet().iterator() + while(it.hasNext) bh.consume(it.next()) + } + + @Benchmark def javalhmIterateEntries(bh: Blackhole): Unit = { + val it = j1.entrySet().iterator() + while(it.hasNext) bh.consume(it.next()) + } + + @Benchmark def javalhmGetExisting(bh: Blackhole): Unit = { + var i = 0 + while (i < size) { + bh.consume(j1.get(existingKeys(i))) + i += 1 + } + } + + @Benchmark def javalhmGetNone(bh: Blackhole): Unit = { + var i = 0 + while (i < size.max(100)) { + bh.consume(j1.get(missingKeys(i))) + i += 1 + } + } + @Benchmark def javalhsFillRegular(bh: Blackhole): Unit = { + val h = new JLHashSet[Any] + existingKeys.foreach(k => h.add(k)) + bh.consume(h) + } + + @Benchmark def javalhsFillColliding(bh: Blackhole): Unit = { + val h = new JLHashSet[Any] + colliders.foreach(k => h.add(k)) + bh.consume(h) + } + + @Benchmark def javalhsBuild(bh: Blackhole): Unit = { + val h = new JLHashSet[Any](((existingKeys.length+1).toDouble/0.75).toInt, 0.75f) + existingKeys.foreach(k => h.add(k)) + bh.consume(h) + } + + @Benchmark def javalhsIterate(bh: Blackhole): Unit = { + val it = j2.iterator() + while(it.hasNext) bh.consume(it.next()) + } + + + @Benchmark def javalhsContainsTrue(bh: Blackhole): Unit = { + var i = 0 + while (i < size) { + bh.consume(j2.contains(existingKeys(i))) + i += 1 + } + } + + @Benchmark def javalhsContainsFalse(bh: Blackhole): Unit = { + var i = 0 + while (i < size.max(100)) { + bh.consume(j2.contains(missingKeys(i))) + i += 1 + } + } +} diff --git a/test/files/instrumented/t12201.check b/test/files/instrumented/t12201.check deleted file mode 100644 index ba4c268ba7ac..000000000000 --- a/test/files/instrumented/t12201.check +++ /dev/null @@ -1,3 +0,0 @@ -Method call statistics: - 1 scala/runtime/BoxedUnit.()V - 1 scala/runtime/BoxedUnit.()V diff --git a/test/files/instrumented/t12201.scala b/test/files/instrumented/t12201.scala index a5a1d1860bdb..f38ae90f55c7 100644 --- a/test/files/instrumented/t12201.scala +++ b/test/files/instrumented/t12201.scala @@ -1,7 +1,10 @@ import scala.tools.partest.instrumented.Instrumentation._ object Test { + @noinline def discard(x: Any) = () + def main(args: Array[String]): Unit = { + discard((): Any) // ensure BoxedUnit is loaded; only under -opt is it not loaded before this method startProfiling() // to optimized @@ -24,6 +27,6 @@ object Test { val k = Array[Unit](()) stopProfiling() - printStatistics() + assert(getStatistics.isEmpty) } } diff --git a/test/files/jvm/.gitignore b/test/files/jvm/.gitignore new file mode 100644 index 000000000000..21888692e907 --- /dev/null +++ b/test/files/jvm/.gitignore @@ -0,0 +1 @@ +natives.o diff --git a/test/files/jvm/deprecation.check b/test/files/jvm/deprecation.check index a135a2ef4bcb..2997495c7b9a 100644 --- a/test/files/jvm/deprecation.check +++ b/test/files/jvm/deprecation.check @@ -1,2 +1,28 @@ -Note: deprecation/Use_2.java uses or overrides a deprecated API. -Note: Recompile with -Xlint:deprecation for details. +Test_1.scala:7: warning: variable i in class Defs is deprecated + val u = d.i + 1 + ^ +Test_1.scala:8: warning: variable i in class Defs is deprecated + d.i = 2 + ^ +Test_1.scala:9: warning: method bar in class Defs is deprecated + val v = d.bar() + ^ +Test_1.scala:10: warning: class Inner in class Defs is deprecated + val i = new d.Inner + ^ +deprecation/Use_2.java:7: warning: [deprecation] Test.Inner in Test has been deprecated + Test.Inner a = u.new Inner(); + ^ +deprecation/Use_2.java:7: warning: [deprecation] Test.Inner in Test has been deprecated + Test.Inner a = u.new Inner(); + ^ +deprecation/Use_2.java:8: warning: [deprecation] f() in Test.Inner has been deprecated + int i = a.f(); + ^ +deprecation/Use_2.java:9: warning: [deprecation] g() in Test.Inner has been deprecated + int j = a.g(); + ^ +deprecation/Use_2.java:10: warning: [deprecation] g_$eq(int) in Test.Inner has been deprecated + a.g_$eq(5); + ^ +5 warnings diff --git a/test/files/jvm/deprecation/Test_1.scala b/test/files/jvm/deprecation/Test_1.scala index 26c636b4755b..04c9b22365be 100644 --- a/test/files/jvm/deprecation/Test_1.scala +++ b/test/files/jvm/deprecation/Test_1.scala @@ -1,3 +1,6 @@ + +// scalac: -Xlint:deprecation + class Test { def test: Unit = { val d = new Defs diff --git a/test/files/jvm/deprecation/Use_2.java b/test/files/jvm/deprecation/Use_2.java index 65da8a8fac90..36d5b4ffed9a 100644 --- a/test/files/jvm/deprecation/Use_2.java +++ b/test/files/jvm/deprecation/Use_2.java @@ -1,3 +1,6 @@ + +// javac: -Xlint:deprecation + class Use_2 { public int test() { Test u = new Test(); @@ -7,4 +10,4 @@ public int test() { a.g_$eq(5); return i + j; } -} \ No newline at end of file +} diff --git a/test/files/jvm/future-spec/main.scala b/test/files/jvm/future-spec/main.scala index 411a6333992f..dda7cfbb4e47 100644 --- a/test/files/jvm/future-spec/main.scala +++ b/test/files/jvm/future-spec/main.scala @@ -17,9 +17,10 @@ object Test { } trait Features { - implicit def implicitously = scala.language.implicitConversions - implicit def reflectively = scala.language.reflectiveCalls - implicit def postulously = scala.language.postfixOps + import languageFeature._ + implicit def implicitously: implicitConversions = scala.language.implicitConversions + implicit def reflectively: reflectiveCalls = scala.language.reflectiveCalls + implicit def postulously: postfixOps = scala.language.postfixOps } @@ -40,7 +41,9 @@ trait MinimalScalaTest extends Output with Features with Vigil { if (throwables.nonEmpty) println(buffer.toString) } - implicit def stringops(s: String) = new { + type Ops = AnyRef{def should[U](snippets: => U): U; def in[U](snippet: => U): scala.collection.mutable.IndexedSeq[_ >: Char with Throwable] with scala.collection.mutable.AbstractSeq[_ >: Char with Throwable] with scala.collection.mutable.Growable[Char with Throwable] with java.io.Serializable} + + implicit def stringops(s: String): Ops = new { def should[U](snippets: => U) = { bufferPrintln(s + " should:") @@ -62,7 +65,8 @@ trait MinimalScalaTest extends Output with Features with Vigil { } - implicit def objectops(obj: Any) = new { + type OOps = AnyRef{def mustBe(other: Any): Unit; def mustEqual(other: Any): Unit} + implicit def objectops(obj: Any): OOps = new { def mustBe(other: Any) = assert(obj == other, s"$obj is not $other") def mustEqual(other: Any) = mustBe(other) diff --git a/test/files/jvm/interpreter.check b/test/files/jvm/interpreter.check index 3acf90e25db0..2e9430f729c4 100644 --- a/test/files/jvm/interpreter.check +++ b/test/files/jvm/interpreter.check @@ -87,6 +87,8 @@ scala> case class Bar(n: Int) class Bar scala> implicit def foo2bar(foo: Foo) = Bar(foo.n) + ^ + warning: Implicit definition should have explicit type (inferred Bar) warning: 1 feature warning; for details, enable `:setting -feature` or `:replay -feature` def foo2bar(foo: Foo): Bar diff --git a/test/files/jvm/libnatives-arm.jnilib b/test/files/jvm/libnatives-arm.jnilib new file mode 100755 index 000000000000..114622219efd Binary files /dev/null and b/test/files/jvm/libnatives-arm.jnilib differ diff --git a/test/files/jvm/libnatives.jnilib b/test/files/jvm/libnatives-x86.jnilib similarity index 100% rename from test/files/jvm/libnatives.jnilib rename to test/files/jvm/libnatives-x86.jnilib diff --git a/test/files/jvm/mkLibNatives.sh b/test/files/jvm/mkLibNatives.sh index 537187eeddbb..38c782985890 100755 --- a/test/files/jvm/mkLibNatives.sh +++ b/test/files/jvm/mkLibNatives.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/sh -e ############################################################################## # Author : Stephane Micheloud @@ -11,15 +11,25 @@ debug= cygwin=false; -darwin=false; +darwin_x86=false; +darwin_arm=false; case "`uname`" in - CYGWIN*) cygwin=true ;; - Darwin*) darwin=true ;; + CYGWIN*) cygwin=true ;; + Darwin*) case "`uname -m`" in + x86_64*) darwin_x86=true ;; + arm64*) darwin_arm=true ;; + esac esac CLASS_NAME=Test\$ CLASS_DIR=natives-jvm.obj +if [ ! -f "${CLASS_DIR}/${CLASS_NAME}.class" ]; then + echo "first you need to run this within sbt:" + echo "partest --debug test/files/jvm/natives.scala" + exit +fi + OBJ_NAME=natives LIB_NAME=libnatives @@ -34,13 +44,31 @@ fi JAVAH=${JAVA_HOME}/bin/javah JAVAH_OPTIONS="-jni -force -classpath ${CLASS_DIR} -o ${OBJ_NAME}.h" +if [ ! -f "${JAVAH}" ]; then + # Oracle removed `javah`. The replacement is `javac -h`, but + # requiring 8 seems fine for now, especially since we commit + # the generated files to version control, so this script hardly + # ever needs to be run at all + echo "this script only works on Java 8" + exit +fi + CC=gcc -if $darwin; then - CC_OPTIONS="-c -arch ppc -arch i386 -arch x86_64" +if $darwin_x86; then + # not sure if this stuff still works on current MacOS -- the + # generated .jnilib file is already in version control and we're not + # likely to need to generate it again, so I didn't bother to see if this + # needs the same changes that are in the darwin_arm section below + CC_OPTIONS="-c -arch i386 -arch x86_64" CC_INCLUDES="-I/System/Library/Frameworks/JavaVM.framework/Headers" LNK_OPTIONS="-dynamiclib -framework JavaVM" - FULL_LIB_NAME=${LIB_NAME}.jnilib + FULL_LIB_NAME=${LIB_NAME}-x86.jnilib +elif $darwin_arm; then + CC_OPTIONS="-c -arch arm64" + CC_INCLUDES="-I${JAVA_HOME}/include -I${JAVA_HOME}/include/darwin" + LNK_OPTIONS="-L${JAVA_HOME}/jre/lib/server -dynamiclib -ljvm" + FULL_LIB_NAME=${LIB_NAME}-arm.jnilib else CC_OPTIONS=-c CC_INCLUDES="-I${JAVA_HOME}/include -I${JAVA_HOME}/include/${OSTYPE}" diff --git a/test/files/jvm/natives.scala b/test/files/jvm/natives.scala index 15a8b298f343..f1b74b74861d 100644 --- a/test/files/jvm/natives.scala +++ b/test/files/jvm/natives.scala @@ -4,14 +4,18 @@ object Test { //println("java.library.path=" + System.getProperty("java.library.path")) - val sysWordSize = System.getProperty("sun.arch.data.model", "32") - val sysType = System.getProperty("os.name") + val os = System.getProperty("os.name") + val arch = System.getProperty("os.arch") - val libName = - if (sysType == "Mac OS X") - "natives" - else - "natives-" + sysWordSize + val libName = (os, arch) match { + case ("Mac OS X", "aarch64") => + "natives-arm" + case ("Mac OS X", "x86_64") => + "natives-x86" + case _ => + val wordSize = System.getProperty("sun.arch.data.model", "32") + "natives-" + wordSize + } System.loadLibrary(libName) diff --git a/test/files/jvm/protectedacc.scala b/test/files/jvm/protectedacc.scala index 43d218fa89fd..51a50bed421c 100644 --- a/test/files/jvm/protectedacc.scala +++ b/test/files/jvm/protectedacc.scala @@ -47,7 +47,7 @@ package p { abstract class PolyA[a] { protected def m(x: a): Unit; - class B { + class BB { trait Node { def s: String = ""; @@ -134,7 +134,7 @@ package p { abstract class X[T] extends PolyA[T] { - trait Inner extends B { + trait Inner extends BB { def self: T; def self2: Node; def getB: Inner; diff --git a/test/files/jvm/scala-concurrent-tck.scala b/test/files/jvm/scala-concurrent-tck.scala index d8eafde41b16..f31d8b2a5757 100644 --- a/test/files/jvm/scala-concurrent-tck.scala +++ b/test/files/jvm/scala-concurrent-tck.scala @@ -9,7 +9,7 @@ import scala.concurrent.{ Awaitable, blocking } -import scala.annotation.tailrec +import scala.annotation.{nowarn, tailrec} import scala.concurrent.duration._ import scala.reflect.{classTag, ClassTag} import scala.tools.testkit.AssertUtil.{Fast, Slow, assertThrows, waitFor, waitForIt} @@ -864,7 +864,8 @@ class Exceptions extends TestBase { class GlobalExecutionContext extends TestBase { import ExecutionContext.Implicits._ - + + @nowarn("cat=deprecation") // Thread.getID is deprecated since JDK 19 def testNameOfGlobalECThreads(): Unit = once { done => Future({ val expectedName = "scala-execution-context-global-"+ Thread.currentThread.getId @@ -1055,7 +1056,7 @@ class ExecutionContextPrepare extends TestBase { delegate.reportFailure(t) } - implicit val ec = new PreparingExecutionContext + implicit val ec: ExecutionContext = new PreparingExecutionContext def testOnComplete(): Unit = once { done => diff --git a/test/files/jvm/t7253.check b/test/files/jvm/t7253.check deleted file mode 100644 index 43f53aba123d..000000000000 --- a/test/files/jvm/t7253.check +++ /dev/null @@ -1 +0,0 @@ -bytecode identical diff --git a/test/files/jvm/t7253/test.scala b/test/files/jvm/t7253/test.scala index bb51650e8a02..c506a116ae0c 100644 --- a/test/files/jvm/t7253/test.scala +++ b/test/files/jvm/t7253/test.scala @@ -1,29 +1,20 @@ -import scala.tools.partest.BytecodeTest -import scala.tools.testkit.ASMConverters +// scalac: -Werror -Xlint -import scala.tools.nsc.util.JavaClassPath -import java.io.InputStream -import scala.tools.asm -import asm.ClassReader -import asm.tree.{ClassNode, InsnList} -import scala.collection.JavaConverters._ +import scala.tools.asm.Opcodes +import scala.tools.partest.BytecodeTest +import scala.tools.testkit.ASMConverters._ object Test extends BytecodeTest { - import ASMConverters._ - def show: Unit = { - val instrBaseSeqs = Seq("ScalaClient_1", "JavaClient_1") map (name => instructionsFromMethod(getMethod(loadClassNode(name), "foo"))) - val instrSeqs = instrBaseSeqs map (_ filter isInvoke) - cmpInstructions(instrSeqs(0), instrSeqs(1)) + def show(): Unit = { + def fooOf(name: String) = instructionsFromMethod(getMethod(loadClassNode(name), "foo")).filter(isInvoke) + cmpInstructions(fooOf("ScalaClient_1"), fooOf("JavaClient_1")) } - def cmpInstructions(isa: List[Instruction], isb: List[Instruction]) = { - if (isa == isb) println("bytecode identical") - else diffInstructions(isa, isb) - } + def cmpInstructions(isa: List[Instruction], isb: List[Instruction]) = + if (!isa.sameElements(isb)) + diffInstructions(isa, isb) - def isInvoke(node: Instruction): Boolean = { - val opcode = node.opcode - (opcode == "INVOKEVIRTUAL") || (opcode == "INVOKEINTERFACE") - } + def isInvoke(node: Instruction): Boolean = + node.opcode == Opcodes.INVOKEVIRTUAL || node.opcode == Opcodes.INVOKEINTERFACE } diff --git a/test/files/neg/dbldef.check b/test/files/neg/dbldef.check index 0f7b02a479f8..30b825a73489 100644 --- a/test/files/neg/dbldef.check +++ b/test/files/neg/dbldef.check @@ -6,7 +6,4 @@ dbldef.scala:1: error: type mismatch; required: Int case class test0(x: Int, x: Float) ^ -dbldef.scala:1: error: in class test0, multiple overloaded alternatives of x define default arguments -case class test0(x: Int, x: Float) - ^ -3 errors +2 errors diff --git a/test/files/neg/dotless-targs-a.check b/test/files/neg/dotless-targs-a.check new file mode 100644 index 000000000000..e16d2b2e4c70 --- /dev/null +++ b/test/files/neg/dotless-targs-a.check @@ -0,0 +1,15 @@ +dotless-targs-a.scala:4: warning: type application will be disallowed for infix operators + def fn2 = List apply[Int] 2 + ^ +dotless-targs-a.scala:9: warning: type application will be disallowed for infix operators + def h1 = List apply[List[Int]] (List(1), List(2)) mapConserve[List[Any]] (x => x) + ^ +dotless-targs-a.scala:9: warning: type application will be disallowed for infix operators + def h1 = List apply[List[Int]] (List(1), List(2)) mapConserve[List[Any]] (x => x) + ^ +dotless-targs-a.scala:9: warning: multiarg infix syntax looks like a tuple and will be deprecated + def h1 = List apply[List[Int]] (List(1), List(2)) mapConserve[List[Any]] (x => x) + ^ +error: No warnings can be incurred under -Werror. +4 warnings +1 error diff --git a/test/files/neg/dotless-targs-a.scala b/test/files/neg/dotless-targs-a.scala new file mode 100644 index 000000000000..1112f676c2da --- /dev/null +++ b/test/files/neg/dotless-targs-a.scala @@ -0,0 +1,10 @@ +// scalac: -Werror -Xlint -Yrangepos:false +class A { + def fn1 = List apply 1 + def fn2 = List apply[Int] 2 + + def g1: Char = "g1" toList 0 + def g2: Char = "g2" apply 1 + + def h1 = List apply[List[Int]] (List(1), List(2)) mapConserve[List[Any]] (x => x) +} diff --git a/test/files/neg/dotless-targs-b.check b/test/files/neg/dotless-targs-b.check new file mode 100644 index 000000000000..6aafb4db9157 --- /dev/null +++ b/test/files/neg/dotless-targs-b.check @@ -0,0 +1,10 @@ +dotless-targs-b.scala:4: error: type application is not allowed for infix operators + def fn2 = List apply[Int] 2 + ^ +dotless-targs-b.scala:9: error: type application is not allowed for infix operators + def h1 = List apply[List[Int]] (List(1), List(2)) mapConserve[List[Any]] (x => x) + ^ +dotless-targs-b.scala:9: error: type application is not allowed for infix operators + def h1 = List apply[List[Int]] (List(1), List(2)) mapConserve[List[Any]] (x => x) + ^ +3 errors diff --git a/test/files/neg/dotless-targs-b.scala b/test/files/neg/dotless-targs-b.scala new file mode 100644 index 000000000000..d54344238a9a --- /dev/null +++ b/test/files/neg/dotless-targs-b.scala @@ -0,0 +1,10 @@ +// scalac: -Werror -Xlint -Xsource:3 -Yrangepos:false +class A { + def fn1 = List apply 1 + def fn2 = List apply[Int] 2 + + def g1: Char = "g1" toList 0 + def g2: Char = "g2" apply 1 + + def h1 = List apply[List[Int]] (List(1), List(2)) mapConserve[List[Any]] (x => x) +} diff --git a/test/files/neg/dotless-targs-ranged-a.check b/test/files/neg/dotless-targs-ranged-a.check new file mode 100644 index 000000000000..870784528580 --- /dev/null +++ b/test/files/neg/dotless-targs-ranged-a.check @@ -0,0 +1,16 @@ +dotless-targs-ranged-a.scala:4: error: type application is not allowed for infix operators + def fn2 = List apply[Int] 2 + ^ +dotless-targs-ranged-a.scala:9: error: type application is not allowed for infix operators + def h1 = List apply[List[Int]] (List(1), List(2)) mapConserve[List[Any]] (x => x) + ^ +dotless-targs-ranged-a.scala:9: error: type application is not allowed for infix operators + def h1 = List apply[List[Int]] (List(1), List(2)) mapConserve[List[Any]] (x => x) + ^ +dotless-targs-ranged-a.scala:13: error: type application is not allowed for infix operators + def eval = 1 ->[Int] 2 + ^ +dotless-targs-ranged-a.scala:14: error: type application is not allowed for infix operators + def evil = new A() op [Int, String ] 42 + ^ +5 errors diff --git a/test/files/neg/dotless-targs-ranged-a.scala b/test/files/neg/dotless-targs-ranged-a.scala new file mode 100644 index 000000000000..9e014c1f0386 --- /dev/null +++ b/test/files/neg/dotless-targs-ranged-a.scala @@ -0,0 +1,16 @@ +// scalac: -Xlint -Xsource:3 -Yrangepos:true +class A { + def fn1 = List apply 1 + def fn2 = List apply[Int] 2 + + def g1: Char = "g1" toList 0 + def g2: Char = "g2" apply 1 + + def h1 = List apply[List[Int]] (List(1), List(2)) mapConserve[List[Any]] (x => x) + + def op[A, B](i: Int): Int = 2*i + + def eval = 1 ->[Int] 2 + def evil = new A() op [Int, String ] 42 +} + diff --git a/test/files/neg/dotless-targs.check b/test/files/neg/dotless-targs.check index 4b22dd63e521..e85ded85bb4c 100644 --- a/test/files/neg/dotless-targs.check +++ b/test/files/neg/dotless-targs.check @@ -1,4 +1,4 @@ -dotless-targs.scala:2: error: type application is not allowed for postfix operators +dotless-targs.scala:4: error: type application is not allowed for postfix operators def f1 = "f1" isInstanceOf[String] // not ok ^ 1 error diff --git a/test/files/neg/dotless-targs.scala b/test/files/neg/dotless-targs.scala index eff63cbec4f9..70e01c9a00a4 100644 --- a/test/files/neg/dotless-targs.scala +++ b/test/files/neg/dotless-targs.scala @@ -1,3 +1,5 @@ +// scalac: -Xsource:3 -language:postfixOps +// class A { def f1 = "f1" isInstanceOf[String] // not ok def f2 = "f2".isInstanceOf[String] // ok diff --git a/test/files/neg/error_dependentMethodTpeConversionToFunction.check b/test/files/neg/error_dependentMethodTpeConversionToFunction.check deleted file mode 100644 index 58da78fe9452..000000000000 --- a/test/files/neg/error_dependentMethodTpeConversionToFunction.check +++ /dev/null @@ -1,4 +0,0 @@ -error_dependentMethodTpeConversionToFunction.scala:4: error: method with dependent type (x: AnyRef): x.type cannot be converted to function value - val x: Any => Any = foo - ^ -1 error diff --git a/test/files/neg/error_dependentMethodTpeConversionToFunction.scala b/test/files/neg/error_dependentMethodTpeConversionToFunction.scala deleted file mode 100644 index d0c4cb48a8e0..000000000000 --- a/test/files/neg/error_dependentMethodTpeConversionToFunction.scala +++ /dev/null @@ -1,5 +0,0 @@ -// test DependentMethodTpeConversionToFunctionError -object Test { - def foo(x: AnyRef): x.type = x - val x: Any => Any = foo -} diff --git a/test/files/neg/i10715a.check b/test/files/neg/i10715a.check new file mode 100644 index 000000000000..ca65c021b764 --- /dev/null +++ b/test/files/neg/i10715a.check @@ -0,0 +1,38 @@ +i10715a.scala:16: error: type mismatch; + found : Int + required: String + c.f: String // error + ^ +i10715a.scala:17: error: polymorphic expression cannot be instantiated to expected type; + found : [A]Int + required: String + c.g: String // error + ^ +i10715a.scala:18: error: value bad is not a member of Int + c.f.bad // error + ^ +i10715a.scala:19: error: value bad is not a member of Int + c.g.bad // error + ^ +i10715a.scala:21: error: type mismatch; + found : String("") + required: Int + c.f("") // error + ^ +i10715a.scala:22: error: type mismatch; + found : String("") + required: Int + c.g("") // error + ^ +i10715a.scala:23: error: overloaded method g with alternatives: + (x: Int)Parent + Int + cannot be applied to (String) + c.g[Int]("") // error + ^ +i10715a.scala:24: error: type mismatch; + found : Int + required: String => String + c.g[Int]: (String => String) // error + ^ +8 errors diff --git a/test/files/neg/i10715a.scala b/test/files/neg/i10715a.scala new file mode 100644 index 000000000000..14bcc5a3a4cc --- /dev/null +++ b/test/files/neg/i10715a.scala @@ -0,0 +1,27 @@ +class Parent { + def f(x: Int): Parent = ??? + def f: Int = 0 + + def g[A](x: Int): Parent = ??? + def g[A]: Int = 0 +} + +class Sub extends Parent { + override def f(x: Int): Parent = ??? + override def g[A](x: Int): Parent = ??? +} + +class C { + def bad(c: Sub): Unit = { + c.f: String // error + c.g: String // error + c.f.bad // error + c.g.bad // error + + c.f("") // error + c.g("") // error + c.g[Int]("") // error + c.g[Int]: (String => String) // error + c.g[Int]: (Int => Parent) // ok + } +} diff --git a/test/files/neg/i10715b.check b/test/files/neg/i10715b.check new file mode 100644 index 000000000000..577d8e421fd0 --- /dev/null +++ b/test/files/neg/i10715b.check @@ -0,0 +1,7 @@ +i10715b.scala:12: error: ambiguous reference to overloaded definition, +both method f in class Sub of type (x: Int)(implicit s: String): Unit +and method f in class Sub of type (x: Int): Unit +match argument types (Int) + def bad(c: Sub): Unit = c.f(1) // error: ambiguous overload + ^ +1 error diff --git a/test/files/neg/i10715b.scala b/test/files/neg/i10715b.scala new file mode 100644 index 000000000000..8cc8076ac193 --- /dev/null +++ b/test/files/neg/i10715b.scala @@ -0,0 +1,13 @@ +class Parent { + def f(x: Int): Unit = () + def f: Int = 0 +} + +class Sub extends Parent { + override def f(x: Int): Unit = () + def f(x: Int)(implicit s: String): Unit = () +} + +class C { + def bad(c: Sub): Unit = c.f(1) // error: ambiguous overload +} diff --git a/test/files/neg/implicit-log.scala b/test/files/neg/implicit-log.scala index f77085e3c2af..7af20610f64d 100644 --- a/test/files/neg/implicit-log.scala +++ b/test/files/neg/implicit-log.scala @@ -37,8 +37,8 @@ object Test1579 { class Query[E](val value: E) class Invoker(q: Any) { val foo = null } - implicit def unwrap[C](q: Query[C]) = q.value - implicit def invoker(q: Query[Column]) = new Invoker(q) + implicit def unwrap[C](q: Query[C]): C = q.value + implicit def invoker(q: Query[Column]): Invoker = new Invoker(q) val q = new Query(new Column) q.foo @@ -50,9 +50,9 @@ object Test1625 { def unwrap() = x } - implicit def byName[A](x: => A) = new Wrapped(x) + implicit def byName[A](x: => A): Wrapped = new Wrapped(x) - implicit def byVal[A](x: A) = x + implicit def byVal[A](x: A): A = x def main(args: Array[String]) = { diff --git a/test/files/neg/implicits.check b/test/files/neg/implicits.check index 2eb03eb5f3db..6bcdf6c81285 100644 --- a/test/files/neg/implicits.check +++ b/test/files/neg/implicits.check @@ -16,4 +16,11 @@ implicits.scala:47: error: type mismatch; implicits.scala:59: error: could not find implicit value for parameter x: Nothing foo { ^ +implicits.scala:34: warning: Implicit definition should have explicit type (inferred T) + implicit def select[T](t: HSome[T,_]) = t.head + ^ +implicits.scala:35: warning: Implicit definition should have explicit type (inferred L) + implicit def selectTail[L](t: HSome[_,L]) = t.tail + ^ +2 warnings 4 errors diff --git a/test/files/neg/lint-int-div-to-float.check b/test/files/neg/lint-int-div-to-float.check new file mode 100644 index 000000000000..462a51ced0e9 --- /dev/null +++ b/test/files/neg/lint-int-div-to-float.check @@ -0,0 +1,18 @@ +lint-int-div-to-float.scala:6: warning: integral division is implicitly converted (widened) to floating point. Add an explicit `.toDouble`. + def w1: Double = f / 2 + ^ +lint-int-div-to-float.scala:7: warning: integral division is implicitly converted (widened) to floating point. Add an explicit `.toDouble`. + def w2: Double = (f / 2) * 3 + ^ +lint-int-div-to-float.scala:8: warning: integral division is implicitly converted (widened) to floating point. Add an explicit `.toDouble`. + def w3: Double = -(f / 2) + ^ +lint-int-div-to-float.scala:9: warning: integral division is implicitly converted (widened) to floating point. Add an explicit `.toDouble`. + def w4: Double = (new C).f / (new C).f * 3 + ^ +lint-int-div-to-float.scala:10: warning: integral division is implicitly converted (widened) to floating point. Add an explicit `.toDouble`. + def w5: Double = f - f.abs / 2 + ^ +error: No warnings can be incurred under -Werror. +5 warnings +1 error diff --git a/test/files/neg/lint-int-div-to-float.scala b/test/files/neg/lint-int-div-to-float.scala new file mode 100644 index 000000000000..4f66c481384e --- /dev/null +++ b/test/files/neg/lint-int-div-to-float.scala @@ -0,0 +1,18 @@ +// scalac: -Xlint -Xfatal-warnings + +class C { + def f = 1 + + def w1: Double = f / 2 + def w2: Double = (f / 2) * 3 + def w3: Double = -(f / 2) + def w4: Double = (new C).f / (new C).f * 3 + def w5: Double = f - f.abs / 2 + + def o1: Double = (f / 2).toDouble + def o2: Double = f.toDouble / 2 + def o3: Double = f / 2.toDouble + def o4: Double = f / 2d + def o5: Double = (new C).f.toDouble / (new C).f * 3 + def o6: Long = f / 2 + 3 // only warn if widening to a floating point, not when widening int to long +} diff --git a/test/files/neg/macro-annot-unused-param.check b/test/files/neg/macro-annot-unused-param.check index f23c11962c34..046e938dfe13 100644 --- a/test/files/neg/macro-annot-unused-param.check +++ b/test/files/neg/macro-annot-unused-param.check @@ -1,4 +1,4 @@ -Test_2.scala:2: warning: parameter value x in anonymous function is never used +Test_2.scala:2: warning: parameter x in anonymous function is never used @mymacro ^ error: No warnings can be incurred under -Werror. diff --git a/test/files/neg/names-defaults-neg-pu.check b/test/files/neg/names-defaults-neg-pu.check index dc2216a39317..b4b5d13b1e46 100644 --- a/test/files/neg/names-defaults-neg-pu.check +++ b/test/files/neg/names-defaults-neg-pu.check @@ -127,6 +127,10 @@ names-defaults-neg-pu.scala:141: error: parameter 'a' is already specified at pa names-defaults-neg-pu.scala:142: error: missing parameter type for expanded function (() => b = x$4) val taf4: (Int, String) => Unit = testAnnFun(_, b = _) ^ +names-defaults-neg-pu.scala:193: error: an expression of type Null is ineligible for implicit conversion +Error occurred in an application involving default arguments. + def f = new A3[Int]() + ^ names-defaults-neg-pu.scala:95: warning: the parameter name y is deprecated: use b instead deprNam3(y = 10, b = 2) ^ @@ -137,4 +141,4 @@ names-defaults-neg-pu.scala:100: warning: naming parameter deprNam5Arg is deprec deprNam5(deprNam5Arg = null) ^ 3 warnings -33 errors +34 errors diff --git a/test/files/neg/names-defaults-neg-pu.scala b/test/files/neg/names-defaults-neg-pu.scala index 4fb1a872315c..e381e1119e11 100644 --- a/test/files/neg/names-defaults-neg-pu.scala +++ b/test/files/neg/names-defaults-neg-pu.scala @@ -188,3 +188,7 @@ object t3685 { class u20 { val x: Int = u.f(x = "32") } class u21 { var x: Int = u.f(x = "32") } } +class A3[T](x: T = null) // scala/bug#4727 cf A2 +class t4727 { + def f = new A3[Int]() +} diff --git a/test/files/neg/nullary-override-3b.check b/test/files/neg/nullary-override-3b.check index a3fda6846686..7c4fd1b73669 100644 --- a/test/files/neg/nullary-override-3b.check +++ b/test/files/neg/nullary-override-3b.check @@ -1,7 +1,9 @@ nullary-override-3b.scala:6: error: method without a parameter list overrides a method with a single empty one +def x(): Int (defined in class P) class Q extends P { override def x: Int = 4 } ^ nullary-override-3b.scala:11: error: method without a parameter list overrides a method with a single empty one +def x(): String (defined in trait T2) class Mix12a extends T1 with T2 { override def x = "12a" } ^ 2 errors diff --git a/test/files/neg/nullary-override.check b/test/files/neg/nullary-override.check index 43f443ef3b9f..93e82b74e0ec 100644 --- a/test/files/neg/nullary-override.check +++ b/test/files/neg/nullary-override.check @@ -1,12 +1,12 @@ +nullary-override.scala:4: warning: method with a single empty parameter list overrides method without any parameter list +class B extends A { override def x(): Int = 4 } + ^ nullary-override.scala:15: warning: method without a parameter list overrides a method with a single empty one class Q extends P { override def x: Int = 4 } ^ nullary-override.scala:36: warning: method without a parameter list overrides a method with a single empty one class Mix12a extends T1 with T2 { override def x = "12a" } ^ -nullary-override.scala:4: warning: method with a single empty parameter list overrides method without any parameter list -class B extends A { override def x(): Int = 4 } - ^ nullary-override.scala:37: warning: method with a single empty parameter list overrides method without any parameter list class Mix12b extends T1 with T2 { override def x() = "12b" } ^ diff --git a/test/files/neg/override-final-implicit.check b/test/files/neg/override-final-implicit.check index d46efa1acb52..d5e546078794 100644 --- a/test/files/neg/override-final-implicit.check +++ b/test/files/neg/override-final-implicit.check @@ -1,5 +1,9 @@ +override-final-implicit.scala:6: warning: Implicit definition should have explicit type (inferred Test.this.FooExtender) + override implicit def FooExtender(foo: String) = super.FooExtender(foo) + ^ override-final-implicit.scala:6: error: cannot override final member: final implicit def FooExtender(foo: String): Test.this.FooExtender (defined in class Implicits) override implicit def FooExtender(foo: String) = super.FooExtender(foo) ^ +1 warning 1 error diff --git a/test/files/neg/parens-for-params.check b/test/files/neg/parens-for-params.check new file mode 100644 index 000000000000..36d277404e0b --- /dev/null +++ b/test/files/neg/parens-for-params.check @@ -0,0 +1,7 @@ +parens-for-params.scala:5: warning: parentheses are required around the parameter of a lambda +Use '-Wconf:msg=lambda-parens:s' to silence this warning. + x: Int => x * 2 + ^ +error: No warnings can be incurred under -Werror. +1 warning +1 error diff --git a/test/files/neg/parens-for-params.scala b/test/files/neg/parens-for-params.scala new file mode 100644 index 000000000000..92e77875bdd1 --- /dev/null +++ b/test/files/neg/parens-for-params.scala @@ -0,0 +1,8 @@ +// scalac: -Werror -Xlint -Xsource:3 + +class C { + def f = { + x: Int => x * 2 + } + def g = (x: Int) => x * 2 +} diff --git a/test/files/neg/patmat-exprs-b.check b/test/files/neg/patmat-exprs-b.check index c1a39e7f5565..40003f76ead4 100644 --- a/test/files/neg/patmat-exprs-b.check +++ b/test/files/neg/patmat-exprs-b.check @@ -1,10 +1,10 @@ -patmat-exprs-b.scala:42: warning: parameter value num in class Add is never used +patmat-exprs-b.scala:42: warning: parameter num in class Add is never used case class Add[T](args: Iterable[Expr[T]])(implicit @nowarn num: NumericOps[T]) extends ManyArg[T] { ^ -patmat-exprs-b.scala:46: warning: parameter value num in class Add2 is never used +patmat-exprs-b.scala:46: warning: parameter num in class Add2 is never used case class Add2[T](left: Expr[T], right: Expr[T])(implicit @nowarn num: NumericOps[T]) extends TwoArg[T] { ^ -patmat-exprs-b.scala:49: warning: parameter value num in class Add3 is never used +patmat-exprs-b.scala:49: warning: parameter num in class Add3 is never used case class Add3[T](a1: Expr[T], a2: Expr[T], a3: Expr[T])(implicit @nowarn num: NumericOps[T]) extends ManyArg[T] { ^ patmat-exprs-b.scala:42: warning: @nowarn annotation does not suppress any warnings diff --git a/test/files/neg/private-implicit-class.check b/test/files/neg/private-implicit-class.check index 31ddc9a023ab..1578f9102325 100644 --- a/test/files/neg/private-implicit-class.check +++ b/test/files/neg/private-implicit-class.check @@ -1,4 +1,8 @@ private-implicit-class.scala:6: error: method BarExtender in class ImplicitsPrivate cannot be accessed as a member of ImplicitsPrivate from class TestPrivate override implicit def BarExtender(bar: Int) = super.BarExtender(bar) // error ^ +private-implicit-class.scala:6: warning: Implicit definition should have explicit type + override implicit def BarExtender(bar: Int) = super.BarExtender(bar) // error + ^ +1 warning 1 error diff --git a/test/files/neg/suggest-similar.check b/test/files/neg/suggest-similar.check index 66b01a71cc4f..1eac32d47248 100644 --- a/test/files/neg/suggest-similar.check +++ b/test/files/neg/suggest-similar.check @@ -10,7 +10,7 @@ did you mean Weehawken? new example.Eeehawken ^ suggest-similar.scala:16: error: value readline is not a member of object scala.io.StdIn -did you mean readLine? +did you mean readLine? or perhaps readByte, readInt, or readLong? import scala.io.StdIn.{readline, readInt} ^ suggest-similar.scala:20: error: object stdin is not a member of package io @@ -18,11 +18,68 @@ did you mean StdIn? import scala.io.stdin.{readLine => line} ^ suggest-similar.scala:37: error: value foo is not a member of object example.Hohokus -did you mean foo1, foo2, foo3, or foo4? +did you mean foo1, foo2, foo3, or foo4? or...? Hohokus.foo ^ suggest-similar.scala:41: error: value bar is not a member of example.Hohokus did you mean bar2? new Hohokus().bar // don't suggest bar1 ^ -7 errors +suggest-similar.scala:54: error: value acb is not a member of example.assignments.C +did you mean abc? + def f = c.acb(42) + ^ +suggest-similar.scala:55: error: value ++- is not a member of example.assignments.C +did you mean +++? + def g = c.++-(42) + ^ +suggest-similar.scala:56: error: value ++= is not a member of example.assignments.C +did you mean +++? + def h = c.++=(42) + ^ +suggest-similar.scala:57: error: value ++= is not a member of example.assignments.C +did you mean +++? + Expression does not convert to assignment because receiver is not assignable. + def i = c ++= 42 + ^ +suggest-similar.scala:59: error: value y_= is not a member of example.assignments.C + def v = c y_= 1 + ^ +suggest-similar.scala:60: error: value y is not a member of example.assignments.C + def v2 = c.y = 1 + ^ +suggest-similar.scala:61: error: value x_== is not a member of example.assignments.C + def w = c x_== 1 + ^ +suggest-similar.scala:62: error: value xx_= is not a member of example.assignments.C +did you mean x_=? + def y = c.xx_=(1) + ^ +suggest-similar.scala:63: error: reassignment to val + def y2 = c.xx = 1 + ^ +suggest-similar.scala:65: error: value zzz_= is not a member of example.assignments.C +did you mean zz_=? or perhaps z_=? + def z2 = c.zzz_=(1) + ^ +suggest-similar.scala:66: error: value ++++ is not a member of example.assignments.C +did you mean +++? + def z3 = c.++++(1) + ^ +suggest-similar.scala:67: error: value xxx is not a member of example.assignments.C +did you mean xx? or perhaps x? + def legacy_duple = c.xxx // did not suggest c.xx as too short + ^ +suggest-similar.scala:76: error: value missN is not a member of example.MaxAlt +did you mean miss0, miss1, miss2, or miss3? or...? + def test: Int = new MaxAlt().missN + ^ +suggest-similar.scala:83: error: value missN is not a member of example.MissAlt +did you mean miss0, miss1, or miss2? + def test: Int = new MissAlt().missN + ^ +suggest-similar.scala:94: error: value missN is not a member of example.MoreAlt +did you mean miss0, miss1, miss2, or miss3? or...? + def test: Int = new MoreAlt().missN + ^ +22 errors diff --git a/test/files/neg/suggest-similar.scala b/test/files/neg/suggest-similar.scala index 92dc0b826e41..f10b2d927be7 100644 --- a/test/files/neg/suggest-similar.scala +++ b/test/files/neg/suggest-similar.scala @@ -21,8 +21,8 @@ object C { } class Hohokus { - protected def bar1 - protected[example] def bar2 + protected def bar1: Unit = () + protected[example] def bar2: Unit = () } object Hohokus { def foo1 = 1 @@ -40,3 +40,56 @@ object D { object E { new Hohokus().bar // don't suggest bar1 } + +object assignments { + class C { + def abc(i: Int) = i + def +++(i: Int) = i + var x = 42 + val xx = 42 + var z = 42 + var zz = 42 + } + val c = new C + def f = c.acb(42) + def g = c.++-(42) + def h = c.++=(42) + def i = c ++= 42 + def u = c x_= 1 + def v = c y_= 1 + def v2 = c.y = 1 + def w = c x_== 1 + def y = c.xx_=(1) + def y2 = c.xx = 1 + def z = c.zz_=(1) + def z2 = c.zzz_=(1) + def z3 = c.++++(1) + def legacy_duple = c.xxx // did not suggest c.xx as too short +} + +class MaxAlt { + def miss0 = 42 + def miss1 = 42 + def miss2 = 42 + def miss3 = 42 + def miss33 = 42 + def test: Int = new MaxAlt().missN +} + +class MissAlt { + def miss0 = 42 + def miss1 = 42 + def miss2 = 42 + def test: Int = new MissAlt().missN +} + +class MoreAlt { + def miss0 = 42 + def miss1 = 42 + def miss2 = 42 + def miss3 = 42 + def miss4 = 42 + def miss5 = 42 + def miss6 = 42 + def test: Int = new MoreAlt().missN +} diff --git a/test/files/neg/t1038.scala b/test/files/neg/t1038.scala index 367022965b99..2cc45a190eea 100644 --- a/test/files/neg/t1038.scala +++ b/test/files/neg/t1038.scala @@ -4,5 +4,6 @@ object Y { val a = new X import a._ implicit val b : Int = 1 + @annotation.nowarn implicit val c = 2 } diff --git a/test/files/neg/t10752.check b/test/files/neg/t10752.check index 85f5c2072c17..76d046bf2408 100644 --- a/test/files/neg/t10752.check +++ b/test/files/neg/t10752.check @@ -1,3 +1,6 @@ +Test_2.scala:2: warning: class DeprecatedClass in package p1 is deprecated +object Test extends p1.DeprecatedClass { + ^ Test_2.scala:3: warning: class DeprecatedClass in package p1 is deprecated def useC = p1.DeprecatedClass.foo ^ @@ -5,5 +8,5 @@ Test_2.scala:4: warning: method foo in class DeprecatedMethod is deprecated def useM = p1.DeprecatedMethod.foo ^ error: No warnings can be incurred under -Werror. -2 warnings +3 warnings 1 error diff --git a/test/files/neg/t10752/Test_2.scala b/test/files/neg/t10752/Test_2.scala index 4193e24f5607..5f76ec601f72 100644 --- a/test/files/neg/t10752/Test_2.scala +++ b/test/files/neg/t10752/Test_2.scala @@ -1,5 +1,5 @@ // scalac: -Xlint:deprecation -Werror -object Test { +object Test extends p1.DeprecatedClass { def useC = p1.DeprecatedClass.foo def useM = p1.DeprecatedMethod.foo } diff --git a/test/files/neg/t10790.check b/test/files/neg/t10790.check index 77c54d47926f..2facf5713ed5 100644 --- a/test/files/neg/t10790.check +++ b/test/files/neg/t10790.check @@ -4,7 +4,7 @@ t10790.scala:13: warning: private val y in class X is never used t10790.scala:10: warning: private class C in class X is never used private class C // warn ^ -t10790.scala:8: warning: parameter value x in method control is never used +t10790.scala:8: warning: parameter x in method control is never used def control(x: Int) = answer // warn to verify control ^ error: No warnings can be incurred under -Werror. diff --git a/test/files/neg/t11644a.check b/test/files/neg/t11644a.check new file mode 100644 index 000000000000..6cee9521f5d1 --- /dev/null +++ b/test/files/neg/t11644a.check @@ -0,0 +1,18 @@ +t11644a.scala:20: error: type mismatch; + found : Int + required: AcciSamZero + val t2AcciSam: AcciSamZero = m2 // error, nilary methods don't eta-expand to SAM types + ^ +t11644a.scala:21: error: type mismatch; + found : Int + required: SamZero + val t2Sam: SamZero = m2 // error, nilary methods don't eta-expand to SAM types + ^ +t11644a.scala:24: warning: Eta-expansion to expected type AcciSamOne, which is not a function type but is SAM-convertible to Int => Int. +trait AcciSamOne should be annotated with `@FunctionalInterface` if eta-expansion is desired. +Avoid eta-expansion by writing the function literal `((x: Int) => m3(x))` or `m3(_)`. +This warning can be filtered with `-Wconf:cat=lint-eta-sam`. + val t3AcciSam: AcciSamOne = m3 // warn + ^ +1 warning +2 errors diff --git a/test/files/neg/t11644a.scala b/test/files/neg/t11644a.scala new file mode 100644 index 000000000000..3e9ef0dcb0bb --- /dev/null +++ b/test/files/neg/t11644a.scala @@ -0,0 +1,28 @@ +// scalac: -Xsource:3 +// +// eta-expansion to SAM type always warns in Scala 3 world + +trait AcciSamZero { def apply(): Int } + +@FunctionalInterface +trait SamZero { def apply(): Int } + +trait AcciSamOne { def apply(i: Int): Int } + +@FunctionalInterface +trait SamOne { def apply(i: Int): Int } + +class EtaExpand214 { + def m2() = 1 + def m3(x: Int) = x + + val t2: () => Any = m2 // eta-expanded with lint warning + val t2AcciSam: AcciSamZero = m2 // error, nilary methods don't eta-expand to SAM types + val t2Sam: SamZero = m2 // error, nilary methods don't eta-expand to SAM types + + val t3: Int => Any = m3 // no warn + val t3AcciSam: AcciSamOne = m3 // warn + val t3SamLit: AcciSamOne = (x: Int) => m3(x) // no warn + val t3SamPH: AcciSamOne = m3(_) // no warn + val t3Sam: SamOne = m3 // no warn +} diff --git a/test/files/neg/t11644b.check b/test/files/neg/t11644b.check new file mode 100644 index 000000000000..c24d848a4208 --- /dev/null +++ b/test/files/neg/t11644b.check @@ -0,0 +1,22 @@ +t11644b.scala:18: error: type mismatch; + found : Int + required: AcciSamZero + val t2AcciSam: AcciSamZero = m2 // error, nilary methods don't eta-expand to SAM types under -Xsource:3 + ^ +t11644b.scala:19: error: type mismatch; + found : Int + required: SamZero + val t2Sam: SamZero = m2 // error, nilary methods don't eta-expand to SAM types under -Xsource:3 + ^ +t11644b.scala:17: warning: An unapplied 0-arity method was eta-expanded (due to the expected type () => Any), rather than applied to `()`. +Write m2() to invoke method m2, or change the expected type. + val t2: () => Any = m2 // eta-expanded with lint warning + ^ +t11644b.scala:22: warning: Eta-expansion to expected type AcciSamOne, which is not a function type but is SAM-convertible to Int => Int. +trait AcciSamOne should be annotated with `@FunctionalInterface` if eta-expansion is desired. +Avoid eta-expansion by writing the function literal `((x: Int) => m3(x))` or `m3(_)`. +This warning can be filtered with `-Wconf:cat=lint-eta-sam`. + val t3AcciSam: AcciSamOne = m3 // warn + ^ +2 warnings +2 errors diff --git a/test/files/neg/t11644b.scala b/test/files/neg/t11644b.scala new file mode 100644 index 000000000000..299667389a50 --- /dev/null +++ b/test/files/neg/t11644b.scala @@ -0,0 +1,24 @@ +// scalac: -Xlint:deprecation,eta-zero,eta-sam + +trait AcciSamZero { def apply(): Int } + +@FunctionalInterface +trait SamZero { def apply(): Int } + +trait AcciSamOne { def apply(i: Int): Int } + +@FunctionalInterface +trait SamOne { def apply(i: Int): Int } + +class EtaExpand214 { + def m2() = 1 + def m3(x: Int) = x + + val t2: () => Any = m2 // eta-expanded with lint warning + val t2AcciSam: AcciSamZero = m2 // error, nilary methods don't eta-expand to SAM types under -Xsource:3 + val t2Sam: SamZero = m2 // error, nilary methods don't eta-expand to SAM types under -Xsource:3 + + val t3: Int => Any = m3 // no warn + val t3AcciSam: AcciSamOne = m3 // warn + val t3Sam: SamOne = m3 // no warn +} diff --git a/test/files/neg/t11644c.check b/test/files/neg/t11644c.check new file mode 100644 index 000000000000..7f1c788df184 --- /dev/null +++ b/test/files/neg/t11644c.check @@ -0,0 +1,14 @@ +s.scala:13: warning: Eta-expansion to expected type J, which is not a function type but is SAM-convertible to Int => Int. +Avoid eta-expansion by writing the function literal `((i: Int) => bump(i))` or `bump(_)`. +This warning can be filtered with `-Wconf:cat=lint-eta-sam`. + c.f(bump), + ^ +s.scala:14: warning: Eta-expansion to expected type K, which is not a function type but is SAM-convertible to Int => Int. +trait K should be annotated with `@FunctionalInterface` if eta-expansion is desired. +Avoid eta-expansion by writing the function literal `((i: Int) => bump(i))` or `bump(_)`. +This warning can be filtered with `-Wconf:cat=lint-eta-sam`. + c.g(bump), + ^ +error: No warnings can be incurred under -Werror. +2 warnings +1 error diff --git a/test/files/neg/t11644c/J.java b/test/files/neg/t11644c/J.java new file mode 100644 index 000000000000..95a3612859da --- /dev/null +++ b/test/files/neg/t11644c/J.java @@ -0,0 +1,7 @@ + +public abstract class J { + public abstract int f(int i); +} +interface K { + int f(int i); +} diff --git a/test/files/neg/t11644c/s.scala b/test/files/neg/t11644c/s.scala new file mode 100644 index 000000000000..185ba67ad221 --- /dev/null +++ b/test/files/neg/t11644c/s.scala @@ -0,0 +1,16 @@ +// scalac: -Xsource:3 -Werror + +class C { + def f(j: J): Int = j.f(42) + def g(k: K): Int = k.f(17) +} +object Test extends App { + def bump(i: Int): Int = i + 1 + val c = new C + println {( + c.f((i: Int) => i + 1), + c.g((i: Int) => i + 1), + c.f(bump), + c.g(bump), + )} +} diff --git a/test/files/neg/t11843.check b/test/files/neg/t11843.check index 09f56848e53d..35a071d8841e 100644 --- a/test/files/neg/t11843.check +++ b/test/files/neg/t11843.check @@ -1,17 +1,17 @@ t11843.scala:6: error: value $isInstanceOf is not a member of String -did you mean asInstanceOf or isInstanceOf? +did you mean isInstanceOf? or perhaps asInstanceOf? "".$isInstanceOf[Int] ^ t11843.scala:7: error: value $asInstanceOf is not a member of String -did you mean asInstanceOf or isInstanceOf? +did you mean asInstanceOf? or perhaps isInstanceOf? "".$asInstanceOf[Int] ^ t11843.scala:10: error: value $isInstanceOf is not a member of Symbol -did you mean asInstanceOf or isInstanceOf? +did you mean isInstanceOf? or perhaps asInstanceOf? ss.$isInstanceOf[String] ^ t11843.scala:11: error: value $asInstanceOf is not a member of Symbol -did you mean asInstanceOf or isInstanceOf? +did you mean asInstanceOf? or perhaps isInstanceOf? ss.$asInstanceOf[String] ^ t11843.scala:8: warning: fruitless type test: a value of type Symbol cannot also be a String (the underlying of String) diff --git a/test/files/neg/t11921-alias.check b/test/files/neg/t11921-alias.check new file mode 100644 index 000000000000..7e9c5d043b2a --- /dev/null +++ b/test/files/neg/t11921-alias.check @@ -0,0 +1,31 @@ +t11921-alias.scala:18: warning: reference to TT is ambiguous; +it is both defined in the enclosing object O and inherited in the enclosing class D as type TT (defined in class C) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.TT`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + def n(x: TT) = x // ambiguous + ^ +t11921-alias.scala:38: warning: reference to c is ambiguous; +it is both defined in the enclosing class B and inherited in the enclosing anonymous class as value c (defined in class A) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.c`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + def n = c // ambiguous + ^ +t11921-alias.scala:57: warning: reference to name is ambiguous; +it is both defined in the enclosing method m and inherited in the enclosing anonymous class as value name (defined in class C) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.name`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + println(name) + ^ +t11921-alias.scala:67: warning: reference to name is ambiguous; +it is both defined in the enclosing method m and inherited in the enclosing anonymous class as value name (defined in class A, inherited through parent class C) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.name`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + println(name) + ^ +error: No warnings can be incurred under -Werror. +4 warnings +1 error diff --git a/test/files/neg/t11921-alias.scala b/test/files/neg/t11921-alias.scala new file mode 100644 index 000000000000..fab5ae9badc2 --- /dev/null +++ b/test/files/neg/t11921-alias.scala @@ -0,0 +1,70 @@ +// scalac: -Werror -Xsource:3 + +object t1 { + class C[T] { type TT = T } + object O { + type TT = String + class D extends C[TT] { + def n(x: TT) = x // OK + } + } +} + +object t2 { + class C[T] { type TT <: T } + object O { + type TT = String + class D extends C[TT] { + def n(x: TT) = x // ambiguous + } + } +} + +object t3 { + trait Context + class A[C <: Context](val c: C) + class B(val c: Context) { b => + val a = new A[c.type](c) { + def n = c // OK + } + } +} + +object t4 { + trait Context + class A[C <: Context](val c: C) + class B(val c: Context) { b => + val a = new A(c) { + def n = c // ambiguous + } + } +} + +object t5 { + trait TT + class K[T <: TT](val t: T) + class C { + def f(t: TT) = new K[t.type](t) { + def test = t + } + } +} + +object t6 { + class C(val name: String) + object Test { + def m(name: String) = new C(name) { + println(name) + } + } +} + +object t7 { + abstract class A(val name: String) + class C(name: String) extends A(name) + object Test { + def m(name: String) = new C(name) { + println(name) + } + } +} diff --git a/test/files/neg/t11921.check b/test/files/neg/t11921.check new file mode 100644 index 000000000000..168bfa56963e --- /dev/null +++ b/test/files/neg/t11921.check @@ -0,0 +1,14 @@ +t11921.scala:6: error: type mismatch; + found : A => B + required: B => B + def iterator = coll.iterator.map(f) // coll is ambiguous + ^ +t11921.scala:6: warning: reference to coll is ambiguous; +it is both defined in the enclosing method lazyMap and inherited in the enclosing anonymous class as method coll (defined in trait Iterable) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.coll`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + def iterator = coll.iterator.map(f) // coll is ambiguous + ^ +1 warning +1 error diff --git a/test/files/neg/t11921.scala b/test/files/neg/t11921.scala new file mode 100644 index 000000000000..91093f3e7443 --- /dev/null +++ b/test/files/neg/t11921.scala @@ -0,0 +1,16 @@ +// scalac: -Xsource:3 + +class C { + def lazyMap[A, B](coll: Iterable[A], f: A => B) = + new Iterable[B] { + def iterator = coll.iterator.map(f) // coll is ambiguous + } +} + +/* was: +t11921.scala:5: error: type mismatch; + found : A => B + required: B => B + def iterator = coll.iterator.map(f) + ^ +*/ diff --git a/test/files/neg/t11921b.check b/test/files/neg/t11921b.check new file mode 100644 index 000000000000..045f13ffb9a2 --- /dev/null +++ b/test/files/neg/t11921b.check @@ -0,0 +1,61 @@ +t11921b.scala:135: error: could not find implicit value for parameter i: Int + def u = t // doesn't compile in Scala 2 (maybe there's a ticket for that) + ^ +t11921b.scala:11: warning: reference to x is ambiguous; +it is both defined in the enclosing object Test and inherited in the enclosing class D as value x (defined in class C) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.x`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + println(x) // error + ^ +t11921b.scala:15: warning: reference to x is ambiguous; +it is both defined in the enclosing object Test and inherited in the enclosing anonymous class as value x (defined in class C) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.x`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + println(x) // error + ^ +t11921b.scala:26: warning: reference to y is ambiguous; +it is both defined in the enclosing method c and inherited in the enclosing anonymous class as value y (defined in class D) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.y`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + println(y) // error + ^ +t11921b.scala:38: warning: reference to y is ambiguous; +it is both defined in the enclosing method c and inherited in the enclosing class E as value y (defined in class D) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.y`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + println(y) // error + ^ +t11921b.scala:65: warning: reference to global is ambiguous; +it is both defined in the enclosing package and inherited in the enclosing object D as value global (defined in class C) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.global`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + println(global) // error + ^ +t11921b.scala:75: warning: reference to x is ambiguous; +it is both defined in the enclosing object Uhu and inherited in the enclosing class C as value x (defined in class A, inherited through parent class B) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.x`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + def t = x // ambiguous, message mentions parent B + ^ +t11921b.scala:89: warning: reference to a is ambiguous; +it is both defined in the enclosing class C and inherited in the enclosing trait J as method a (defined in trait I) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.a`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + val t = a // error + ^ +t11921b.scala:136: warning: reference to lo is ambiguous; +it is both defined in the enclosing object test10 and inherited in the enclosing class C as value lo (defined in class P) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.lo`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + def v = t(lo) // error + ^ +8 warnings +1 error diff --git a/test/files/neg/t11921b.scala b/test/files/neg/t11921b.scala new file mode 100644 index 000000000000..887b7280698c --- /dev/null +++ b/test/files/neg/t11921b.scala @@ -0,0 +1,145 @@ +// scalac: -Werror -Xsource:3 + +object test1 { + + class C { + val x = 0 + } + object Test { + val x = 1 + class D extends C { + println(x) // error + } + def f() = + new C { + println(x) // error + } + } +} + +object test2 { + def c(y: Float) = { + class D { + val y = 2 + } + new D { + println(y) // error + } + } +} + +object test3 { + def c(y: Float) = { + class D { + val y = 2 + } + class E extends D { + class F { + println(y) // error + } + } + } +} + +object test4 { + + class C { + val x = 0 + } + object Test { + val x = 1 + class D extends C { + def x(y: Int) = 3 + val y: Int = this.x // OK + val z: Int = x // OK + } + } +} + +object global + +class C { + val global = 42 +} +object D extends C { + println(global) // error +} + +object test5 { + class A { val x = 1 } + class B extends A + object Uhu { + val x = 2 + class C extends B { + class Inner { + def t = x // ambiguous, message mentions parent B + } + } + } +} + +object test6 { + trait I { + val a = 1 + def a(x: Int) = "" + } + class C { + val a = "" + trait J extends I { + val t = a // error + } + } +} + + +object test7 { + trait T { + // overloaded a + val a = "" + def a(x: Int) = "" + } + + trait I { + val a = 1 + } + + class C extends T { + trait J { + self: I => + // no warning here. when checking for an outer `a`, we find an OverloadedSymbol with the two definitions in `T`. + // The owner of the overloaded symbol is `C`, but the alternatives have owner `T`. + val t = a + } + } +} + +object test9 { + val lo: Int = 1 + class P { + implicit val lo: Int = 1 + } + class C extends P { + def t(implicit i: Int) = 10 + def u = t // ok, reference to `lo` by implicit search + def v = t(lo) // should warn, but doesn't. can't tell if reference to `lo` was explicit or not. + } +} + +object test10 { + implicit val lo: Int = 1 + class P { + val lo: Int = 1 + } + class C extends P { + def t(implicit i: Int) = 10 + def u = t // doesn't compile in Scala 2 (maybe there's a ticket for that) + def v = t(lo) // error + } +} + +package scala { + trait P { trait Option[+A] } + class C extends P { + def t = new Option[String] {} // OK, competing scala.Option is not defined in the same compilation unit + } +} diff --git a/test/files/neg/t11921c.check b/test/files/neg/t11921c.check new file mode 100644 index 000000000000..1c2d1ba8adda --- /dev/null +++ b/test/files/neg/t11921c.check @@ -0,0 +1,6 @@ +t11921c.scala:6: error: type mismatch; + found : A => B + required: B => B + def iterator = coll.iterator.map(f) // coll is ambiguous + ^ +1 error diff --git a/test/files/neg/t11921c.scala b/test/files/neg/t11921c.scala new file mode 100644 index 000000000000..3b38fa6c9520 --- /dev/null +++ b/test/files/neg/t11921c.scala @@ -0,0 +1,16 @@ +// scalac: -Wconf:msg=legacy-binding:s -Xsource:3 + +class C { + def lazyMap[A, B](coll: Iterable[A], f: A => B) = + new Iterable[B] { + def iterator = coll.iterator.map(f) // coll is ambiguous + } +} + +/* was: +t11921.scala:5: error: type mismatch; + found : A => B + required: B => B + def iterator = coll.iterator.map(f) + ^ +*/ diff --git a/test/files/neg/t12159.check b/test/files/neg/t12159.check new file mode 100644 index 000000000000..bda2e48622ce --- /dev/null +++ b/test/files/neg/t12159.check @@ -0,0 +1,7 @@ +s.scala:5: error: illegal inheritance from sealed class H +class S extends H { + ^ +s.scala:8: error: illegal inheritance from sealed trait I +trait T extends I { + ^ +2 errors diff --git a/test/files/neg/t12159/H.java b/test/files/neg/t12159/H.java new file mode 100644 index 000000000000..3a15309f733e --- /dev/null +++ b/test/files/neg/t12159/H.java @@ -0,0 +1,19 @@ +// javaVersion: 17+ +package p; + +sealed public class H { +} + +final class K extends H { +} + +non-sealed class L extends H { +} + +sealed +class P extends H { +} + +final +class Q extends P { +} diff --git a/test/files/neg/t12159/I.java b/test/files/neg/t12159/I.java new file mode 100644 index 000000000000..f91c69dd7828 --- /dev/null +++ b/test/files/neg/t12159/I.java @@ -0,0 +1,6 @@ +// javaVersion: 17+ + +package p; + +sealed interface I permits J { +} diff --git a/test/files/neg/t12159/J.java b/test/files/neg/t12159/J.java new file mode 100644 index 000000000000..5bd2c4c92374 --- /dev/null +++ b/test/files/neg/t12159/J.java @@ -0,0 +1,6 @@ +// javaVersion: 17+ + +package p; + +sealed public class J implements I permits M { +} diff --git a/test/files/neg/t12159/M.java b/test/files/neg/t12159/M.java new file mode 100644 index 000000000000..245c79304d29 --- /dev/null +++ b/test/files/neg/t12159/M.java @@ -0,0 +1,9 @@ +// javaVersion: 17+ + +package p; + +public final class M extends J { +} + +final class N extends L { +} diff --git a/test/files/neg/t12159/s.scala b/test/files/neg/t12159/s.scala new file mode 100644 index 000000000000..b8ec52070d07 --- /dev/null +++ b/test/files/neg/t12159/s.scala @@ -0,0 +1,9 @@ +// javaVersion: 17+ + +package p + +class S extends H { +} + +trait T extends I { +} diff --git a/test/files/neg/t12159b.check b/test/files/neg/t12159b.check new file mode 100644 index 000000000000..14dd6627065d --- /dev/null +++ b/test/files/neg/t12159b.check @@ -0,0 +1,4 @@ +s_2.scala:5: error: illegal inheritance from sealed trait I +class S extends I + ^ +1 error diff --git a/test/files/neg/t12159b/I.java b/test/files/neg/t12159b/I.java new file mode 100644 index 000000000000..f91c69dd7828 --- /dev/null +++ b/test/files/neg/t12159b/I.java @@ -0,0 +1,6 @@ +// javaVersion: 17+ + +package p; + +sealed interface I permits J { +} diff --git a/test/files/neg/t12159b/J.java b/test/files/neg/t12159b/J.java new file mode 100644 index 000000000000..12de6f9fcbd4 --- /dev/null +++ b/test/files/neg/t12159b/J.java @@ -0,0 +1,6 @@ +// javaVersion: 17+ + +package p; + +public final class J implements I { +} diff --git a/test/files/neg/t12159b/s_2.scala b/test/files/neg/t12159b/s_2.scala new file mode 100644 index 000000000000..ec5f40dba180 --- /dev/null +++ b/test/files/neg/t12159b/s_2.scala @@ -0,0 +1,5 @@ +// javaVersion: 17+ + +package p + +class S extends I diff --git a/test/files/neg/t12159c.check b/test/files/neg/t12159c.check new file mode 100644 index 000000000000..189c51ef6817 --- /dev/null +++ b/test/files/neg/t12159c.check @@ -0,0 +1,7 @@ +s_2.scala:7: warning: match may not be exhaustive. +It would fail on the following input: K() + h match { + ^ +error: No warnings can be incurred under -Werror. +1 warning +1 error diff --git a/test/files/neg/t12159c/H.java b/test/files/neg/t12159c/H.java new file mode 100644 index 000000000000..bf6394e1e869 --- /dev/null +++ b/test/files/neg/t12159c/H.java @@ -0,0 +1,14 @@ +// javaVersion: 17+ +package p; + +sealed abstract public class H { +} + +final class J extends H { +} + +final class K extends H { +} + +final class L extends H { +} diff --git a/test/files/neg/t12159c/s_2.scala b/test/files/neg/t12159c/s_2.scala new file mode 100644 index 000000000000..ec9c445cad42 --- /dev/null +++ b/test/files/neg/t12159c/s_2.scala @@ -0,0 +1,12 @@ +// javaVersion: 17+ +// scalac: -Werror +package p + +class C { + def f(h: H) = + h match { + case j: J => j.toString + case l: L => l.toString + } +} + diff --git a/test/files/neg/t12159d.check b/test/files/neg/t12159d.check new file mode 100644 index 000000000000..1efb798c80d7 --- /dev/null +++ b/test/files/neg/t12159d.check @@ -0,0 +1,7 @@ +t.scala:7: warning: match may not be exhaustive. +It would fail on the following input: W() + x match { + ^ +error: No warnings can be incurred under -Werror. +1 warning +1 error diff --git a/test/files/neg/t12159d/X.java b/test/files/neg/t12159d/X.java new file mode 100644 index 000000000000..0812a6fd21cd --- /dev/null +++ b/test/files/neg/t12159d/X.java @@ -0,0 +1,14 @@ +// javaVersion: 17+ +package p; + +sealed abstract public class X { +} + +final class W extends X { +} + +final class Y extends X { +} + +final class Z extends X { +} diff --git a/test/files/neg/t12159d/t.scala b/test/files/neg/t12159d/t.scala new file mode 100644 index 000000000000..e57c9cd62ddc --- /dev/null +++ b/test/files/neg/t12159d/t.scala @@ -0,0 +1,12 @@ +// javaVersion: 17+ +// scalac: -Werror +package p + +class C { + def f(x: X) = + x match { + case y: Y => y.toString + case z: Z => z.toString + } +} + diff --git a/test/files/neg/t12159e.check b/test/files/neg/t12159e.check new file mode 100644 index 000000000000..86807203f124 --- /dev/null +++ b/test/files/neg/t12159e.check @@ -0,0 +1,11 @@ +t.scala:7: warning: match may not be exhaustive. +It would fail on the following input: W() + x match { + ^ +t.scala:12: warning: match may not be exhaustive. +It would fail on the following inputs: Z(), Z2() + x match { + ^ +error: No warnings can be incurred under -Werror. +2 warnings +1 error diff --git a/test/files/neg/t12159e/X.java b/test/files/neg/t12159e/X.java new file mode 100644 index 000000000000..6b770b115ea6 --- /dev/null +++ b/test/files/neg/t12159e/X.java @@ -0,0 +1,20 @@ +// javaVersion: 17+ +package p; + +sealed abstract public class X { +} + +final class W extends X { +} + +final class Y extends X { +} + +sealed class Z extends X permits Z1, Z2 { +} + +final class Z1 extends Z { +} + +final class Z2 extends Z { +} diff --git a/test/files/neg/t12159e/t.scala b/test/files/neg/t12159e/t.scala new file mode 100644 index 000000000000..1d5810a09f85 --- /dev/null +++ b/test/files/neg/t12159e/t.scala @@ -0,0 +1,18 @@ +// javaVersion: 17+ +// scalac: -Werror +package p + +class C { + def f(x: X) = + x match { + case y: Y => y.toString + case z: Z => z.toString + } + def g(x: X) = + x match { + case w: W => w.toString + case y: Y => y.toString + case z: Z1 => z.toString + } +} + diff --git a/test/files/neg/t12159f.check b/test/files/neg/t12159f.check new file mode 100644 index 000000000000..86807203f124 --- /dev/null +++ b/test/files/neg/t12159f.check @@ -0,0 +1,11 @@ +t.scala:7: warning: match may not be exhaustive. +It would fail on the following input: W() + x match { + ^ +t.scala:12: warning: match may not be exhaustive. +It would fail on the following inputs: Z(), Z2() + x match { + ^ +error: No warnings can be incurred under -Werror. +2 warnings +1 error diff --git a/test/files/neg/t12159f/X.java b/test/files/neg/t12159f/X.java new file mode 100644 index 000000000000..bc1043b66039 --- /dev/null +++ b/test/files/neg/t12159f/X.java @@ -0,0 +1,14 @@ +// javaVersion: 17+ +package p; + +sealed abstract public class X { +} + +final class W extends X { +} + +final class Y extends X { +} + +sealed class Z extends X permits Z1, Z2 { +} diff --git a/test/files/neg/t12159f/Z.java b/test/files/neg/t12159f/Z.java new file mode 100644 index 000000000000..55fcc661a182 --- /dev/null +++ b/test/files/neg/t12159f/Z.java @@ -0,0 +1,8 @@ +// javaVersion: 17+ +package p; + +final class Z1 extends Z { +} + +final class Z2 extends Z { +} diff --git a/test/files/neg/t12159f/t.scala b/test/files/neg/t12159f/t.scala new file mode 100644 index 000000000000..1d5810a09f85 --- /dev/null +++ b/test/files/neg/t12159f/t.scala @@ -0,0 +1,18 @@ +// javaVersion: 17+ +// scalac: -Werror +package p + +class C { + def f(x: X) = + x match { + case y: Y => y.toString + case z: Z => z.toString + } + def g(x: X) = + x match { + case w: W => w.toString + case y: Y => y.toString + case z: Z1 => z.toString + } +} + diff --git a/test/files/neg/t12159g.check b/test/files/neg/t12159g.check new file mode 100644 index 000000000000..f268b3430cdc --- /dev/null +++ b/test/files/neg/t12159g.check @@ -0,0 +1,7 @@ +t.scala:4: warning: match may not be exhaustive. +It would fail on the following inputs: Oz(), Z() + def n(a: X) = a match { case _: Y => 42 } + ^ +error: No warnings can be incurred under -Werror. +1 warning +1 error diff --git a/test/files/neg/t12159g/X.java b/test/files/neg/t12159g/X.java new file mode 100644 index 000000000000..8687ede65157 --- /dev/null +++ b/test/files/neg/t12159g/X.java @@ -0,0 +1,12 @@ + +package p; +public sealed interface X { + public default int x() { return 27; } +} +final class Y implements X { } +final class O { + final static class Z implements X { } + final static class Inner { + final static class Oz implements X { } + } +} diff --git a/test/files/neg/t12159g/t.scala b/test/files/neg/t12159g/t.scala new file mode 100644 index 000000000000..f92009287f1b --- /dev/null +++ b/test/files/neg/t12159g/t.scala @@ -0,0 +1,5 @@ +// scalac: -Werror -Xlint +package p +class T { + def n(a: X) = a match { case _: Y => 42 } +} diff --git a/test/files/neg/t12349.check b/test/files/neg/t12349.check index ed6d1b26451d..e774d33bd3a0 100644 --- a/test/files/neg/t12349.check +++ b/test/files/neg/t12349.check @@ -31,129 +31,150 @@ def a7(): Unit (defined in class t12349a) t12349b.scala:13: error: weaker access privileges in overriding def a8(): Unit (defined in class t12349a) override should be public - protected[this] override def a8(): Unit = println("Inner12349b#a8()") // weaker access privileges + protected[Inner12349b] override def a8(): Unit = println("Inner12349b#a8()") // weaker access privileges + ^ +t12349b.scala:15: error: weaker access privileges in overriding +def aA(): Unit (defined in class t12349a) + override should be public + protected[this] override def aA(): Unit = println("Inner12349b#aA()") // weaker access privileges ^ -t12349b.scala:14: error: weaker access privileges in overriding -def a9(): Unit (defined in class t12349a) +t12349b.scala:16: error: weaker access privileges in overriding +def aB(): Unit (defined in class t12349a) override should not be private - private[this] override def a9(): Unit = println("Inner12349b#a9()") // weaker access privileges + private[this] override def aB(): Unit = println("Inner12349b#aB()") // weaker access privileges ^ -t12349b.scala:18: error: weaker access privileges in overriding +t12349b.scala:20: error: weaker access privileges in overriding protected[package t12349] def b3(): Unit (defined in class t12349a) override should not be private private override def b3(): Unit = println("Inner12349b#b3()") // weaker access privileges ^ -t12349b.scala:20: error: weaker access privileges in overriding +t12349b.scala:22: error: weaker access privileges in overriding protected[package t12349] def b5(): Unit (defined in class t12349a) override should at least be protected[t12349] private[t12349b] override def b5(): Unit = println("Inner12349b#b5()") // weaker access privileges ^ -t12349b.scala:22: error: weaker access privileges in overriding +t12349b.scala:24: error: weaker access privileges in overriding protected[package t12349] def b7(): Unit (defined in class t12349a) override should at least be protected[t12349] private[t12349] override def b7(): Unit = println("Inner12349b#b7()") // weaker access privileges ^ -t12349b.scala:24: error: weaker access privileges in overriding -protected[package t12349] def b9(): Unit (defined in class t12349a) +t12349b.scala:28: error: weaker access privileges in overriding +protected[package t12349] def bB(): Unit (defined in class t12349a) override should not be private - private[this] override def b9(): Unit = println("Inner12349b#b9()") // weaker access privileges + private[this] override def bB(): Unit = println("Inner12349b#bB()") // weaker access privileges ^ -t12349b.scala:27: error: weaker access privileges in overriding +t12349b.scala:31: error: weaker access privileges in overriding private[package t12349] def c2(): Unit (defined in class t12349a) override should at least be private[t12349] protected override def c2(): Unit = println("Inner12349b#c2()") // weaker access privileges ^ -t12349b.scala:28: error: weaker access privileges in overriding +t12349b.scala:32: error: weaker access privileges in overriding private[package t12349] def c3(): Unit (defined in class t12349a) override should not be private private override def c3(): Unit = println("Inner12349b#c3()") // weaker access privileges ^ -t12349b.scala:29: error: weaker access privileges in overriding +t12349b.scala:33: error: weaker access privileges in overriding private[package t12349] def c4(): Unit (defined in class t12349a) override should at least be private[t12349] protected[t12349b] override def c4(): Unit = println("Inner12349b#c4()") // weaker access privileges ^ -t12349b.scala:30: error: weaker access privileges in overriding +t12349b.scala:34: error: weaker access privileges in overriding private[package t12349] def c5(): Unit (defined in class t12349a) override should at least be private[t12349] private[t12349b] override def c5(): Unit = println("Inner12349b#c5()") // weaker access privileges ^ -t12349b.scala:33: error: weaker access privileges in overriding +t12349b.scala:37: error: weaker access privileges in overriding private[package t12349] def c8(): Unit (defined in class t12349a) override should at least be private[t12349] - protected[this] override def c8(): Unit = println("Inner12349b#c8()") // weaker access privileges + protected[Inner12349b] override def c8(): Unit = println("Inner12349b#c8()") // weaker access privileges + ^ +t12349b.scala:39: error: weaker access privileges in overriding +private[package t12349] def cA(): Unit (defined in class t12349a) + override should at least be private[t12349] + protected[this] override def cA(): Unit = println("Inner12349b#cA()") // weaker access privileges ^ -t12349b.scala:34: error: weaker access privileges in overriding -private[package t12349] def c9(): Unit (defined in class t12349a) +t12349b.scala:40: error: weaker access privileges in overriding +private[package t12349] def cB(): Unit (defined in class t12349a) override should not be private - private[this] override def c9(): Unit = println("Inner12349b#c9()") // weaker access privileges + private[this] override def cB(): Unit = println("Inner12349b#cB()") // weaker access privileges ^ -t12349b.scala:36: error: method d1 overrides nothing +t12349b.scala:42: error: method d1 overrides nothing override def d1(): Unit = println("Inner12349b#d1()") // overrides nothing ^ -t12349b.scala:37: error: method d2 overrides nothing +t12349b.scala:43: error: method d2 overrides nothing protected override def d2(): Unit = println("Inner12349b#d2()") // overrides nothing ^ -t12349b.scala:38: error: method d3 overrides nothing +t12349b.scala:44: error: method d3 overrides nothing private override def d3(): Unit = println("Inner12349b#d3()") // overrides nothing ^ -t12349b.scala:39: error: method d4 overrides nothing +t12349b.scala:45: error: method d4 overrides nothing protected[t12349b] override def d4(): Unit = println("Inner12349b#d4()") // overrides nothing ^ -t12349b.scala:40: error: method d5 overrides nothing +t12349b.scala:46: error: method d5 overrides nothing private[t12349b] override def d5(): Unit = println("Inner12349b#d5()") // overrides nothing ^ -t12349b.scala:41: error: method d6 overrides nothing +t12349b.scala:47: error: method d6 overrides nothing protected[t12349] override def d6(): Unit = println("Inner12349b#d6()") // overrides nothing ^ -t12349b.scala:42: error: method d7 overrides nothing +t12349b.scala:48: error: method d7 overrides nothing private[t12349] override def d7(): Unit = println("Inner12349b#d7()") // overrides nothing ^ -t12349b.scala:43: error: method d8 overrides nothing - protected[this] override def d8(): Unit = println("Inner12349b#d8()") // overrides nothing +t12349b.scala:49: error: method d8 overrides nothing + protected[Inner12349b] override def d8(): Unit = println("Inner12349b#d8()") // overrides nothing + ^ +t12349b.scala:51: error: method dA overrides nothing + protected[this] override def dA(): Unit = println("Inner12349b#dA()") // overrides nothing ^ -t12349b.scala:44: error: method d9 overrides nothing - private[this] override def d9(): Unit = println("Inner12349b#d9()") // overrides nothing +t12349b.scala:52: error: method dB overrides nothing + private[this] override def dB(): Unit = println("Inner12349b#dB()") // overrides nothing ^ -t12349c.scala:11: error: weaker access privileges in overriding +t12349b.scala:50: error: method d9 overrides nothing + private[Inner12349b] override def d9(): Unit = println("Inner12349b#d9()") // overrides nothing + ^ +t12349c.scala:9: error: weaker access privileges in overriding def a2(): Unit (defined in class t12349a) override should be public protected override def a2(): Unit = println("Inner12349c#a2()") // weaker access privileges ^ -t12349c.scala:12: error: weaker access privileges in overriding +t12349c.scala:10: error: weaker access privileges in overriding def a3(): Unit (defined in class t12349a) override should not be private private override def a3(): Unit = println("Inner12349c#a3()") // weaker access privileges ^ -t12349c.scala:13: error: weaker access privileges in overriding +t12349c.scala:11: error: weaker access privileges in overriding def a4(): Unit (defined in class t12349a) override should be public protected[t12349c] override def a4(): Unit = println("Inner12349c#a4()") // weaker access privileges ^ -t12349c.scala:14: error: weaker access privileges in overriding +t12349c.scala:12: error: weaker access privileges in overriding def a5(): Unit (defined in class t12349a) override should be public private[t12349c] override def a5(): Unit = println("Inner12349c#a5()") // weaker access privileges ^ -t12349c.scala:15: error: weaker access privileges in overriding +t12349c.scala:13: error: weaker access privileges in overriding def a6(): Unit (defined in class t12349a) override should be public protected[pkg] override def a6(): Unit = println("Inner12349c#a6()") // weaker access privileges ^ -t12349c.scala:16: error: weaker access privileges in overriding +t12349c.scala:14: error: weaker access privileges in overriding def a7(): Unit (defined in class t12349a) override should be public private[pkg] override def a7(): Unit = println("Inner12349c#a7()") // weaker access privileges ^ -t12349c.scala:17: error: weaker access privileges in overriding +t12349c.scala:15: error: weaker access privileges in overriding def a8(): Unit (defined in class t12349a) override should be public - protected[this] override def a8(): Unit = println("Inner12349c#a8()") // weaker access privileges + protected[Inner12349c] override def a8(): Unit = println("Inner12349c#a8()") // weaker access privileges + ^ +t12349c.scala:17: error: weaker access privileges in overriding +def aA(): Unit (defined in class t12349a) + override should be public + protected[this] override def aA(): Unit = println("Inner12349c#aA()") // weaker access privileges ^ t12349c.scala:18: error: weaker access privileges in overriding -def a9(): Unit (defined in class t12349a) +def aB(): Unit (defined in class t12349a) override should not be private - private[this] override def a9(): Unit = println("Inner12349c#a9()") // weaker access privileges + private[this] override def aB(): Unit = println("Inner12349c#aB()") // weaker access privileges ^ t12349c.scala:22: error: weaker access privileges in overriding protected[package t12349] def b3(): Unit (defined in class t12349a) @@ -170,79 +191,93 @@ protected[package t12349] def b7(): Unit (defined in class t12349a) override should at least be protected[t12349] private[pkg] override def b7(): Unit = println("Inner12349c#b7()") // weaker access privileges ^ -t12349c.scala:28: error: weaker access privileges in overriding -protected[package t12349] def b9(): Unit (defined in class t12349a) +t12349c.scala:30: error: weaker access privileges in overriding +protected[package t12349] def bB(): Unit (defined in class t12349a) override should not be private - private[this] override def b9(): Unit = println("Inner12349c#b9()") // weaker access privileges + private[this] override def bB(): Unit = println("Inner12349c#bB()") // weaker access privileges ^ -t12349c.scala:31: error: weaker access privileges in overriding +t12349c.scala:33: error: weaker access privileges in overriding private[package t12349] def c2(): Unit (defined in class t12349a) override should at least be private[t12349] protected override def c2(): Unit = println("Inner12349c#c2()") // weaker access privileges ^ -t12349c.scala:32: error: weaker access privileges in overriding +t12349c.scala:34: error: weaker access privileges in overriding private[package t12349] def c3(): Unit (defined in class t12349a) override should not be private private override def c3(): Unit = println("Inner12349c#c3()") // weaker access privileges ^ -t12349c.scala:33: error: weaker access privileges in overriding +t12349c.scala:35: error: weaker access privileges in overriding private[package t12349] def c4(): Unit (defined in class t12349a) override should at least be private[t12349] protected[t12349c] override def c4(): Unit = println("Inner12349c#c4()") // weaker access privileges ^ -t12349c.scala:34: error: weaker access privileges in overriding +t12349c.scala:36: error: weaker access privileges in overriding private[package t12349] def c5(): Unit (defined in class t12349a) override should at least be private[t12349] private[t12349c] override def c5(): Unit = println("Inner12349c#c5()") // weaker access privileges ^ -t12349c.scala:35: error: weaker access privileges in overriding +t12349c.scala:37: error: weaker access privileges in overriding private[package t12349] def c6(): Unit (defined in class t12349a) override should at least be private[t12349] protected[pkg] override def c6(): Unit = println("Inner12349c#c6()") // weaker access privileges ^ -t12349c.scala:36: error: weaker access privileges in overriding +t12349c.scala:38: error: weaker access privileges in overriding private[package t12349] def c7(): Unit (defined in class t12349a) override should at least be private[t12349] private[pkg] override def c7(): Unit = println("Inner12349c#c7()") // weaker access privileges ^ -t12349c.scala:37: error: weaker access privileges in overriding +t12349c.scala:39: error: weaker access privileges in overriding private[package t12349] def c8(): Unit (defined in class t12349a) override should at least be private[t12349] - protected[this] override def c8(): Unit = println("Inner12349c#c8()") // weaker access privileges + protected[Inner12349c] override def c8(): Unit = println("Inner12349c#c8()") // weaker access privileges + ^ +t12349c.scala:41: error: weaker access privileges in overriding +private[package t12349] def cA(): Unit (defined in class t12349a) + override should at least be private[t12349] + protected[this] override def cA(): Unit = println("Inner12349c#cA()") // weaker access privileges ^ -t12349c.scala:38: error: weaker access privileges in overriding -private[package t12349] def c9(): Unit (defined in class t12349a) +t12349c.scala:42: error: weaker access privileges in overriding +private[package t12349] def cB(): Unit (defined in class t12349a) override should not be private - private[this] override def c9(): Unit = println("Inner12349c#c9()") // overrides nothing (invisible) + private[this] override def cB(): Unit = println("Inner12349c#cB()") // weaker access privileges ^ -t12349c.scala:30: error: method c1 overrides nothing +t12349c.scala:32: error: method c1 overrides nothing override def c1(): Unit = println("Inner12349c#c1()") // overrides nothing (invisible) ^ -t12349c.scala:40: error: method d1 overrides nothing +t12349c.scala:44: error: method d1 overrides nothing override def d1(): Unit = println("Inner12349c#d1()") // overrides nothing ^ -t12349c.scala:41: error: method d2 overrides nothing +t12349c.scala:45: error: method d2 overrides nothing protected override def d2(): Unit = println("Inner12349c#d2()") // overrides nothing ^ -t12349c.scala:42: error: method d3 overrides nothing +t12349c.scala:46: error: method d3 overrides nothing private override def d3(): Unit = println("Inner12349c#d3()") // overrides nothing ^ -t12349c.scala:43: error: method d4 overrides nothing +t12349c.scala:47: error: method d4 overrides nothing protected[t12349c] override def d4(): Unit = println("Inner12349c#d4()") // overrides nothing ^ -t12349c.scala:44: error: method d5 overrides nothing +t12349c.scala:48: error: method d5 overrides nothing private[t12349c] override def d5(): Unit = println("Inner12349c#d5()") // overrides nothing ^ -t12349c.scala:45: error: method d6 overrides nothing +t12349c.scala:49: error: method d6 overrides nothing protected[pkg] override def d6(): Unit = println("Inner12349c#d6()") // overrides nothing ^ -t12349c.scala:46: error: method d7 overrides nothing +t12349c.scala:50: error: method d7 overrides nothing private[pkg] override def d7(): Unit = println("Inner12349c#d7()") // overrides nothing ^ -t12349c.scala:47: error: method d8 overrides nothing - protected[this] override def d8(): Unit = println("Inner12349c#d8()") // overrides nothing +t12349c.scala:51: error: method d8 overrides nothing + protected[Inner12349c] override def d8(): Unit = println("Inner12349c#d8()") // overrides nothing + ^ +t12349c.scala:53: error: method dA overrides nothing + protected[this] override def dA(): Unit = println("Inner12349c#dA()") // overrides nothing ^ -t12349c.scala:48: error: method d9 overrides nothing - private[this] override def d9(): Unit = println("Inner12349c#d9()") // overrides nothing +t12349c.scala:54: error: method dB overrides nothing + private[this] override def dB(): Unit = println("Inner12349c#dB()") // overrides nothing ^ -57 errors +t12349c.scala:40: error: method c9 overrides nothing + private[Inner12349c] override def c9(): Unit = println("Inner12349c#c9()") // weaker access privileges + ^ +t12349c.scala:52: error: method d9 overrides nothing + private[Inner12349c] override def d9(): Unit = println("Inner12349c#d9()") // overrides nothing + ^ +66 errors diff --git a/test/files/neg/t12349/t12349a.java b/test/files/neg/t12349/t12349a.java index db9de0b0a539..f1b2cb51af39 100644 --- a/test/files/neg/t12349/t12349a.java +++ b/test/files/neg/t12349/t12349a.java @@ -11,6 +11,8 @@ public class t12349a { public void a7() { System.out.println("t12349a#a7()"); } public void a8() { System.out.println("t12349a#a8()"); } public void a9() { System.out.println("t12349a#a9()"); } + public void aA() { System.out.println("t12349a#aA()"); } + public void aB() { System.out.println("t12349a#aB()"); } protected void b1() { System.out.println("t12349a#b1()"); } protected void b2() { System.out.println("t12349a#b2()"); } @@ -21,6 +23,8 @@ public class t12349a { protected void b7() { System.out.println("t12349a#b7()"); } protected void b8() { System.out.println("t12349a#b8()"); } protected void b9() { System.out.println("t12349a#b9()"); } + protected void bA() { System.out.println("t12349a#bA()"); } + protected void bB() { System.out.println("t12349a#bB()"); } void c1() { System.out.println("t12349a#c1()"); } void c2() { System.out.println("t12349a#c2()"); } @@ -31,6 +35,8 @@ public class t12349a { void c7() { System.out.println("t12349a#c7()"); } void c8() { System.out.println("t12349a#c8()"); } void c9() { System.out.println("t12349a#c9()"); } + void cA() { System.out.println("t12349a#cA()"); } + void cB() { System.out.println("t12349a#cB()"); } private void d1() { System.out.println("t12349a#d1()"); } private void d2() { System.out.println("t12349a#d2()"); } @@ -41,5 +47,7 @@ public class t12349a { private void d7() { System.out.println("t12349a#d7()"); } private void d8() { System.out.println("t12349a#d8()"); } private void d9() { System.out.println("t12349a#d9()"); } + private void dA() { System.out.println("t12349a#dA()"); } + private void dB() { System.out.println("t12349a#dB()"); } } diff --git a/test/files/neg/t12349/t12349b.scala b/test/files/neg/t12349/t12349b.scala index 38b3309779b3..2d72c8124c32 100644 --- a/test/files/neg/t12349/t12349b.scala +++ b/test/files/neg/t12349/t12349b.scala @@ -10,8 +10,10 @@ object t12349b { private[t12349b] override def a5(): Unit = println("Inner12349b#a5()") // weaker access privileges protected[t12349] override def a6(): Unit = println("Inner12349b#a6()") // weaker access privileges private[t12349] override def a7(): Unit = println("Inner12349b#a7()") // weaker access privileges - protected[this] override def a8(): Unit = println("Inner12349b#a8()") // weaker access privileges - private[this] override def a9(): Unit = println("Inner12349b#a9()") // weaker access privileges + protected[Inner12349b] override def a8(): Unit = println("Inner12349b#a8()") // weaker access privileges + private[Inner12349b] override def a9(): Unit = println("Inner12349b#a9()") // weaker access privileges + protected[this] override def aA(): Unit = println("Inner12349b#aA()") // weaker access privileges + private[this] override def aB(): Unit = println("Inner12349b#aB()") // weaker access privileges override def b1(): Unit = println("Inner12349b#b1()") protected override def b2(): Unit = println("Inner12349b#b2()") @@ -20,8 +22,10 @@ object t12349b { private[t12349b] override def b5(): Unit = println("Inner12349b#b5()") // weaker access privileges protected[t12349] override def b6(): Unit = println("Inner12349b#b6()") private[t12349] override def b7(): Unit = println("Inner12349b#b7()") // weaker access privileges - protected[this] override def b8(): Unit = println("Inner12349b#b8()") // [#12349] - not fixed by PR #9525 - private[this] override def b9(): Unit = println("Inner12349b#b9()") // weaker access privileges + protected[Inner12349b] override def b8(): Unit = println("Inner12349b#b8()") // [#12349] - not fixed by PR #9525 + private[Inner12349b] override def b9(): Unit = println("Inner12349b#b9()") // weaker access privileges + protected[this] override def bA(): Unit = println("Inner12349b#bA()") // [#12349] - not fixed by PR #9525 + private[this] override def bB(): Unit = println("Inner12349b#bB()") // weaker access privileges override def c1(): Unit = println("Inner12349b#c1()") protected override def c2(): Unit = println("Inner12349b#c2()") // weaker access privileges @@ -30,8 +34,10 @@ object t12349b { private[t12349b] override def c5(): Unit = println("Inner12349b#c5()") // weaker access privileges protected[t12349] override def c6(): Unit = println("Inner12349b#c6()") private[t12349] override def c7(): Unit = println("Inner12349b#c7()") - protected[this] override def c8(): Unit = println("Inner12349b#c8()") // weaker access privileges - private[this] override def c9(): Unit = println("Inner12349b#c9()") // weaker access privileges + protected[Inner12349b] override def c8(): Unit = println("Inner12349b#c8()") // weaker access privileges + private[Inner12349b] override def c9(): Unit = println("Inner12349b#c9()") // weaker access privileges + protected[this] override def cA(): Unit = println("Inner12349b#cA()") // weaker access privileges + private[this] override def cB(): Unit = println("Inner12349b#cB()") // weaker access privileges override def d1(): Unit = println("Inner12349b#d1()") // overrides nothing protected override def d2(): Unit = println("Inner12349b#d2()") // overrides nothing @@ -40,8 +46,10 @@ object t12349b { private[t12349b] override def d5(): Unit = println("Inner12349b#d5()") // overrides nothing protected[t12349] override def d6(): Unit = println("Inner12349b#d6()") // overrides nothing private[t12349] override def d7(): Unit = println("Inner12349b#d7()") // overrides nothing - protected[this] override def d8(): Unit = println("Inner12349b#d8()") // overrides nothing - private[this] override def d9(): Unit = println("Inner12349b#d9()") // overrides nothing + protected[Inner12349b] override def d8(): Unit = println("Inner12349b#d8()") // overrides nothing + private[Inner12349b] override def d9(): Unit = println("Inner12349b#d9()") // overrides nothing + protected[this] override def dA(): Unit = println("Inner12349b#dA()") // overrides nothing + private[this] override def dB(): Unit = println("Inner12349b#dB()") // overrides nothing } } diff --git a/test/files/neg/t12349/t12349c.scala b/test/files/neg/t12349/t12349c.scala index 942991a22430..e86cfd9a79bf 100644 --- a/test/files/neg/t12349/t12349c.scala +++ b/test/files/neg/t12349/t12349c.scala @@ -1,7 +1,5 @@ package t12349 -import t12349.t12349a - package pkg { object t12349c { @@ -14,8 +12,10 @@ package pkg { private[t12349c] override def a5(): Unit = println("Inner12349c#a5()") // weaker access privileges protected[pkg] override def a6(): Unit = println("Inner12349c#a6()") // weaker access privileges private[pkg] override def a7(): Unit = println("Inner12349c#a7()") // weaker access privileges - protected[this] override def a8(): Unit = println("Inner12349c#a8()") // weaker access privileges - private[this] override def a9(): Unit = println("Inner12349c#a9()") // weaker access privileges + protected[Inner12349c] override def a8(): Unit = println("Inner12349c#a8()") // weaker access privileges + private[Inner12349c] override def a9(): Unit = println("Inner12349c#a9()") // weaker access privileges + protected[this] override def aA(): Unit = println("Inner12349c#aA()") // weaker access privileges + private[this] override def aB(): Unit = println("Inner12349c#aB()") // weaker access privileges override def b1(): Unit = println("Inner12349c#b1()") protected override def b2(): Unit = println("Inner12349c#b2()") @@ -24,8 +24,10 @@ package pkg { private[t12349c] override def b5(): Unit = println("Inner12349c#b5()") // weaker access privileges protected[pkg] override def b6(): Unit = println("Inner12349c#b6()") private[pkg] override def b7(): Unit = println("Inner12349c#b7()") // weaker access privileges - protected[this] override def b8(): Unit = println("Inner12349c#b8()") // [#12349] - not fixed by PR #9525 - private[this] override def b9(): Unit = println("Inner12349c#b9()") // weaker access privileges + protected[Inner12349c] override def b8(): Unit = println("Inner12349c#b8()") // [#12349] - not fixed by PR #9525 + private[Inner12349c] override def b9(): Unit = println("Inner12349c#b9()") // weaker access privileges + protected[this] override def bA(): Unit = println("Inner12349c#bA()") // [#12349] - not fixed by PR #9525 + private[this] override def bB(): Unit = println("Inner12349c#bB()") // weaker access privileges override def c1(): Unit = println("Inner12349c#c1()") // overrides nothing (invisible) protected override def c2(): Unit = println("Inner12349c#c2()") // weaker access privileges @@ -34,8 +36,10 @@ package pkg { private[t12349c] override def c5(): Unit = println("Inner12349c#c5()") // weaker access privileges protected[pkg] override def c6(): Unit = println("Inner12349c#c6()") // weaker access privileges private[pkg] override def c7(): Unit = println("Inner12349c#c7()") // weaker access privileges - protected[this] override def c8(): Unit = println("Inner12349c#c8()") // weaker access privileges - private[this] override def c9(): Unit = println("Inner12349c#c9()") // overrides nothing (invisible) + protected[Inner12349c] override def c8(): Unit = println("Inner12349c#c8()") // weaker access privileges + private[Inner12349c] override def c9(): Unit = println("Inner12349c#c9()") // weaker access privileges + protected[this] override def cA(): Unit = println("Inner12349c#cA()") // weaker access privileges + private[this] override def cB(): Unit = println("Inner12349c#cB()") // weaker access privileges override def d1(): Unit = println("Inner12349c#d1()") // overrides nothing protected override def d2(): Unit = println("Inner12349c#d2()") // overrides nothing @@ -44,8 +48,10 @@ package pkg { private[t12349c] override def d5(): Unit = println("Inner12349c#d5()") // overrides nothing protected[pkg] override def d6(): Unit = println("Inner12349c#d6()") // overrides nothing private[pkg] override def d7(): Unit = println("Inner12349c#d7()") // overrides nothing - protected[this] override def d8(): Unit = println("Inner12349c#d8()") // overrides nothing - private[this] override def d9(): Unit = println("Inner12349c#d9()") // overrides nothing + protected[Inner12349c] override def d8(): Unit = println("Inner12349c#d8()") // overrides nothing + private[Inner12349c] override def d9(): Unit = println("Inner12349c#d9()") // overrides nothing + protected[this] override def dA(): Unit = println("Inner12349c#dA()") // overrides nothing + private[this] override def dB(): Unit = println("Inner12349c#dB()") // overrides nothing } } diff --git a/test/files/neg/t12494.check b/test/files/neg/t12494.check new file mode 100644 index 000000000000..b408a1af431e --- /dev/null +++ b/test/files/neg/t12494.check @@ -0,0 +1,164 @@ +[running phase parser on t12494.scala] +[running phase namer on t12494.scala] +[running phase packageobjects on t12494.scala] +[running phase typer on t12494.scala] +[running phase superaccessors on t12494.scala] +[log superaccessors] [context] ++ t12494.scala / Import(value ) +[log superaccessors] [context] ++ t12494.scala / Import(value ) +[log superaccessors] [context] ++ t12494.scala / Import(value ) +[log superaccessors] [context] ++ t12494.scala / EmptyTree +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / Ident() +[log superaccessors] [context] ++ t12494.scala / Ident() +[log superaccessors] [context] ++ t12494.scala / term X +[log superaccessors] [context] ++ t12494.scala / term X +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term m +[log superaccessors] [context] ++ t12494.scala / term m +[log superaccessors] [context] ++ t12494.scala / type C +[log superaccessors] [context] ++ t12494.scala / type C +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term C +[log superaccessors] [context] ++ t12494.scala / term C +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / type C2 +[log superaccessors] [context] ++ t12494.scala / type C2 +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term test +[log superaccessors] [context] ++ t12494.scala / term test +[log superaccessors] [context] ++ t12494.scala / term Y +[log superaccessors] [context] ++ t12494.scala / term Y +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term n +[log superaccessors] [context] ++ t12494.scala / term n +[log superaccessors] [context] ++ t12494.scala / type C +[log superaccessors] [context] ++ t12494.scala / type C +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / type X +[log superaccessors] [context] ++ t12494.scala / type X +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term x +[log superaccessors] [context] ++ t12494.scala / term x +[log superaccessors] [context] ++ t12494.scala / term X +[log superaccessors] [context] ++ t12494.scala / term X +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term y +[log superaccessors] [context] ++ t12494.scala / term y +[log superaccessors] [context] ++ t12494.scala / term y +[log superaccessors] [context] ++ t12494.scala / term y +[log superaccessors] [context] ++ t12494.scala / term C +[log superaccessors] [context] ++ t12494.scala / term C +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / type C2 +[log superaccessors] [context] ++ t12494.scala / type C2 +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term test +[log superaccessors] [context] ++ t12494.scala / term test +[log superaccessors] In trait Base, renaming g -> Base$$g +[log superaccessors] Expanded 'g' to 'Base$$g' in trait Base +[log superaccessors] In trait Base, renaming h -> Base$$h +[log superaccessors] Expanded 'h' to 'Base$$h' in trait Base +[log superaccessors] In trait Base, renaming p -> Base$$p +[log superaccessors] Expanded 'p' to 'Base$$p' in trait Base +[log superaccessors] [context] ++ t12494.scala / type Base +[log superaccessors] [context] ++ t12494.scala / type Base +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term g +[log superaccessors] [context] ++ t12494.scala / term g +[log superaccessors] [context] ++ t12494.scala / term h +[log superaccessors] [context] ++ t12494.scala / term h +[log superaccessors] [context] ++ t12494.scala / term p +[log superaccessors] [context] ++ t12494.scala / term p +[log superaccessors] [context] ++ t12494.scala / term Base +[log superaccessors] [context] ++ t12494.scala / term Base +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / type Child +[log superaccessors] [context] ++ t12494.scala / type Child +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term g +[log superaccessors] [context] ++ t12494.scala / term g +[log superaccessors] [context] ++ t12494.scala / term h +[log superaccessors] [context] ++ t12494.scala / term h +[log superaccessors] [context] ++ t12494.scala / term p +[log superaccessors] [context] ++ t12494.scala / term p +[running phase extmethods on t12494.scala] +[running phase pickler on t12494.scala] +[running phase refchecks on t12494.scala] +t12494.scala:9: error: weaker access privileges in overriding + protected[trait C] def f: scala.this.Int (defined in trait C) + override should at least be protected[C]; + found : scala.this.Int + required: scala.this.Int + protected[C] def f: Int = 42 // no, limitation + ^ +t12494.scala:28: error: weaker access privileges in overriding + protected[trait C] def f: scala.this.Int (defined in trait C) + override should at least be protected[C]; + found : scala.this.Int + required: scala.this.Int + protected[C] def f: Int = 42 // no + ^ +t12494.scala:47: error: class Child needs to be abstract. +Missing implementations for 3 members of trait Base. + private[trait Base] def g: scala.this.Int = ??? + private[trait Base] def h: scala.this.Int = ??? + private[trait Base] def p: scala.this.Int = ??? + + class Child extends Base { + ^ +t12494.scala:50: error: method g overrides nothing + override private[Base] def g: Int = 42 // ok, companion + ^ +t12494.scala:51: error: method h overrides nothing + override protected[Base] def h: Int = 42 // ok, private[C] widens to protected[C] + ^ +t12494.scala:52: error: method p overrides nothing + override protected def p: Int = 42 // error, protected only overrides protected + ^ +6 errors diff --git a/test/files/neg/t12494.scala b/test/files/neg/t12494.scala new file mode 100644 index 000000000000..4d2f27e99b85 --- /dev/null +++ b/test/files/neg/t12494.scala @@ -0,0 +1,54 @@ +// scalac: -Ylog:superaccessors -Ydebug +object X { + def m: Int = { + trait C { + protected[C] def f: Int + } + object C { + class C2 extends C { + protected[C] def f: Int = 42 // no, limitation + def test = f + } + } + new C.C2().test + } +} +object Y { + def n: Int = { + trait C { + protected[C] def f: Int + } + class X { private def x = 17 } + locally { + object X { + val y = 27 + } + object C { + class C2 extends C { + protected[C] def f: Int = 42 // no + def test = f + X.y + } + } + new C.C2().test + } + } +} + +// other combinations +// mangling qualified privates says: +// Expanded 'g' to 'Base$$g' in trait Base +trait Base { + protected[Base] def f: Int + private[Base] def g: Int + private[Base] def h: Int + private[Base] def p: Int +} +object Base { + class Child extends Base { + override protected[Base] def f: Int = 42 // ok, companion + // was: overrides nothing (because of name mangling) + override private[Base] def g: Int = 42 // ok, companion + override protected[Base] def h: Int = 42 // ok, private[C] widens to protected[C] + override protected def p: Int = 42 // error, protected only overrides protected + } +} diff --git a/test/files/neg/t12495.check b/test/files/neg/t12495.check new file mode 100644 index 000000000000..52c058692538 --- /dev/null +++ b/test/files/neg/t12495.check @@ -0,0 +1,12 @@ +t12495.scala:4: warning: discarded non-Unit value of type (Int, Int) + def g = f(42, 27) + ^ +t12495.scala:4: warning: adapted the argument list to expected Unit type: arguments will be discarded + signature: Function1.apply(v1: T1): R + given arguments: 42, 27 + after adaptation: Function1((42, 27): Unit) + def g = f(42, 27) + ^ +error: No warnings can be incurred under -Werror. +2 warnings +1 error diff --git a/test/files/neg/t12495.scala b/test/files/neg/t12495.scala new file mode 100644 index 000000000000..5be5e3796c22 --- /dev/null +++ b/test/files/neg/t12495.scala @@ -0,0 +1,5 @@ +// scalac: -Werror -Xlint:arg-discard,adapted-args -Wvalue-discard +class C { + val f = (u: Unit) => println(s"[$u]") + def g = f(42, 27) +} diff --git a/test/files/neg/t12499.check b/test/files/neg/t12499.check new file mode 100644 index 000000000000..c49779c18ec4 --- /dev/null +++ b/test/files/neg/t12499.check @@ -0,0 +1,8 @@ +t12499.scala:455: warning: Cannot check match for unreachability. +The analysis required more space than allowed. +Please try with scalac -Ypatmat-exhaust-depth 60 or -Ypatmat-exhaust-depth off. + implicit val converter: Thing[Phantom.TypeA] => String = { + ^ +error: No warnings can be incurred under -Werror. +1 warning +1 error diff --git a/test/files/neg/t12499.scala b/test/files/neg/t12499.scala new file mode 100644 index 000000000000..91f397f12b24 --- /dev/null +++ b/test/files/neg/t12499.scala @@ -0,0 +1,591 @@ +// scalac: -Werror -Ystop-after:patmat -Ypatmat-exhaust-depth 30 +sealed trait Phantom[A] {} + +object Phantom { + type TypeA +} + +sealed abstract class ThingType(val v: Int) + +private object ThingType { + case object A extends ThingType(1) + case object B extends ThingType(-10) + case object C extends ThingType(-5) + case object D extends ThingType(15) + case object E extends ThingType(-16) +} + +sealed abstract class Thing[A](val thingType: ThingType) + +object Thing { + sealed abstract class ThingA[A] extends Thing[A](ThingType.A) + sealed abstract class ThingB[A] extends Thing[A](ThingType.B) + sealed abstract class ThingC[A] extends Thing[A](ThingType.C) + sealed abstract class ThingD[A] extends Thing[A](ThingType.D) + sealed abstract class ThingE[A] extends Thing[A](ThingType.E) +} + +object Stuff extends Phantom[Phantom.TypeA] { + import Phantom.TypeA + import Thing._ + + sealed abstract class OM(val id: String) extends ThingA[TypeA] { + def linkedA: ThingA[TypeA] + } + + case object OM1L extends ThingA[TypeA] + + case object OM1 extends OM("1") { + override def linkedA: ThingA[TypeA] = OM1L + } + + case object OM2L extends ThingA[TypeA] + + case object OM2 extends OM("2") { + override def linkedA: ThingA[TypeA] = OM2L + } + + case object OM3L extends ThingA[TypeA] + + case object OM3 extends OM("3") { + override def linkedA: ThingA[TypeA] = OM3L + } + + case object OM4L extends ThingA[TypeA] + + case object OM4 extends OM("4") { + override def linkedA: ThingA[TypeA] = OM4L + } + + case object OM5L extends ThingA[TypeA] + + case object OM5 extends OM("5") { + override def linkedA: ThingA[TypeA] = OM5L + } + + case object OM6L extends ThingA[TypeA] + + case object OM6 extends OM("6") { + override def linkedA: ThingA[TypeA] = OM6L + } + + case object OM7L extends ThingA[TypeA] + + case object OM7 extends OM("7") { + override def linkedA: ThingA[TypeA] = OM7L + } + + case object A1 extends ThingA[TypeA] + + case object A2 extends ThingA[TypeA] + + case object A3 extends ThingA[TypeA] + + sealed trait AA extends ThingA[TypeA] { + def linkedD: ThingD[TypeA] + } + + case object A4 extends AA { + override val linkedD: ThingD[TypeA] = A4L + } + + case object A5 extends AA { + override val linkedD: ThingD[TypeA] = A5L + } + + case object A6 extends AA { + override val linkedD: ThingD[TypeA] = A6L + } + + case object A7 extends AA { + override val linkedD: ThingD[TypeA] = A7L + } + + case object A8 extends AA { + override val linkedD: ThingD[TypeA] = A8L + } + + case object A9 extends AA { + override val linkedD: ThingD[TypeA] = A9L + } + + case object A10 extends AA { + override val linkedD: ThingD[TypeA] = A10L + } + + case object A11 extends AA { + override val linkedD: ThingD[TypeA] = A11L + } + + case object A12 extends AA { + override val linkedD: ThingD[TypeA] = A12L + } + + case object A13 extends AA { + override val linkedD: ThingD[TypeA] = A13L + } + + case object A14 extends AA { + override val linkedD: ThingD[TypeA] = A14L + } + + case object A15 extends AA { + override val linkedD: ThingD[TypeA] = A15L + } + + sealed abstract class G(val id: String) extends ThingA[TypeA] { + def linkedG: ThingA[TypeA] + } + + case object G1L extends ThingA[TypeA] + + case object G1 extends G("1") { + override def linkedG: ThingA[TypeA] = G1L + } + + case object G2L extends ThingA[TypeA] + + case object G2 extends G("2") { + override def linkedG: ThingA[TypeA] = G2L + } + + case object G3L extends ThingA[TypeA] + + case object G3 extends G("3") { + override def linkedG: ThingA[TypeA] = G3L + } + + case object G4L extends ThingA[TypeA] + + case object G4 extends G("4") { + override def linkedG: ThingA[TypeA] = G4L + } + + case object G5L extends ThingA[TypeA] + + case object G5 extends G("%") { + override def linkedG: ThingA[TypeA] = G5L + } + + case object G6L extends ThingA[TypeA] + + case object G6 extends G("6") { + override def linkedG: ThingA[TypeA] = G6L + } + + case object G7L extends ThingA[TypeA] + + case object G7 extends G("7") { + override def linkedG: ThingA[TypeA] = G7L + } + + case object G8L extends ThingA[TypeA] + + case object G8 extends G("8") { + override def linkedG: ThingA[TypeA] = G8L + } + + case object G9L extends ThingA[TypeA] + + case object G9 extends G("9") { + override def linkedG: ThingA[TypeA] = G9L + } + + case object G10L extends ThingA[TypeA] + + case object G10 extends G("10") { + override def linkedG: ThingA[TypeA] = G10L + } + + case object G11L extends ThingA[TypeA] + + case object G11 extends G("11") { + override def linkedG: ThingA[TypeA] = G11L + } + + sealed abstract class CC(val id: String) extends ThingA[TypeA] { + def c1: ThingA[TypeA] + + def c2: ThingA[TypeA] + + def c3: ThingA[TypeA] + + def c4: ThingD[TypeA] + + def c5: ThingD[TypeA] + } + + case object C1 extends CC("1") { + override def c1: ThingA[TypeA] = C11 + + override def c2: ThingA[TypeA] = C12 + + override def c3: ThingA[TypeA] = C13 + + override def c4: ThingD[TypeA] = C14 + + override def c5: ThingD[TypeA] = C15 + } + + case object C11 extends ThingA[TypeA] + + case object C12 extends ThingA[TypeA] + + case object C13 extends ThingA[TypeA] + + case object C2 extends CC("2") { + override def c1: ThingA[TypeA] = C21 + + override def c2: ThingA[TypeA] = C22 + + override def c3: ThingA[TypeA] = C23 + + override def c4: ThingD[TypeA] = C24 + + override def c5: ThingD[TypeA] = C25 + } + + case object C21 extends ThingA[TypeA] + + case object C22 extends ThingA[TypeA] + + case object C23 extends ThingA[TypeA] + + case object SN extends ThingC[TypeA] + + case object CLC extends ThingE[TypeA] + + case object SW extends ThingE[TypeA] + + case object A4L extends ThingD[TypeA] + + case object A5L extends ThingD[TypeA] + + case object A6L extends ThingD[TypeA] + + case object A7L extends ThingD[TypeA] + + case object A8L extends ThingD[TypeA] + + case object A9L extends ThingD[TypeA] + + case object A10L extends ThingD[TypeA] + + case object A11L extends ThingD[TypeA] + + case object A12L extends ThingD[TypeA] + + case object A13L extends ThingD[TypeA] + + case object A14L extends ThingD[TypeA] + + case object A15L extends ThingD[TypeA] + + case object ABC1 extends ThingD[TypeA] + + case object ABC2 extends ThingD[TypeA] + + case object ABC3 extends ThingD[TypeA] + + case object ABC4 extends ThingD[TypeA] + + case object ABC5 extends ThingD[TypeA] + + case object ABC6 extends ThingD[TypeA] + + case object ABC7 extends ThingD[TypeA] + + case object ABC8 extends ThingD[TypeA] + + case object ABC9 extends ThingD[TypeA] + + case object ABC10 extends ThingD[TypeA] + + case object C14 extends ThingD[TypeA] + + case object C15 extends ThingD[TypeA] + + case object C24 extends ThingD[TypeA] + + case object C25 extends ThingD[TypeA] + + case object ASD1 extends ThingD[TypeA] + + case object ASD2 extends ThingD[TypeA] + + case object ASD3 extends ThingD[TypeA] + + case object ASD4 extends ThingD[TypeA] + + case object ASD5 extends ThingE[TypeA] + + case object ASD6 extends ThingE[TypeA] + + sealed trait IR extends ThingE[TypeA] { + def linkedIR1: ThingD[TypeA] + def linkedIR2: ThingD[TypeA] + } + + case object IR11 extends ThingD[TypeA] + + case object IR12 extends ThingD[TypeA] + + case object IR1 extends IR { + override def linkedIR1: ThingD[TypeA] = IR11 + + override def linkedIR2: ThingD[TypeA] = IR12 + } + + case object IR21 extends ThingD[TypeA] + + case object IR22 extends ThingD[TypeA] + + case object IR2 extends IR { + override def linkedIR1: ThingD[TypeA] = IR21 + override def linkedIR2: ThingD[TypeA] = IR22 + } + + case object QW1 extends ThingE[TypeA] + + case object QW2 extends ThingE[TypeA] + + case object QW3 extends ThingE[TypeA] + + case object QW4 extends ThingE[TypeA] + + case object QW5 extends ThingE[TypeA] + + case object QW6 extends ThingE[TypeA] + + sealed abstract class IE(val id: String) extends ThingA[TypeA] { + def linkedIE1: ThingE[TypeA] + def linkedIE2: ThingE[TypeA] + def linkedIE3: Thing[TypeA] + def linkedIE4: Thing[TypeA] + } + + case object IE1 extends IE("1") { + override val linkedIE1: ThingE[TypeA] = IE11 + override val linkedIE2: ThingE[TypeA] = IE12 + override val linkedIE3: ThingD[TypeA] = ABC3 + override val linkedIE4: ThingD[TypeA] = ABC4 + } + + case object IE11 extends ThingE[TypeA] + + case object IE12 extends ThingE[TypeA] + + case object IE2 extends IE("2") { + override val linkedIE1: ThingE[TypeA] = IE21 + override val linkedIE2: ThingE[TypeA] = IE22 + override val linkedIE3: ThingE[TypeA] = IE23 + override val linkedIE4: ThingE[TypeA] = IE24 + } + + case object IE21 extends ThingE[TypeA] + + case object IE22 extends ThingE[TypeA] + + case object IE23 extends ThingE[TypeA] + + case object IE24 extends ThingE[TypeA] + + sealed abstract class LA extends ThingC[TypeA] + + case object LA1 extends LA + + case object LA2 extends LA + + case object LA3 extends LA + + case object LA4 extends LA + + case object LA5 extends ThingC[TypeA] + + sealed abstract class MAD(val id: String) extends ThingC[TypeA] { + def otherId: String + def linkedMAD1: ThingC[TypeA] + def linkedMAD2: ThingC[TypeA] + def linkedMAD3: ThingD[TypeA] + def linkedMAD4: ThingC[TypeA] + def linkedMAD5: ThingC[TypeA] + def linkedMAD6: ThingC[TypeA] + } + + case object MAD11 extends ThingC[TypeA] + + case object MAD12 extends ThingC[TypeA] + + case object MAD13 extends ThingD[TypeA] + + case object MAD14 extends ThingC[TypeA] + + case object MAD15 extends ThingC[TypeA] + + case object MAD16 extends ThingC[TypeA] + + case object MAD1 extends MAD("1") { + override def otherId: String = "c1" + override def linkedMAD1: ThingC[TypeA] = MAD11 + override def linkedMAD2: ThingC[TypeA] = MAD12 + override def linkedMAD3: ThingD[TypeA] = MAD13 + override def linkedMAD4: ThingC[TypeA] = MAD14 + override def linkedMAD5: ThingC[TypeA] = MAD15 + override def linkedMAD6: ThingC[TypeA] = MAD16 + } + + case object MAD21 extends ThingC[TypeA] + case object MAD22 extends ThingC[TypeA] + case object MAD23 extends ThingD[TypeA] + case object MAD24 extends ThingC[TypeA] + case object MAD25 extends ThingC[TypeA] + case object MAD26 extends ThingC[TypeA] + case object MAD2 extends MAD("2") { + override def otherId: String = "c2" + override def linkedMAD1: ThingC[TypeA] = MAD21 + override def linkedMAD2: ThingC[TypeA] = MAD22 + override def linkedMAD3: ThingD[TypeA] = MAD23 + override def linkedMAD4: ThingC[TypeA] = MAD24 + override def linkedMAD5: ThingC[TypeA] = MAD25 + override def linkedMAD6: ThingC[TypeA] = MAD26 + } +} + +object Matcher { + implicit val converter: Thing[Phantom.TypeA] => String = { + case Stuff.OM1 => "OM1" + case Stuff.OM1L => "OM1L" + case Stuff.OM2 => "OM2" + case Stuff.OM2L => "OM2L" + case Stuff.OM3 => "OM3" + case Stuff.OM3L => "OM3L" + case Stuff.OM4 => "OM4" + case Stuff.OM4L => "OM4L" + case Stuff.OM5 => "OM5" + case Stuff.OM5L => "OM5L" + case Stuff.OM6 => "OM6" + case Stuff.OM6L => "OM6L" + case Stuff.OM7 => "OM7" + case Stuff.OM7L => "OM7L" + case Stuff.A4 => "A4" + case Stuff.A5 => "A5" + case Stuff.A6 => "A6" + case Stuff.A7 => "A7" + case Stuff.A8 => "A8" + case Stuff.A9 => "A9" + case Stuff.A10 => "A10" + case Stuff.A11 => "A11" + case Stuff.A12 => "A12" + case Stuff.A13 => "A13" + case Stuff.A14 => "A14" + case Stuff.A15 => "A15" + case Stuff.A4L => "A4L" + case Stuff.A5L => "A5L" + case Stuff.A6L => "A6L" + case Stuff.A7L => "A7L" + case Stuff.A8L => "A8L" + case Stuff.A9L => "A9L" + case Stuff.A10L => "A10L" + case Stuff.A11L => "A11L" + case Stuff.A12L => "A12L" + case Stuff.A13L => "A13L" + case Stuff.A14L => "A14L" + case Stuff.A15L => "A15L" + case Stuff.ABC1 => "ABC1" + case Stuff.ABC2 => "ABC2" + case Stuff.ABC3 => "ABC3" + case Stuff.ABC4 => "ABC4" + case Stuff.QW1 => "QW1" + case Stuff.QW2 => "QW2" + case Stuff.IR1 => "IR1" + case Stuff.QW3 => "QW3" + case Stuff.QW4 => "QW4" + case Stuff.QW5 => "QW5" + case Stuff.QW6 => "QW6" + case Stuff.IE1 => "IE1" + case Stuff.IE11 => "IE11" + case Stuff.IE12 => "IE12" + case Stuff.IE2 => "IE2" + case Stuff.IE21 => "IE21" + case Stuff.IE22 => "IE22" + case Stuff.IE23 => "IE23" + case Stuff.IE24 => "IE24" + case Stuff.LA1 => "LA1" + case Stuff.LA2 => "LA2" + case Stuff.LA3 => "LA3" + case Stuff.LA5 => "LA5" + case Stuff.A3 => "A3" + case Stuff.ASD1 => "ASD1" + case Stuff.ASD5 => "ASD5" + case Stuff.ASD6 => "ASD6" + case Stuff.IR11 => "IR11" + case Stuff.IR12 => "IR12" + case Stuff.ASD2 => "ASD2" + case Stuff.ASD3 => "ASD3" + case Stuff.A1 => "A1" + case Stuff.A2 => "A2" + case Stuff.G1 => "G1" + case Stuff.G2 => "G2" + case Stuff.G3 => "G3" + case Stuff.G4 => "G4" + case Stuff.G5 => "G5" + case Stuff.G6 => "G6" + case Stuff.G1L => "G1L" + case Stuff.G2L => "G2L" + case Stuff.G3L => "G3L" + case Stuff.G4L => "G4L" + case Stuff.G5L => "G5L" + case Stuff.G6L => "G6L" + case Stuff.ABC5 => "ABC5" + case Stuff.ABC6 => "ABC6" + case Stuff.ABC7 => "ABC7" + case Stuff.ABC8 => "ABC8" + case Stuff.ABC9 => "ABC9" + case Stuff.ABC10 => "ABC10" + case Stuff.ASD4 => "ASD4" + case Stuff.SW => "SW" + case Stuff.C1 => "C1" + case Stuff.C11 => "C11" + case Stuff.IR2 => "IR2" + case Stuff.IR21 => "IR21" + case Stuff.IR22 => "IR22" + case Stuff.MAD14 => "MAD14" + case Stuff.MAD15 => "MAD15" + case Stuff.MAD11 => "MAD11" + case Stuff.MAD1 => "MAD1" + case Stuff.SN => "SN" + case Stuff.C12 => "C12" + case Stuff.C13 => "C13" + case Stuff.MAD12 => "MAD12" + case Stuff.C14 => "C14" + case Stuff.C15 => "C15" + case Stuff.G7 => "G7" + case Stuff.G7L => "G7L" + case Stuff.G8 => "G8" + case Stuff.G8L => "G8L" + case Stuff.C2 => "C2" + case Stuff.C21 => "C21" + case Stuff.C22 => "C22" + case Stuff.C23 => "C23" + case Stuff.C24 => "C24" + case Stuff.C25 => "C25" + case Stuff.MAD21 => "MAD21" + case Stuff.MAD22 => "MAD22" + case Stuff.MAD24 => "MAD24" + case Stuff.MAD25 => "MAD25" + case Stuff.MAD2 => "MAD2" + case Stuff.CLC => "CLC" + case Stuff.MAD13 => "MAD13" + case Stuff.MAD16 => "MAD16" + case Stuff.MAD23 => "MAD23" + case Stuff.MAD26 => "MAD26" + case Stuff.G9 => "G9" + case Stuff.G9L => "G9L" + case Stuff.LA4 => "LA4" + case Stuff.G10 => "G10" + case Stuff.G10L => "G10L" + case Stuff.G11 => "G11" + case Stuff.G11L => "G11L" + case _ => "unknown" + } +} diff --git a/test/files/neg/t12513.check b/test/files/neg/t12513.check new file mode 100644 index 000000000000..e156ba7fc383 --- /dev/null +++ b/test/files/neg/t12513.check @@ -0,0 +1,4 @@ +predefer_2.scala:10: error: not found: value x + println((x,y)) + ^ +1 error diff --git a/test/files/neg/t12513/predefer_2.scala b/test/files/neg/t12513/predefer_2.scala new file mode 100644 index 000000000000..6688ec5acbad --- /dev/null +++ b/test/files/neg/t12513/predefer_2.scala @@ -0,0 +1,12 @@ +// scalac: -Yimports:p.MyPredef,scala.Predef,scala + +package p { + object Test extends App { + println((x,y)) + } +} +package q { + object Test extends App { + println((x,y)) + } +} diff --git a/test/files/neg/t12513/predefined_1.scala b/test/files/neg/t12513/predefined_1.scala new file mode 100644 index 000000000000..eefdfa7011eb --- /dev/null +++ b/test/files/neg/t12513/predefined_1.scala @@ -0,0 +1,7 @@ + +package p + +object MyPredef { + private [p] def x = 27 + def y = 42 +} diff --git a/test/files/neg/t12513b.check b/test/files/neg/t12513b.check new file mode 100644 index 000000000000..6204fe160453 --- /dev/null +++ b/test/files/neg/t12513b.check @@ -0,0 +1,4 @@ +t12513b.scala:8: error: could not optimize @tailrec annotated method f: it contains a recursive call not in tail position + @T def f: Int = { f ; 42 } // the annotation worked: error, f is not tail recursive + ^ +1 error diff --git a/test/files/neg/t12513b.scala b/test/files/neg/t12513b.scala new file mode 100644 index 000000000000..51038b82a06d --- /dev/null +++ b/test/files/neg/t12513b.scala @@ -0,0 +1,9 @@ + +// scalac: -Werror -Xsource:3 + +object X { type T = annotation.tailrec } +object Y { type T = annotation.tailrec } +object Z { + import X.*, Y.* // OK, both T mean tailrec + @T def f: Int = { f ; 42 } // the annotation worked: error, f is not tail recursive +} diff --git a/test/files/neg/t12590.check b/test/files/neg/t12590.check new file mode 100644 index 000000000000..c52ae56a1b44 --- /dev/null +++ b/test/files/neg/t12590.check @@ -0,0 +1,6 @@ +t12590.scala:4: warning: local val a in method unusedLocal is never used + val a = 27 + ^ +error: No warnings can be incurred under -Werror. +1 warning +1 error diff --git a/test/files/neg/t12590.scala b/test/files/neg/t12590.scala new file mode 100644 index 000000000000..059391320087 --- /dev/null +++ b/test/files/neg/t12590.scala @@ -0,0 +1,7 @@ +// scalac: -Werror -Wunused:locals +class C { + def unusedLocal = { + val a = 27 + 42 + } +} diff --git a/test/files/neg/t12647.check b/test/files/neg/t12647.check new file mode 100644 index 000000000000..7b0f04dc8b10 --- /dev/null +++ b/test/files/neg/t12647.check @@ -0,0 +1,4 @@ +Test_3.scala:6: error: value value is not a member of Result + println(resolver.resolve.value) + ^ +1 error diff --git a/test/files/neg/t12647/Macro_1.scala b/test/files/neg/t12647/Macro_1.scala new file mode 100644 index 000000000000..748657682eec --- /dev/null +++ b/test/files/neg/t12647/Macro_1.scala @@ -0,0 +1,13 @@ + +// scalac: -Xsource:3 + +import scala.reflect.macros.blackbox.Context + +trait Result + +object Macros { + def impl(c: Context) = { + import c.universe._ + q"""new Result { def value = "Was this the answer you sought?" }""" + } +} diff --git a/test/files/neg/t12647/Resolve_2.scala b/test/files/neg/t12647/Resolve_2.scala new file mode 100644 index 000000000000..dab65c6a310b --- /dev/null +++ b/test/files/neg/t12647/Resolve_2.scala @@ -0,0 +1,13 @@ + +// scalac: -Xsource:3 + +import language.experimental.macros + +trait Resolver { + def resolve: Result = ??? +} + +class ValueResolver extends Resolver { + override def resolve = valueResult + def valueResult: Result = macro Macros.impl +} diff --git a/test/files/neg/t12647/Test_3.scala b/test/files/neg/t12647/Test_3.scala new file mode 100644 index 000000000000..152e5ddc4aa4 --- /dev/null +++ b/test/files/neg/t12647/Test_3.scala @@ -0,0 +1,7 @@ + +// scalac: -Xsource:3 + +object Test extends App { + val resolver = new ValueResolver + println(resolver.resolve.value) +} diff --git a/test/files/neg/t12691.check b/test/files/neg/t12691.check new file mode 100644 index 000000000000..5367c6238725 --- /dev/null +++ b/test/files/neg/t12691.check @@ -0,0 +1,110 @@ +t12691.scala:4: error: class Example needs to be abstract. +Missing implementations for 101 members. + // Members declared in Example + val f1: Int = ??? + val f10: Int = ??? + val f100: Int = ??? + val f11: Int = ??? + val f12: Int = ??? + val f13: Int = ??? + val f14: Int = ??? + val f15: Int = ??? + val f16: Int = ??? + val f17: Int = ??? + val f18: Int = ??? + val f19: Int = ??? + val f2: Int = ??? + val f20: Int = ??? + val f21: Int = ??? + val f22: Int = ??? + val f23: Int = ??? + val f24: Int = ??? + val f25: Int = ??? + val f26: Int = ??? + val f27: Int = ??? + val f28: Int = ??? + val f29: Int = ??? + val f3: Int = ??? + val f30: Int = ??? + val f31: Int = ??? + val f32: Int = ??? + val f33: Int = ??? + val f34: Int = ??? + val f35: Int = ??? + val f36: Int = ??? + val f37: Int = ??? + val f38: Int = ??? + val f39: Int = ??? + val f4: Int = ??? + val f40: Int = ??? + val f41: Int = ??? + val f42: Int = ??? + val f43: Int = ??? + val f44: Int = ??? + val f45: Int = ??? + val f46: Int = ??? + val f47: Int = ??? + val f48: Int = ??? + val f49: Int = ??? + val f5: Int = ??? + val f50: Int = ??? + val f51: Int = ??? + val f52: Int = ??? + val f53: Int = ??? + val f54: Int = ??? + val f55: Int = ??? + val f56: Int = ??? + val f57: Int = ??? + val f58: Int = ??? + val f59: Int = ??? + val f6: Int = ??? + val f60: Int = ??? + val f61: Int = ??? + val f62: Int = ??? + val f63: Int = ??? + val f64: Int = ??? + val f65: Int = ??? + val f66: Int = ??? + val f67: Int = ??? + val f68: Int = ??? + val f69: Int = ??? + val f7: Int = ??? + val f70: Int = ??? + val f71: Int = ??? + val f72: Int = ??? + val f73: Int = ??? + val f74: Int = ??? + val f75: Int = ??? + val f76: Int = ??? + val f77: Int = ??? + val f78: Int = ??? + val f79: Int = ??? + val f8: Int = ??? + val f80: Int = ??? + val f81: Int = ??? + val f82: Int = ??? + val f83: Int = ??? + val f84: Int = ??? + val f85: Int = ??? + val f86: Int = ??? + val f87: Int = ??? + val f88: Int = ??? + val f89: Int = ??? + val f9: Int = ??? + val f90: Int = ??? + val f91: Int = ??? + val f92: Int = ??? + val f93: Int = ??? + val f94: Int = ??? + val f95: Int = ??? + val f96: Int = ??? + val f97: Int = ??? + val f98: Int = ??? + val f99: Int = ??? + + // Members declared in Runner + def run(i: Int): Unit = ??? + +class Example extends Runner { + ^ +1 error diff --git a/test/files/neg/t12691.scala b/test/files/neg/t12691.scala new file mode 100644 index 000000000000..0047f7ae404b --- /dev/null +++ b/test/files/neg/t12691.scala @@ -0,0 +1,106 @@ +abstract class Runner { + def run(i: Int): Unit +} +class Example extends Runner { + def run(s: String) = () + val f1: Int + val f2: Int + val f3: Int + val f4: Int + val f5: Int + val f6: Int + val f7: Int + val f8: Int + val f9: Int + val f10: Int + val f11: Int + val f12: Int + val f13: Int + val f14: Int + val f15: Int + val f16: Int + val f17: Int + val f18: Int + val f19: Int + val f20: Int + val f21: Int + val f22: Int + val f23: Int + val f24: Int + val f25: Int + val f26: Int + val f27: Int + val f28: Int + val f29: Int + val f30: Int + val f31: Int + val f32: Int + val f33: Int + val f34: Int + val f35: Int + val f36: Int + val f37: Int + val f38: Int + val f39: Int + val f40: Int + val f41: Int + val f42: Int + val f43: Int + val f44: Int + val f45: Int + val f46: Int + val f47: Int + val f48: Int + val f49: Int + val f50: Int + val f51: Int + val f52: Int + val f53: Int + val f54: Int + val f55: Int + val f56: Int + val f57: Int + val f58: Int + val f59: Int + val f60: Int + val f61: Int + val f62: Int + val f63: Int + val f64: Int + val f65: Int + val f66: Int + val f67: Int + val f68: Int + val f69: Int + val f70: Int + val f71: Int + val f72: Int + val f73: Int + val f74: Int + val f75: Int + val f76: Int + val f77: Int + val f78: Int + val f79: Int + val f80: Int + val f81: Int + val f82: Int + val f83: Int + val f84: Int + val f85: Int + val f86: Int + val f87: Int + val f88: Int + val f89: Int + val f90: Int + val f91: Int + val f92: Int + val f93: Int + val f94: Int + val f95: Int + val f96: Int + val f97: Int + val f98: Int + val f99: Int + val f100: Int +} diff --git a/test/files/neg/t12691b.check b/test/files/neg/t12691b.check new file mode 100644 index 000000000000..14f2c613a5f6 --- /dev/null +++ b/test/files/neg/t12691b.check @@ -0,0 +1,109 @@ +t12691b.scala:4: error: class Example needs to be abstract. +Missing implementations for 100 members. + // Members declared in Example + val f1: Int = ??? + val f10: Int = ??? + val f11: Int = ??? + val f12: Int = ??? + val f13: Int = ??? + val f14: Int = ??? + val f15: Int = ??? + val f16: Int = ??? + val f17: Int = ??? + val f18: Int = ??? + val f19: Int = ??? + val f2: Int = ??? + val f20: Int = ??? + val f21: Int = ??? + val f22: Int = ??? + val f23: Int = ??? + val f24: Int = ??? + val f25: Int = ??? + val f26: Int = ??? + val f27: Int = ??? + val f28: Int = ??? + val f29: Int = ??? + val f3: Int = ??? + val f30: Int = ??? + val f31: Int = ??? + val f32: Int = ??? + val f33: Int = ??? + val f34: Int = ??? + val f35: Int = ??? + val f36: Int = ??? + val f37: Int = ??? + val f38: Int = ??? + val f39: Int = ??? + val f4: Int = ??? + val f40: Int = ??? + val f41: Int = ??? + val f42: Int = ??? + val f43: Int = ??? + val f44: Int = ??? + val f45: Int = ??? + val f46: Int = ??? + val f47: Int = ??? + val f48: Int = ??? + val f49: Int = ??? + val f5: Int = ??? + val f50: Int = ??? + val f51: Int = ??? + val f52: Int = ??? + val f53: Int = ??? + val f54: Int = ??? + val f55: Int = ??? + val f56: Int = ??? + val f57: Int = ??? + val f58: Int = ??? + val f59: Int = ??? + val f6: Int = ??? + val f60: Int = ??? + val f61: Int = ??? + val f62: Int = ??? + val f63: Int = ??? + val f64: Int = ??? + val f65: Int = ??? + val f66: Int = ??? + val f67: Int = ??? + val f68: Int = ??? + val f69: Int = ??? + val f7: Int = ??? + val f70: Int = ??? + val f71: Int = ??? + val f72: Int = ??? + val f73: Int = ??? + val f74: Int = ??? + val f75: Int = ??? + val f76: Int = ??? + val f77: Int = ??? + val f78: Int = ??? + val f79: Int = ??? + val f8: Int = ??? + val f80: Int = ??? + val f81: Int = ??? + val f82: Int = ??? + val f83: Int = ??? + val f84: Int = ??? + val f85: Int = ??? + val f86: Int = ??? + val f87: Int = ??? + val f88: Int = ??? + val f89: Int = ??? + val f9: Int = ??? + val f90: Int = ??? + val f91: Int = ??? + val f92: Int = ??? + val f93: Int = ??? + val f94: Int = ??? + val f95: Int = ??? + val f96: Int = ??? + val f97: Int = ??? + val f98: Int = ??? + val f99: Int = ??? + + // Members declared in Runner + def run(i: Int): Unit = ??? // Int does not match String in `def run(s: String): Unit` + +class Example extends Runner { + ^ +1 error diff --git a/test/files/neg/t12691b.scala b/test/files/neg/t12691b.scala new file mode 100644 index 000000000000..571936ec2a35 --- /dev/null +++ b/test/files/neg/t12691b.scala @@ -0,0 +1,105 @@ +abstract class Runner { + def run(i: Int): Unit +} +class Example extends Runner { + def run(s: String) = () + val f1: Int + val f2: Int + val f3: Int + val f4: Int + val f5: Int + val f6: Int + val f7: Int + val f8: Int + val f9: Int + val f10: Int + val f11: Int + val f12: Int + val f13: Int + val f14: Int + val f15: Int + val f16: Int + val f17: Int + val f18: Int + val f19: Int + val f20: Int + val f21: Int + val f22: Int + val f23: Int + val f24: Int + val f25: Int + val f26: Int + val f27: Int + val f28: Int + val f29: Int + val f30: Int + val f31: Int + val f32: Int + val f33: Int + val f34: Int + val f35: Int + val f36: Int + val f37: Int + val f38: Int + val f39: Int + val f40: Int + val f41: Int + val f42: Int + val f43: Int + val f44: Int + val f45: Int + val f46: Int + val f47: Int + val f48: Int + val f49: Int + val f50: Int + val f51: Int + val f52: Int + val f53: Int + val f54: Int + val f55: Int + val f56: Int + val f57: Int + val f58: Int + val f59: Int + val f60: Int + val f61: Int + val f62: Int + val f63: Int + val f64: Int + val f65: Int + val f66: Int + val f67: Int + val f68: Int + val f69: Int + val f70: Int + val f71: Int + val f72: Int + val f73: Int + val f74: Int + val f75: Int + val f76: Int + val f77: Int + val f78: Int + val f79: Int + val f80: Int + val f81: Int + val f82: Int + val f83: Int + val f84: Int + val f85: Int + val f86: Int + val f87: Int + val f88: Int + val f89: Int + val f90: Int + val f91: Int + val f92: Int + val f93: Int + val f94: Int + val f95: Int + val f96: Int + val f97: Int + val f98: Int + val f99: Int +} diff --git a/test/files/neg/t12704.check b/test/files/neg/t12704.check new file mode 100644 index 000000000000..2de8a06ca4df --- /dev/null +++ b/test/files/neg/t12704.check @@ -0,0 +1,7 @@ +t12704.scala:13: warning: match may not be exhaustive. +It would fail on the following input: L(_) + def t2(t: P) = t match { + ^ +error: No warnings can be incurred under -Werror. +1 warning +1 error diff --git a/test/files/neg/t12704.scala b/test/files/neg/t12704.scala new file mode 100644 index 000000000000..db43b888d029 --- /dev/null +++ b/test/files/neg/t12704.scala @@ -0,0 +1,16 @@ +// scalac: -Werror +class Out { + sealed trait P + case class K(x: Int) extends P + case class L(x: Int) extends P +} +class C[O <: Out](val o: O) { + import o._ + def t1(t: P) = t match { + case _: K => 0 + case _: L => 0 + } + def t2(t: P) = t match { + case _: K => 0 + } +} diff --git a/test/files/neg/t12715.check b/test/files/neg/t12715.check new file mode 100644 index 000000000000..729af9c43246 --- /dev/null +++ b/test/files/neg/t12715.check @@ -0,0 +1,7 @@ +t12715.scala:21: error: parent trait E has a super call to method B.f, which binds to the value D.f. Super calls can only target methods. +object O1 extends B with C with D with E + ^ +t12715.scala:22: error: parent trait E has a super call to method B.f, which binds to the value C.f. Super calls can only target methods. +object O2 extends B with C with E with D + ^ +2 errors diff --git a/test/files/neg/t12715.scala b/test/files/neg/t12715.scala new file mode 100644 index 000000000000..e3769141b4c4 --- /dev/null +++ b/test/files/neg/t12715.scala @@ -0,0 +1,23 @@ +trait A { + def f: String +} + +trait B extends A { + def f = "B"; +} + +trait C extends A { + override val f = "C" +} + +trait D extends C { + override val f = "D" +} + +trait E extends A with B { + def d = super.f +} + +object O1 extends B with C with D with E +object O2 extends B with C with E with D +object O3 extends B with E with C with D diff --git a/test/files/neg/t12715b.check b/test/files/neg/t12715b.check new file mode 100644 index 000000000000..3cdb24a74351 --- /dev/null +++ b/test/files/neg/t12715b.check @@ -0,0 +1,4 @@ +t12715b.scala:17: error: parent trait D has a super call to method B.f, which binds to the value C.f. Super calls can only target methods. + new A(10.0f) with C with D {} + ^ +1 error diff --git a/test/files/neg/t12715b.scala b/test/files/neg/t12715b.scala new file mode 100644 index 000000000000..9d89233dc85f --- /dev/null +++ b/test/files/neg/t12715b.scala @@ -0,0 +1,19 @@ +trait B { + def f: Float = 1.0f +} + +class A(override val f: Float) extends B + +trait C extends B { + abstract override val f = super.f + 100.0f +} + +trait D extends B { + abstract override val f = super.f + 1000.0f +} + +object Test { + def main(args: Array[String]): Unit = { + new A(10.0f) with C with D {} + } +} diff --git a/test/files/neg/t12720.check b/test/files/neg/t12720.check new file mode 100644 index 000000000000..603ba26094e4 --- /dev/null +++ b/test/files/neg/t12720.check @@ -0,0 +1,40 @@ +t12720.scala:15: error: missing argument list for method f*** in class D*** +Unapplied methods are only converted to functions when a function type is expected. +You can make this conversion explicit by writing `f _` or `f(_)` instead of `f`. + def f = d.f + ^ +t12720.scala:10: warning: private constructor in class Test*** is never used + private def this(stuff: Int) = this() + ^ +t12720.scala:20: warning: local method m*** in method forgone*** is never used + def m = 17 + ^ +t12720.scala:21: warning: local object j*** in method forgone*** is never used + object j + ^ +t12720.scala:22: warning: local var totally*** in method forgone*** is never used + var totally = 27 + ^ +t12720.scala:24: warning: local val z*** in method forgone*** is never used + val z = unset + ^ +t12720.scala:28: warning: private var misbegotten*** in class Test*** is never used + private var misbegotten: Int = 42 + ^ +t12720.scala:30: warning: private var forgotten (forgotten_=***) in class Test*** is never updated: consider using immutable val + private var forgotten: Int = 42 + ^ +t12720.scala:12: warning: private class Subtle*** in class Test*** is never used + private class Subtle + ^ +t12720.scala:23: warning: local var unset*** in method forgone*** is never updated: consider using immutable val + var unset: Int = 0 + ^ +t12720.scala:16: warning: parameter x*** in method g*** is never used + def g(x: Int) = println("error") + ^ +t12720.scala:25: warning: parameter y*** in anonymous function is never used + for (x <- Option(42); y <- Option(27) if x < i; res = x - y) yield res + ^ +11 warnings +1 error diff --git a/test/files/neg/t12720.scala b/test/files/neg/t12720.scala new file mode 100644 index 000000000000..48c659065c4f --- /dev/null +++ b/test/files/neg/t12720.scala @@ -0,0 +1,32 @@ +// scalac: -uniqid -Werror -Wunused +// hide: #\d+ +class C { + def f(x: Int) = "" +} +class D extends C { + def f(x: String) = "" +} +final class Test { + private def this(stuff: Int) = this() + + private class Subtle + + val d = new D + def f = d.f + def g(x: Int) = println("error") + + // -Vprint shows two symbols `y` + def forgone(i: Int) = { + def m = 17 + object j + var totally = 27 + var unset: Int = 0 + val z = unset + for (x <- Option(42); y <- Option(27) if x < i; res = x - y) yield res + } + + private var misbegotten: Int = 42 + def touch() = misbegotten = 17 + private var forgotten: Int = 42 + def fetch = forgotten +} diff --git a/test/files/neg/t12728.check b/test/files/neg/t12728.check new file mode 100644 index 000000000000..482707b6200a --- /dev/null +++ b/test/files/neg/t12728.check @@ -0,0 +1,162 @@ +t12728.scala:10: warning: dubious usage of method != with unit value + println(u.!=(x)) + ^ +t12728.scala:11: warning: dubious usage of method ## with unit value + println(u.##) + ^ +t12728.scala:12: warning: dubious usage of method == with unit value + println(u.==(x)) + ^ +t12728.scala:13: warning: dubious usage of method asInstanceOf with unit value + println(u.asInstanceOf[Any]) + ^ +t12728.scala:14: warning: dubious usage of method equals with unit value + println(u.equals(x)) + ^ +t12728.scala:15: warning: dubious usage of method hashCode with unit value + println(u.hashCode) + ^ +t12728.scala:16: warning: dubious usage of method isInstanceOf with unit value + println(u.isInstanceOf[Any]) + ^ +t12728.scala:17: warning: dubious usage of method toString with unit value + println(u.toString) + ^ +t12728.scala:20: warning: Widening conversion from Int to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(i.isNaN) + ^ +t12728.scala:20: warning: dubious usage of method isNaN with integer value + println(i.isNaN) + ^ +t12728.scala:21: warning: Widening conversion from Int to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(i.isInfinity) + ^ +t12728.scala:21: warning: dubious usage of method isInfinity with integer value + println(i.isInfinity) + ^ +t12728.scala:22: warning: Widening conversion from Int to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(i.isInfinite) + ^ +t12728.scala:22: warning: dubious usage of method isInfinite with integer value + println(i.isInfinite) + ^ +t12728.scala:23: warning: Widening conversion from Int to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(i.isFinite) + ^ +t12728.scala:23: warning: dubious usage of method isFinite with integer value + println(i.isFinite) + ^ +t12728.scala:24: warning: Widening conversion from Int to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(i.isPosInfinity) + ^ +t12728.scala:24: warning: dubious usage of method isPosInfinity with integer value + println(i.isPosInfinity) + ^ +t12728.scala:25: warning: Widening conversion from Int to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(i.isNegInfinity) + ^ +t12728.scala:25: warning: dubious usage of method isNegInfinity with integer value + println(i.isNegInfinity) + ^ +t12728.scala:27: warning: Widening conversion from Int to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(i.ceil) + ^ +t12728.scala:27: warning: dubious usage of method ceil with integer value + println(i.ceil) + ^ +t12728.scala:28: warning: Widening conversion from Int to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(i.floor) + ^ +t12728.scala:28: warning: dubious usage of method floor with integer value + println(i.floor) + ^ +t12728.scala:30: warning: Widening conversion from Long to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(l.isNaN) + ^ +t12728.scala:30: warning: dubious usage of method isNaN with integer value + println(l.isNaN) + ^ +t12728.scala:31: warning: Widening conversion from Long to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(l.isInfinity) + ^ +t12728.scala:31: warning: dubious usage of method isInfinity with integer value + println(l.isInfinity) + ^ +t12728.scala:32: warning: Widening conversion from Long to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(l.isInfinite) + ^ +t12728.scala:32: warning: dubious usage of method isInfinite with integer value + println(l.isInfinite) + ^ +t12728.scala:33: warning: Widening conversion from Long to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(l.isFinite) + ^ +t12728.scala:33: warning: dubious usage of method isFinite with integer value + println(l.isFinite) + ^ +t12728.scala:34: warning: Widening conversion from Long to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(l.isPosInfinity) + ^ +t12728.scala:34: warning: dubious usage of method isPosInfinity with integer value + println(l.isPosInfinity) + ^ +t12728.scala:35: warning: Widening conversion from Long to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(l.isNegInfinity) + ^ +t12728.scala:35: warning: dubious usage of method isNegInfinity with integer value + println(l.isNegInfinity) + ^ +t12728.scala:37: warning: Widening conversion from Long to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(l.ceil) + ^ +t12728.scala:37: warning: dubious usage of method ceil with integer value + println(l.ceil) + ^ +t12728.scala:38: warning: Widening conversion from Long to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(l.floor) + ^ +t12728.scala:38: warning: dubious usage of method floor with integer value + println(l.floor) + ^ +t12728.scala:40: warning: dubious usage of method isNaN with integer value + println(c.isNaN) + ^ +t12728.scala:41: warning: dubious usage of method isInfinity with integer value + println(c.isInfinity) + ^ +t12728.scala:42: warning: dubious usage of method isInfinite with integer value + println(c.isInfinite) + ^ +t12728.scala:43: warning: dubious usage of method isFinite with integer value + println(c.isFinite) + ^ +t12728.scala:44: warning: dubious usage of method isPosInfinity with integer value + println(c.isPosInfinity) + ^ +t12728.scala:45: warning: dubious usage of method isNegInfinity with integer value + println(c.isNegInfinity) + ^ +t12728.scala:47: warning: dubious usage of method ceil with integer value + println(c.ceil) + ^ +t12728.scala:48: warning: dubious usage of method floor with integer value + println(c.floor) + ^ +t12728.scala:54: warning: dubious usage of method toString with unit value + def g = new java.lang.StringBuilder("hi").setCharAt(0, 'H').toString // "()" + ^ +t12728.scala:14: warning: comparing values of types Unit and Any using `equals` unsafely bypasses cooperative equality; use `==` instead + println(u.equals(x)) + ^ +t12728.scala:26: warning: method round in class RichInt is deprecated (since 2.11.0): this is an integer type; there is no reason to round it. Perhaps you meant to call this on a floating-point value? + println(i.round) + ^ +t12728.scala:36: warning: method round in class RichLong is deprecated (since 2.11.0): this is an integer type; there is no reason to round it. Perhaps you meant to call this on a floating-point value? + println(l.round) + ^ +t12728.scala:46: warning: method round in class RichInt is deprecated (since 2.11.0): this is an integer type; there is no reason to round it. Perhaps you meant to call this on a floating-point value? + println(c.round) + ^ +error: No warnings can be incurred under -Werror. +53 warnings +1 error diff --git a/test/files/neg/t12728.scala b/test/files/neg/t12728.scala new file mode 100644 index 000000000000..6ccb0f19d639 --- /dev/null +++ b/test/files/neg/t12728.scala @@ -0,0 +1,55 @@ +// scalac: -Werror -Xlint + +class C { + val u = () + val i = 42 + val l = 42L + val c = 'c' + val x = null: Any + + println(u.!=(x)) + println(u.##) + println(u.==(x)) + println(u.asInstanceOf[Any]) + println(u.equals(x)) + println(u.hashCode) + println(u.isInstanceOf[Any]) + println(u.toString) + println(i.toString) + + println(i.isNaN) + println(i.isInfinity) + println(i.isInfinite) + println(i.isFinite) + println(i.isPosInfinity) + println(i.isNegInfinity) + println(i.round) + println(i.ceil) + println(i.floor) + + println(l.isNaN) + println(l.isInfinity) + println(l.isInfinite) + println(l.isFinite) + println(l.isPosInfinity) + println(l.isNegInfinity) + println(l.round) + println(l.ceil) + println(l.floor) + + println(c.isNaN) + println(c.isInfinity) + println(c.isInfinite) + println(c.isFinite) + println(c.isPosInfinity) + println(c.isNegInfinity) + println(c.round) + println(c.ceil) + println(c.floor) +} + +class UseCase { + def f = new scala.StringBuilder("hi").setCharAt(0, 'H').toString // "Hi" + + def g = new java.lang.StringBuilder("hi").setCharAt(0, 'H').toString // "()" +} diff --git a/test/files/neg/t1477.check b/test/files/neg/t1477.check index 032a7bb747c8..4893654ecf85 100644 --- a/test/files/neg/t1477.check +++ b/test/files/neg/t1477.check @@ -1,5 +1,5 @@ t1477.scala:13: error: volatile type member cannot override type member with non-volatile upper bound: type V <: Middle.this.D (defined in trait C) - type V <: (D with U) + type V <: (this.D with U) ^ 1 error diff --git a/test/files/neg/t1477.scala b/test/files/neg/t1477.scala index a9a6d678ca5f..9e4bcba224b7 100644 --- a/test/files/neg/t1477.scala +++ b/test/files/neg/t1477.scala @@ -10,7 +10,7 @@ object Test extends App { } trait Middle extends C { - type V <: (D with U) + type V <: (this.D with U) } class D extends Middle { diff --git a/test/files/neg/t2206.check b/test/files/neg/t2206.check index 5ea5570f9a90..9aca6ff9f50a 100644 --- a/test/files/neg/t2206.check +++ b/test/files/neg/t2206.check @@ -1,5 +1,9 @@ t2206.scala:10: error: value f is not a member of o.A - Note: implicit method ax is not applicable here because it comes after the application point and it lacks an explicit result type + Note: implicit method ax is not applicable here because it comes after the application point and it lacks an explicit result type. a.f() ^ +t2206.scala:13: warning: Implicit definition should have explicit type (inferred o.AX) + implicit def ax(a: A) = new AX + ^ +1 warning 1 error diff --git a/test/files/neg/t2421b.check b/test/files/neg/t2421b.check index 7c714f1c9bd7..644395fc6a12 100644 --- a/test/files/neg/t2421b.check +++ b/test/files/neg/t2421b.check @@ -1,4 +1,8 @@ t2421b.scala:12: error: could not find implicit value for parameter aa: Test.F[Test.A] f ^ +t2421b.scala:10: warning: Implicit definition should have explicit type (inferred Test.F[X]) + implicit def b[X <: B] = new F[X]() + ^ +1 warning 1 error diff --git a/test/files/neg/t2799.check b/test/files/neg/t2799.check index 866697676936..87419d7ce8cc 100644 --- a/test/files/neg/t2799.check +++ b/test/files/neg/t2799.check @@ -10,6 +10,12 @@ t2799.scala:8: warning: trait T is deprecated: other mother t2799.scala:9: warning: method int2float in object Int is deprecated (since 2.13.1): Implicit conversion from Int to Float is dangerous because it loses precision. Write `.toFloat` instead. def g() = implicitly[Int => Float] ^ +t2799.scala:16: warning: trait T is deprecated: other mother +object T extends T[String] { + ^ +t2799.scala:17: warning: class Bob is deprecated: hi mom + def t = Bob() // warn + ^ error: No warnings can be incurred under -Werror. -4 warnings +6 warnings 1 error diff --git a/test/files/neg/t2799.scala b/test/files/neg/t2799.scala index 2079ac2dc22c..abf78ab17a28 100644 --- a/test/files/neg/t2799.scala +++ b/test/files/neg/t2799.scala @@ -8,3 +8,15 @@ class C[A: T] { def f = (t: T[A]) => null.asInstanceOf[T[A]] def g() = implicitly[Int => Float] } + +@deprecated("hi mom", "") +case class Bob () + +// No exclusion for companion of deprecated T +object T extends T[String] { + def t = Bob() // warn +} + +class Client { + def test = T.t // if no warn at t, then this code appears deprecation-free +} diff --git a/test/files/neg/t2866.scala b/test/files/neg/t2866.scala index 2f464593d30c..d80822349244 100644 --- a/test/files/neg/t2866.scala +++ b/test/files/neg/t2866.scala @@ -1,7 +1,7 @@ // for 2.7.x compatibility object A { - implicit val one = 1 + implicit val one: Int = 1 } object Test { diff --git a/test/files/neg/t3006.check b/test/files/neg/t3006.check index b8dea080a766..e2358b0f0dd9 100644 --- a/test/files/neg/t3006.check +++ b/test/files/neg/t3006.check @@ -3,4 +3,8 @@ t3006.scala:8: error: type mismatch; required: Int println(A(3) + "H") ^ +t3006.scala:6: warning: Implicit definition should have explicit type (inferred Test.Foo) + implicit def aToFoo(x: A) = new Foo(x); + ^ +1 warning 1 error diff --git a/test/files/neg/t3346c.check b/test/files/neg/t3346c.check index 10c96eeb6cdb..7ffdda688f76 100644 --- a/test/files/neg/t3346c.check +++ b/test/files/neg/t3346c.check @@ -1,5 +1,4 @@ -t3346c.scala:60: error: value bar is not a member of Either[Int,String] -did you mean map? +t3346c.scala:65: error: value bar is not a member of Either[Int,String] eii.bar ^ 1 error diff --git a/test/files/neg/t3346c.scala b/test/files/neg/t3346c.scala index 59584e0a6168..287c91bdf2ca 100644 --- a/test/files/neg/t3346c.scala +++ b/test/files/neg/t3346c.scala @@ -1,3 +1,5 @@ +import annotation._ + object Test extends App { // // An attempt to workaround scala/bug#2712, foiled by scala/bug#3346 @@ -32,15 +34,18 @@ object Test extends App { } + @nowarn implicit def ToTCValue[M[_], A](ma: M[A])(implicit M0: TC[M]) = new TCValue[M, A] { - implicit val M = M0 + implicit val M: TC[M] = M0 val self = ma } implicit def ToTCValueBin1[M[_, _], A, B](ma: M[A, B])(implicit M0: TC[({type λ[α]=M[A, α]})#λ]): TCValue[({type λ[α] = M[A, α]})#λ, B] = new TCValue[({type λ[α]=M[A, α]})#λ, B] { + @nowarn implicit val M = M0 val self = ma } implicit def ToTCValueBin2[M[_, _], A, B](ma: M[A, B])(implicit M0: TC[({type λ[α]=M[α, B]})#λ]): TCValue[({type λ[α]=M[α, B]})#λ, A] = new TCValue[({type λ[α]=M[α, B]})#λ, A] { + @nowarn implicit val M = M0 val self = ma } diff --git a/test/files/neg/t3346i.check b/test/files/neg/t3346i.check index b1b5a140e8f9..dafa0b861e24 100644 --- a/test/files/neg/t3346i.check +++ b/test/files/neg/t3346i.check @@ -4,4 +4,20 @@ t3346i.scala:28: error: value a is not a member of Test.A[T] t3346i.scala:29: error: value a is not a member of Test.A[Nothing] (new A[Nothing]).a ^ +t3346i.scala:16: warning: Implicit definition should have explicit type (inferred Implicit1[T]) + implicit def implicit1[T <: Intermediate[_, _]](implicit b: Implicit2[T]) = new Implicit1[T](b) + ^ +t3346i.scala:18: warning: Implicit definition should have explicit type (inferred Implicit2[T]) + implicit def implicit2alt1[T <: Intermediate[_ <: String, _]](implicit c: Implicit3[T]) = new Implicit2[T](c) + ^ +t3346i.scala:19: warning: Implicit definition should have explicit type (inferred Implicit2[T]) + implicit def implicit2alt2[T <: Intermediate[_ <: Double, _]](implicit c: Implicit3[T]) = new Implicit2[T](c) + ^ +t3346i.scala:21: warning: Implicit definition should have explicit type (inferred Implicit3[T]) + implicit def implicit3alt1[T <: Intermediate[_, _ <: Int]] = new Implicit3[T]() + ^ +t3346i.scala:22: warning: Implicit definition should have explicit type (inferred Implicit3[T]) + implicit def implicit3alt2[T <: Intermediate[_ <: Double, _ <: AnyRef],X] = new Implicit3[T]() + ^ +5 warnings 2 errors diff --git a/test/files/neg/t3420b.check b/test/files/neg/t3420b.check new file mode 100644 index 000000000000..3770c326dede --- /dev/null +++ b/test/files/neg/t3420b.check @@ -0,0 +1,4 @@ +t3420b.scala:8: error: value translateEscapes is not a member of String + def f(s: String) = s.translateEscapes + ^ +1 error diff --git a/test/files/neg/t3420b.scala b/test/files/neg/t3420b.scala new file mode 100644 index 000000000000..33d907594145 --- /dev/null +++ b/test/files/neg/t3420b.scala @@ -0,0 +1,9 @@ + +// scalac: --release 8 -opt:inline:** -Wopt -Werror +// +class C { + val cv = Map[Int, Int](1 -> 2) + lazy val cl = Map[Int, Int](1 -> 2) + def cd = Map[Int, Int](1 -> 2) + def f(s: String) = s.translateEscapes +} diff --git a/test/files/neg/t3909.check b/test/files/neg/t3909.check index b4d1c26ed14b..9ed443d6b494 100644 --- a/test/files/neg/t3909.check +++ b/test/files/neg/t3909.check @@ -1,5 +1,4 @@ -t3909.scala:1: error: in object DO, multiple overloaded alternatives of m1 define default arguments -Error occurred in an application involving default arguments. +t3909.scala:1: error: in object DO, multiple overloaded alternatives of method m1 define default arguments. object DO { ^ 1 error diff --git a/test/files/neg/t3909b.check b/test/files/neg/t3909b.check new file mode 100644 index 000000000000..7fd44088d4b3 --- /dev/null +++ b/test/files/neg/t3909b.check @@ -0,0 +1,4 @@ +t3909b.scala:1: error: in object DO, multiple overloaded alternatives of method m1 define default arguments. +object DO { + ^ +1 error diff --git a/test/files/neg/t3909b.scala b/test/files/neg/t3909b.scala new file mode 100644 index 000000000000..26875f61c072 --- /dev/null +++ b/test/files/neg/t3909b.scala @@ -0,0 +1,10 @@ +object DO { + + def m1(str: String, extraStuff: String = "stuff"): Int = ??? + def m1(i: Int, extraStuff: Int = "42".toInt): Int = ??? + + def main(args: Array[String]): Unit = { + val m1s = m1("foo") + val m1i = m1(42) + } +} diff --git a/test/files/neg/t4271.check b/test/files/neg/t4271.check index 402a7b9b7acb..f4f3c9438a20 100644 --- a/test/files/neg/t4271.check +++ b/test/files/neg/t4271.check @@ -1,11 +1,31 @@ t4271.scala:9: error: value to is not a member of Int +did you mean toInt? 3 to 5 ^ t4271.scala:10: error: value ensuring is not a member of Int 5 ensuring true ^ t4271.scala:11: error: value -> is not a member of Int -did you mean >>>? +did you mean >>? 3 -> 5 ^ +t4271.scala:3: warning: Implicit definition should have explicit type (inferred foo.Donotuseme.type) + implicit def Ensuring[A](x: A) = Donotuseme + ^ +t4271.scala:4: warning: Implicit definition should have explicit type (inferred foo.Donotuseme.type) + implicit def doubleWrapper(x: Int) = Donotuseme + ^ +t4271.scala:5: warning: Implicit definition should have explicit type (inferred foo.Donotuseme.type) + implicit def floatWrapper(x: Int) = Donotuseme + ^ +t4271.scala:6: warning: Implicit definition should have explicit type (inferred foo.Donotuseme.type) + implicit def intWrapper(x: Int) = Donotuseme + ^ +t4271.scala:7: warning: Implicit definition should have explicit type (inferred foo.Donotuseme.type) + implicit def longWrapper(x: Int) = Donotuseme + ^ +t4271.scala:8: warning: Implicit definition should have explicit type (inferred foo.Donotuseme.type) + implicit def ArrowAssoc[A](x: A) = Donotuseme + ^ +6 warnings 3 errors diff --git a/test/files/neg/t4457_1.check b/test/files/neg/t4457_1.check index a16bb1ff8b1b..4d65bc39c1e0 100644 --- a/test/files/neg/t4457_1.check +++ b/test/files/neg/t4457_1.check @@ -4,4 +4,20 @@ and method aFunc in object ImplicitConvAmbiguity2 of type [A](a: ImplicitConvAm match argument types (Float) val x = aFunc(4F) ^ +t4457_1.scala:11: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.NE[Float]) + implicit def conv1(i: Float) = new NE[Float] + ^ +t4457_1.scala:12: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.N[java.util.TooManyListenersException]) + implicit def conv3(op: AA[java.util.TooManyListenersException]) = new N[java.util.TooManyListenersException] + ^ +t4457_1.scala:13: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.N[Float]) + implicit def conv4(op: AA[Float]) = new N[Float] + ^ +t4457_1.scala:14: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.NZ[Float]) + implicit def conv7(i: Float) = new NZ[Float] + ^ +t4457_1.scala:15: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.N[java.util.GregorianCalendar]) + implicit def conv5(e: BB[java.util.GregorianCalendar]) = new N[java.util.GregorianCalendar] + ^ +5 warnings 1 error diff --git a/test/files/neg/t4457_2.check b/test/files/neg/t4457_2.check index b1310f74ed50..d0be6d48ef38 100644 --- a/test/files/neg/t4457_2.check +++ b/test/files/neg/t4457_2.check @@ -10,4 +10,20 @@ and method aFunc in object ImplicitConvAmbiguity2 of type [A](a: ImplicitConvAm match argument types (Float) bFunc(aFunc(4F)) ^ +t4457_2.scala:11: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.NE[Float]) + implicit def conv1(i: Float) = new NE[Float] + ^ +t4457_2.scala:12: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.N[java.util.TooManyListenersException]) + implicit def conv3(op: AA[java.util.TooManyListenersException]) = new N[java.util.TooManyListenersException] + ^ +t4457_2.scala:13: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.N[Float]) + implicit def conv4(op: AA[Float]) = new N[Float] + ^ +t4457_2.scala:14: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.NZ[Float]) + implicit def conv7(i: Float) = new NZ[Float] + ^ +t4457_2.scala:15: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.N[java.util.GregorianCalendar]) + implicit def conv5(e: BB[java.util.GregorianCalendar]) = new N[java.util.GregorianCalendar] + ^ +5 warnings 2 errors diff --git a/test/files/neg/t4568.check b/test/files/neg/t4568.check index 3582e24b188a..604948e41b47 100644 --- a/test/files/neg/t4568.check +++ b/test/files/neg/t4568.check @@ -1,4 +1,8 @@ t4568.scala:8: error: recursive method isSubListOf needs result type case h :: t => y.contains(h) && (t.isSubListOf(y.drop(y.indexOf(h) + 1))) ^ +t4568.scala:2: warning: Implicit definition should have explicit type (inferred SubList.SubListable[A]) + implicit def sublistable[A](x: List[A]) = new SubListable(x) + ^ +1 warning 1 error diff --git a/test/files/neg/t4727.check b/test/files/neg/t4727.check deleted file mode 100644 index 3b1c0feac98f..000000000000 --- a/test/files/neg/t4727.check +++ /dev/null @@ -1,5 +0,0 @@ -t4727.scala:5: error: an expression of type Null is ineligible for implicit conversion -Error occurred in an application involving default arguments. - new C[Int] - ^ -1 error diff --git a/test/files/neg/t4727.scala b/test/files/neg/t4727.scala deleted file mode 100644 index 40c06713caa6..000000000000 --- a/test/files/neg/t4727.scala +++ /dev/null @@ -1,7 +0,0 @@ -class C[T](x : T = null) - -object Test { - def main(args: Array[String]): Unit = { - new C[Int] - } -} diff --git a/test/files/neg/t4762.check b/test/files/neg/t4762.check index aa7bdcec39eb..4088131de277 100644 --- a/test/files/neg/t4762.check +++ b/test/files/neg/t4762.check @@ -1,7 +1,7 @@ -t4762.scala:17: warning: private[this] value x in class B shadows mutable x inherited from class A. Changes to x will not be visible within class B - you may want to give them distinct names. +t4762.scala:17: warning: private[this] value x in class B shadows mutable x inherited from class A. Changes to x will not be visible within class B; you may want to give them distinct names. /* (99,99) */ (this.x, this.y), ^ -t4762.scala:50: warning: private[this] value x in class Derived shadows mutable x inherited from class Base. Changes to x will not be visible within class Derived - you may want to give them distinct names. +t4762.scala:50: warning: private[this] value x in class Derived shadows mutable x inherited from class Base. Changes to x will not be visible within class Derived; you may want to give them distinct names. class Derived( x : Int ) extends Base( x ) { override def toString = x.toString } ^ t4762.scala:13: error: weaker access privileges in overriding diff --git a/test/files/neg/t4889.check b/test/files/neg/t4889.check index 96e9b7528e67..e643dd1ffc05 100644 --- a/test/files/neg/t4889.check +++ b/test/files/neg/t4889.check @@ -1,4 +1,8 @@ t4889.scala:19: error: could not find implicit value for parameter ma1: t4889.MatrixAdder[Int,[S]t4889.SparseMatrix[S]] m1.foo ^ +t4889.scala:14: warning: Implicit definition should have explicit type (inferred t4889.MatrixAdder[S,R]) + implicit def adderImplicit[S, R[s] <: Matrix[s, R]] = new MatrixAdder[S, R] { + ^ +1 warning 1 error diff --git a/test/files/neg/t5197.check b/test/files/neg/t5197.check new file mode 100644 index 000000000000..2d1d16a12cc0 --- /dev/null +++ b/test/files/neg/t5197.check @@ -0,0 +1,7 @@ +t5197.scala:12: error: type mismatch; + found : List[String] + required: Int + Note: implicit object O2 is not applicable here because it comes after the application point and it lacks an explicit result type. An object can be written as a lazy val with an explicit type. + val y: Int = List("b") + ^ +1 error diff --git a/test/files/neg/t5197.scala b/test/files/neg/t5197.scala new file mode 100644 index 000000000000..8e568ea47660 --- /dev/null +++ b/test/files/neg/t5197.scala @@ -0,0 +1,14 @@ + +// scalac: -Werror -feature + +// Periodic reminder that the feature is not required for implicit function values. +//import scala.language.implicitConversions + +object A { + val x: Int = List("a") + implicit lazy val O1: (List[String] => Int) = _.head.toInt +} +object B { + val y: Int = List("b") + implicit object O2 extends (List[String] => Int) { def apply(ss: List[String]): Int = ss.head.toInt } +} diff --git a/test/files/neg/t5265a.check b/test/files/neg/t5265a.check new file mode 100644 index 000000000000..f788868ec4b0 --- /dev/null +++ b/test/files/neg/t5265a.check @@ -0,0 +1,18 @@ +t5265a.scala:7: warning: Implicit definition should have explicit type (inferred T[String]) + implicit val tsMissing = new T[String] {} // warn val in trait + ^ +t5265a.scala:20: warning: Implicit definition should have explicit type (inferred T[String]) + implicit val tsChild = new T[String] {} // warn because inferred from RHS + ^ +t5265a.scala:22: warning: Implicit definition should have explicit type (inferred Int) + implicit private[this] val pChild = 42 // also warn + ^ +t5265a.scala:27: warning: Implicit definition should have explicit type (inferred Int) + implicit private[this] val y = 42 // also warn + ^ +t5265a.scala:25: warning: Implicit definition should have explicit type (inferred T[String]) + implicit val tsD = new T[String] {} // warn val in class + ^ +error: No warnings can be incurred under -Werror. +5 warnings +1 error diff --git a/test/files/neg/t5265a.scala b/test/files/neg/t5265a.scala new file mode 100644 index 000000000000..80b127e1adeb --- /dev/null +++ b/test/files/neg/t5265a.scala @@ -0,0 +1,32 @@ +// scalac: -Werror +trait T[A] + +class C[A: T] + +trait Missing { + implicit val tsMissing = new T[String] {} // warn val in trait + def f = new C[String] +} +trait Local { + def f = { + implicit val tsLocal = new T[String] {} // nowarn because local + new C[String] + } +} +trait Parent { + def t: T[String] +} +trait Child extends Parent { + implicit val tsChild = new T[String] {} // warn because inferred from RHS + def f = new C[String] + implicit private[this] val pChild = 42 // also warn +} +class D { + implicit val tsD = new T[String] {} // warn val in class + def f = new C[String] + implicit private[this] val y = 42 // also warn +} +class X extends Missing +trait Z { + val z = 42 +} diff --git a/test/files/neg/t5265b.check b/test/files/neg/t5265b.check new file mode 100644 index 000000000000..97f96fd82be1 --- /dev/null +++ b/test/files/neg/t5265b.check @@ -0,0 +1,4 @@ +t5265b.scala:7: error: Implicit definition must have explicit type (inferred T[String]) + implicit val tsMissing = new T[String] {} // warn val in trait + ^ +1 error diff --git a/test/files/neg/t5265b.scala b/test/files/neg/t5265b.scala new file mode 100644 index 000000000000..19512c3c9180 --- /dev/null +++ b/test/files/neg/t5265b.scala @@ -0,0 +1,22 @@ +// scalac: -Werror -Xlint -Xsource:3 +trait T[A] + +class C[A: T] + +trait Missing { + implicit val tsMissing = new T[String] {} // warn val in trait + def f = new C[String] +} +trait Local { + def f = { + implicit val tsLocal = new T[String] {} // nowarn because local + new C[String] + } +} +trait Parent { + def tsChild: T[String] +} +trait Child extends Parent { + implicit val tsChild = new T[String] {} // nowarn because inferred from overridden + def f = new C[String] +} diff --git a/test/files/neg/t5429.check b/test/files/neg/t5429.check index 98d13c8a9772..bdd094a8fcd0 100644 --- a/test/files/neg/t5429.check +++ b/test/files/neg/t5429.check @@ -1,9 +1,3 @@ -t5429.scala:68: warning: method without a parameter list overrides a method with a single empty one - def emptyArg = 10 // fail - ^ -t5429.scala:75: warning: method without a parameter list overrides a method with a single empty one - override def emptyArg = 10 // override - ^ t5429.scala:20: error: `override` modifier required to override concrete member: val value: Int (defined in class A) object value // fail @@ -121,6 +115,9 @@ t5429.scala:73: error: stable, immutable value required to override: lazy val lazyvalue: Any (defined in class A0) override def lazyvalue = 2 // fail ^ +t5429.scala:75: warning: method without a parameter list overrides a method with a single empty one + override def emptyArg = 10 // override + ^ t5429.scala:76: error: method oneArg overrides nothing. Note: the super classes of class E0 contain the following, non final members named oneArg: def oneArg(x: String): Any @@ -151,5 +148,5 @@ Note: the super classes of class F0 contain the following, non final members nam def oneArg(x: String): Any override lazy val oneArg = 15 // fail ^ -2 warnings +1 warning 34 errors diff --git a/test/files/neg/t5728.check b/test/files/neg/t5728.check index 7a80af2af06d..72a0c0273005 100644 --- a/test/files/neg/t5728.check +++ b/test/files/neg/t5728.check @@ -1,4 +1,8 @@ t5728.scala:3: error: implicit classes must accept exactly one primary constructor parameter implicit class Foo ^ +t5728.scala:5: warning: Implicit definition should have explicit type (inferred Test.Foo) + implicit def Foo = new Foo + ^ +1 warning 1 error diff --git a/test/files/neg/t5735.check b/test/files/neg/t5735.check index 8f49838cd6d4..f187d54caadf 100644 --- a/test/files/neg/t5735.check +++ b/test/files/neg/t5735.check @@ -1,5 +1,5 @@ t5735.scala:6: error: type mismatch; - found : (x: Int): Int String + found : String required: Int val z: Int = a ^ diff --git a/test/files/neg/t5801.check b/test/files/neg/t5801.check index 7f6cb4cfe6c3..f1e672c02e46 100644 --- a/test/files/neg/t5801.check +++ b/test/files/neg/t5801.check @@ -1,5 +1,4 @@ t5801.scala:1: error: object sth is not a member of package scala -did you mean math or sys? import scala.sth ^ t5801.scala:4: error: not found: value sth diff --git a/test/files/neg/t6159.check b/test/files/neg/t6159.check new file mode 100644 index 000000000000..19da9e4aa0cc --- /dev/null +++ b/test/files/neg/t6159.check @@ -0,0 +1,7 @@ +t6159.scala:10: warning: match may not be exhaustive. +It would fail on the following input: (_ : A.this.X2) + def f(x: X) = x match { + ^ +error: No warnings can be incurred under -Werror. +1 warning +1 error diff --git a/test/files/neg/t6159.scala b/test/files/neg/t6159.scala new file mode 100644 index 000000000000..2e7477a299c7 --- /dev/null +++ b/test/files/neg/t6159.scala @@ -0,0 +1,13 @@ +// scalac: -Werror +// like test/files/pos/t6159.scala +// but with T2 not private +trait A { + sealed abstract class X + private class X1 extends X with X2 { } + trait X2 extends X + sealed trait X3 extends X + + def f(x: X) = x match { + case _: X1 => 0 + } +} diff --git a/test/files/neg/t6436.check b/test/files/neg/t6436.check index 27f4466572e5..2d74aa8c2a78 100644 --- a/test/files/neg/t6436.check +++ b/test/files/neg/t6436.check @@ -7,4 +7,11 @@ Note that implicit conversions are not applicable because they are ambiguous: are possible conversion functions from StringContext to ?{def q: ?} println(q"a") ^ +t6436.scala:2: warning: Implicit definition should have explicit type (inferred AnyRef{def q: Nothing}) + implicit def foo1(ctx: StringContext) = new { def q = ??? } + ^ +t6436.scala:3: warning: Implicit definition should have explicit type (inferred AnyRef{def q: Nothing}) + implicit def foo2(ctx: StringContext) = new { def q = ??? } + ^ +2 warnings 1 error diff --git a/test/files/neg/t6436b.check b/test/files/neg/t6436b.check index be9b17a4e387..8e33d7bb134a 100644 --- a/test/files/neg/t6436b.check +++ b/test/files/neg/t6436b.check @@ -7,4 +7,11 @@ Note that implicit conversions are not applicable because they are ambiguous: are possible conversion functions from StringContext to ?{def q: ?} println(StringContext("a").q()) ^ +t6436b.scala:2: warning: Implicit definition should have explicit type (inferred AnyRef{def q: Nothing}) + implicit def foo1(ctx: StringContext) = new { def q = ??? } + ^ +t6436b.scala:3: warning: Implicit definition should have explicit type (inferred AnyRef{def q: Nothing}) + implicit def foo2(ctx: StringContext) = new { def q = ??? } + ^ +2 warnings 1 error diff --git a/test/files/neg/t6534.check b/test/files/neg/t6534.check index ae99eac73bb2..c2d57178c6ed 100644 --- a/test/files/neg/t6534.check +++ b/test/files/neg/t6534.check @@ -1,10 +1,10 @@ -t6534.scala:8: error: redefinition of equals method. See SIP-15, criterion 4. is not allowed in value class +t6534.scala:8: error: redefinition of equals method. See SIP-15, criterion 5. is not allowed in value class class Bippy3(val x: Int) extends AnyVal { override def equals(x: Any) = false } // error ^ -t6534.scala:9: error: redefinition of hashCode method. See SIP-15, criterion 4. is not allowed in value class +t6534.scala:9: error: redefinition of hashCode method. See SIP-15, criterion 5. is not allowed in value class class Bippy4(val x: Int) extends AnyVal { override def hashCode = -1 } // error ^ -t6534.scala:11: error: redefinition of equals method. See SIP-15, criterion 4. is not allowed in value class +t6534.scala:11: error: redefinition of equals method. See SIP-15, criterion 5. is not allowed in value class case class Bippy6(val x: Int) extends AnyVal { override def productPrefix = "Dingo" ; override def equals(x: Any) = false } // error ^ 3 errors diff --git a/test/files/neg/t6567.check b/test/files/neg/t6567.check index 5ae946a4c1ac..552f4cbd48df 100644 --- a/test/files/neg/t6567.check +++ b/test/files/neg/t6567.check @@ -1,3 +1,6 @@ +t6567.scala:8: warning: Implicit definition should have explicit type (inferred B) + implicit def a2b(a: A) = new B + ^ t6567.scala:10: warning: Suspicious application of an implicit view (Test.this.a2b) in the argument to Option.apply. Option[B](a) ^ @@ -6,5 +9,5 @@ t6567.scala:12: warning: Suspicious application of an implicit view (Test.this.a ^ warning: 1 feature warning; re-run with -feature for details error: No warnings can be incurred under -Werror. -3 warnings +4 warnings 1 error diff --git a/test/files/neg/t6667.check b/test/files/neg/t6667.check index 1fe4a15e593e..dfcad24c4645 100644 --- a/test/files/neg/t6667.check +++ b/test/files/neg/t6667.check @@ -10,4 +10,8 @@ t6667.scala:9: error: ambiguous implicit values: match expected type C implicitly[C] // ambiguity reported, rather than falling back to C.companion ^ +t6667.scala:3: warning: Implicit definition should have explicit type (inferred C) + implicit def companion = new C + ^ +1 warning 2 errors diff --git a/test/files/neg/t6667.scala b/test/files/neg/t6667.scala index fb857ebd3322..840921f3449d 100644 --- a/test/files/neg/t6667.scala +++ b/test/files/neg/t6667.scala @@ -4,7 +4,7 @@ object C { } object Test { - implicit val inScope1, inScope2 = new C + implicit val inScope1, inScope2: C = new C implicitly[C]: Unit // C.companion was used; whereas the ambiguity should abort the implicit search. implicitly[C] // ambiguity reported, rather than falling back to C.companion } diff --git a/test/files/neg/t692.check b/test/files/neg/t692.check index 670495f6467d..42e2a1b8f2c6 100644 --- a/test/files/neg/t692.check +++ b/test/files/neg/t692.check @@ -16,4 +16,8 @@ t692.scala:14: error: class Foo takes type parameters t692.scala:19: error: class Foo takes type parameters class Bar[A <: Foo](implicit tpeA : Type[A]) extends Foo; ^ +t692.scala:11: warning: Implicit definition should have explicit type (inferred test3.this.FooType) + implicit def typeOfFoo = FooType(); + ^ +1 warning 6 errors diff --git a/test/files/neg/t712.check b/test/files/neg/t712.check index fc8f7824f2c7..606e41f04d92 100644 --- a/test/files/neg/t712.check +++ b/test/files/neg/t712.check @@ -1,4 +1,11 @@ t712.scala:10: error: overloaded method coerce needs result type implicit def coerce(p : ParentImpl) = p.self; ^ +t712.scala:3: warning: Implicit definition should have explicit type (inferred A.this.Node) + implicit def coerce(n : NodeImpl) = n.self; + ^ +t712.scala:10: warning: Implicit definition should have explicit type + implicit def coerce(p : ParentImpl) = p.self; + ^ +2 warnings 1 error diff --git a/test/files/neg/t7131.check b/test/files/neg/t7131.check index c66f1b4f927c..2c8a35025cf3 100644 --- a/test/files/neg/t7131.check +++ b/test/files/neg/t7131.check @@ -1,7 +1,14 @@ t7131.scala:21: error: type mismatch; found : Iterable[U] required: That - Note: implicit method convertToSimpleMappable is not applicable here because it comes after the application point and it lacks an explicit result type + Note: implicit method convertToSimpleMappable is not applicable here because it comes after the application point and it lacks an explicit result type. x.value.map(f) ^ +t7131.scala:28: warning: Implicit definition should have explicit type (inferred ObservableValue.TraversableMappable[T,Container]) + implicit def convertToTraversableMappable[T, Container[X] <: Traversable[X]](x: ObservableValue[Container[T]]) = + ^ +t7131.scala:43: warning: Implicit definition should have explicit type (inferred ObservableValue.NestedMappable[T,Container]) + implicit def convertToSimpleMappable[T, Container[X] <: ObservableValue.HasMap[X, Container]](x: ObservableValue[Container[T]]) = + ^ +2 warnings 1 error diff --git a/test/files/neg/t7187-3.check b/test/files/neg/t7187-3.check index 6ea263f0f35b..ba739c55898b 100644 --- a/test/files/neg/t7187-3.check +++ b/test/files/neg/t7187-3.check @@ -6,12 +6,12 @@ t7187-3.scala:13: error: type mismatch; t7187-3.scala:15: error: type mismatch; found : Int required: AcciSamZero - val t2AcciSam: AcciSamZero = m2 // error, nilary methods don't eta-expand to SAM types under -Xsource:3 + val t2AcciSam: AcciSamZero = m2 // error, nilary methods don't eta-expand to SAM types ^ t7187-3.scala:16: error: type mismatch; found : Int required: SamZero - val t2Sam: SamZero = m2 // error, nilary methods don't eta-expand to SAM types under -Xsource:3 + val t2Sam: SamZero = m2 // error, nilary methods don't eta-expand to SAM types ^ t7187-3.scala:27: error: Methods without a parameter list and by-name params can not be converted to functions as `m _`, write a function literal `() => m` instead val t7 = m1 _ // error: eta-expanding a nullary method diff --git a/test/files/neg/t7187-3.scala b/test/files/neg/t7187-3.scala index 9216d09c579f..a3bc502ba6b1 100644 --- a/test/files/neg/t7187-3.scala +++ b/test/files/neg/t7187-3.scala @@ -12,8 +12,8 @@ class EtaExpand214 { val t1: () => Any = m1 // error val t2: () => Any = m2 // eta-expanded with lint warning - val t2AcciSam: AcciSamZero = m2 // error, nilary methods don't eta-expand to SAM types under -Xsource:3 - val t2Sam: SamZero = m2 // error, nilary methods don't eta-expand to SAM types under -Xsource:3 + val t2AcciSam: AcciSamZero = m2 // error, nilary methods don't eta-expand to SAM types + val t2Sam: SamZero = m2 // error, nilary methods don't eta-expand to SAM types val t3: Int => Any = m3 // ok val t4 = m1 // apply diff --git a/test/files/neg/t7187-deprecation.check b/test/files/neg/t7187-deprecation.check index edbc44fe2b11..ae8772e4352e 100644 --- a/test/files/neg/t7187-deprecation.check +++ b/test/files/neg/t7187-deprecation.check @@ -6,12 +6,12 @@ t7187-deprecation.scala:17: error: type mismatch; t7187-deprecation.scala:19: error: type mismatch; found : Int required: AcciSamZero - val t2AcciSam: AcciSamZero = m2 // error, nilary methods don't eta-expand to SAM types under -Xsource:3 + val t2AcciSam: AcciSamZero = m2 // error, nilary methods don't eta-expand to SAM types ^ t7187-deprecation.scala:20: error: type mismatch; found : Int required: SamZero - val t2Sam: SamZero = m2 // error, nilary methods don't eta-expand to SAM types under -Xsource:3 + val t2Sam: SamZero = m2 // error, nilary methods don't eta-expand to SAM types ^ t7187-deprecation.scala:31: error: Methods without a parameter list and by-name params can not be converted to functions as `m _`, write a function literal `() => m` instead val t7 = m1 _ // error: eta-expanding a nullary method diff --git a/test/files/neg/t7187-deprecation.scala b/test/files/neg/t7187-deprecation.scala index 156642ff287b..7808df726e59 100644 --- a/test/files/neg/t7187-deprecation.scala +++ b/test/files/neg/t7187-deprecation.scala @@ -16,8 +16,8 @@ class EtaExpand214 { val t1: () => Any = m1 // error val t2: () => Any = m2 // eta-expanded, only warns w/ -Xlint:eta-zero - val t2AcciSam: AcciSamZero = m2 // error, nilary methods don't eta-expand to SAM types under -Xsource:3 - val t2Sam: SamZero = m2 // error, nilary methods don't eta-expand to SAM types under -Xsource:3 + val t2AcciSam: AcciSamZero = m2 // error, nilary methods don't eta-expand to SAM types + val t2Sam: SamZero = m2 // error, nilary methods don't eta-expand to SAM types val t3: Int => Any = m3 // ok val t4 = m1 // apply diff --git a/test/files/neg/t7187.check b/test/files/neg/t7187.check index e81eb7aac0cd..696b7bbbaa4f 100644 --- a/test/files/neg/t7187.check +++ b/test/files/neg/t7187.check @@ -49,9 +49,10 @@ t7187.scala:33: warning: An unapplied 0-arity method was eta-expanded (due to th Write zap()() to invoke method zap, or change the expected type. val t4b: () => Any = zap() // ditto ^ -t7187.scala:40: warning: Eta-expansion performed to meet expected type AcciSamOne, which is SAM-equivalent to Int => Int, -even though trait AcciSamOne is not annotated with `@FunctionalInterface`; -to suppress warning, add the annotation or write out the equivalent function literal. +t7187.scala:40: warning: Eta-expansion to expected type AcciSamOne, which is not a function type but is SAM-convertible to Int => Int. +trait AcciSamOne should be annotated with `@FunctionalInterface` if eta-expansion is desired. +Avoid eta-expansion by writing the function literal `((x: Int) => zup(x))` or `zup(_)`. +This warning can be filtered with `-Wconf:cat=lint-eta-sam`. val t5AcciSam: AcciSamOne = zup // ok, but warning ^ 8 warnings diff --git a/test/files/neg/t7187.scala b/test/files/neg/t7187.scala index 69fd0d3ee487..f58d7e49cb98 100644 --- a/test/files/neg/t7187.scala +++ b/test/files/neg/t7187.scala @@ -1,4 +1,4 @@ -// scalac: -deprecation -Xlint:eta-zero -Xlint:eta-sam +// scalac: -Xlint:deprecation,eta-zero,eta-sam // trait AcciSamOne { def apply(x: Int): Int } diff --git a/test/files/neg/t7212.check b/test/files/neg/t7212.check new file mode 100644 index 000000000000..8f1fe20edcb4 --- /dev/null +++ b/test/files/neg/t7212.check @@ -0,0 +1,16 @@ +t7212.scala:8: error: type mismatch; + found : Object + required: String + val s: String = k.f + ^ +t7212.scala:14: error: type mismatch; + found : Object + required: String + val s: String = f.f + ^ +t7212.scala:21: error: type mismatch; + found : Object + required: String + val s: String = w.f + ^ +3 errors diff --git a/test/files/neg/t7212.scala b/test/files/neg/t7212.scala new file mode 100644 index 000000000000..5dd16a6c8091 --- /dev/null +++ b/test/files/neg/t7212.scala @@ -0,0 +1,22 @@ + +// scalac: -Xsource:3 + +trait T { def f: Object } +class K extends T { def f = "" } +object K { + val k = new K + val s: String = k.f +} + +class F extends T { val f = "" } +object F { + val f = new F + val s: String = f.f +} + +trait V extends T { var f = "" } +class W extends V +object W { + val w = new W + val s: String = w.f +} diff --git a/test/files/neg/t729.check b/test/files/neg/t729.check index 050772cf84b9..7680b2fdfa4b 100644 --- a/test/files/neg/t729.check +++ b/test/files/neg/t729.check @@ -3,4 +3,11 @@ t729.scala:20: error: type mismatch; required: ScalaParserAutoEdit.this.NodeImpl(in trait ScalaParserAutoEdit) val yyy : NodeImpl = link.from; ^ +t729.scala:3: warning: Implicit definition should have explicit type (inferred Parser.this.Node) + implicit def coerce(n : NodeImpl) = n.self; + ^ +t729.scala:14: warning: Implicit definition should have explicit type (inferred ScalaParserAutoEdit.this.Node) + implicit def coerce(node : NodeImpl) = node.self; + ^ +2 warnings 1 error diff --git a/test/files/neg/t7475f.check b/test/files/neg/t7475f.check index 4f69d9a63fd1..ad16bb89e564 100644 --- a/test/files/neg/t7475f.check +++ b/test/files/neg/t7475f.check @@ -5,6 +5,7 @@ t7475f.scala:13: error: not found: value c2 c2 // a member, but inaccessible. ^ t7475f.scala:26: error: value d2 is not a member of D[Any] +did you mean d1? other.d2 // not a member ^ 3 errors diff --git a/test/files/neg/t7783.check b/test/files/neg/t7783.check index 26b9996fc8f9..b25bd6bd02a5 100644 --- a/test/files/neg/t7783.check +++ b/test/files/neg/t7783.check @@ -1,16 +1,16 @@ -t7783.scala:3: warning: type D in object O is deprecated: +t7783.scala:3: warning: type D in object O is deprecated object O { class C; @deprecated("", "") type D = C; def foo: Seq[D] = Nil } ^ -t7783.scala:13: warning: type D in object O is deprecated: +t7783.scala:13: warning: type D in object O is deprecated type T = O.D ^ -t7783.scala:14: warning: type D in object O is deprecated: +t7783.scala:14: warning: type D in object O is deprecated locally(null: O.D) ^ -t7783.scala:15: warning: type D in object O is deprecated: +t7783.scala:15: warning: type D in object O is deprecated val x: O.D = null ^ -t7783.scala:16: warning: type D in object O is deprecated: +t7783.scala:16: warning: type D in object O is deprecated locally(null.asInstanceOf[O.D]) ^ error: No warnings can be incurred under -Werror. diff --git a/test/files/neg/t8322.check b/test/files/neg/t8322.check index 6c3f9a5e53ce..bc56230cd385 100644 --- a/test/files/neg/t8322.check +++ b/test/files/neg/t8322.check @@ -13,4 +13,11 @@ t8322.scala:19: error: type mismatch; required: scala.util.Either[?,?] Right(0).right.flatMap(_ => new String()) ^ +t8322.scala:15: warning: Implicit definition should have explicit type (inferred Writes[Seq[E]]) + implicit def rw[E] = Writes[Seq[E]] { _ => "" } + ^ +t8322.scala:17: warning: Implicit definition should have explicit type + implicit def wr[E] = jw(implicitly, implicitly) + ^ +2 warnings 3 errors diff --git a/test/files/neg/t8525.check b/test/files/neg/t8525.check index ca69a56c4ec3..667cccc69646 100644 --- a/test/files/neg/t8525.check +++ b/test/files/neg/t8525.check @@ -4,7 +4,7 @@ t8525.scala:9: warning: adapted the argument list to the expected 2-tuple: add a after adaptation: X.f((3, 4): (Int, Int)) def g = f(3, 4) // adapted ^ -t8525.scala:11: warning: private[this] value name in class X shadows mutable name inherited from class Named. Changes to name will not be visible within class X - you may want to give them distinct names. +t8525.scala:11: warning: private[this] value name in class X shadows mutable name inherited from class Named. Changes to name will not be visible within class X; you may want to give them distinct names. override def toString = name // shadowing mutable var name ^ t8525.scala:10: warning: side-effecting nullary methods are discouraged: suggest defining as `def u()` instead diff --git a/test/files/neg/t8610.check b/test/files/neg/t8610.check index 3dacf74ff7f9..e640853b67ea 100644 --- a/test/files/neg/t8610.check +++ b/test/files/neg/t8610.check @@ -7,7 +7,7 @@ t8610.scala:9: warning: adapted the argument list to the expected 2-tuple: add a after adaptation: X.f((3, 4): (Int, Int)) def g = f(3, 4) // adapted ^ -t8610.scala:11: warning: private[this] value name in class X shadows mutable name inherited from class Named. Changes to name will not be visible within class X - you may want to give them distinct names. +t8610.scala:11: warning: private[this] value name in class X shadows mutable name inherited from class Named. Changes to name will not be visible within class X; you may want to give them distinct names. override def toString = name // shadowing mutable var name ^ t8610.scala:10: warning: side-effecting nullary methods are discouraged: suggest defining as `def u()` instead diff --git a/test/files/neg/t8667.check b/test/files/neg/t8667.check index 8a0c37775990..142d7508543e 100644 --- a/test/files/neg/t8667.check +++ b/test/files/neg/t8667.check @@ -127,4 +127,20 @@ t8667.scala:76: error: can't supply unit value with infix notation because nulla t8667.scala:77: error: no arguments allowed for nullary method f0: (): Int x.f0(()) ^ -43 errors +t8667.scala:89: error: unknown parameter name: k + f2(k = 42) + ^ +t8667.scala:89: error: not enough arguments for method f2: (i: Int, j: Int): Unit. +Unspecified value parameters i, j. + f2(k = 42) + ^ +t8667.scala:90: error: unknown parameter name: k + f2(17, k = 42) + ^ +t8667.scala:91: error: unknown parameter name: k + f2(17, 27, k = 42) + ^ +t8667.scala:91: error: too many arguments (found 3, expected 2) for method f2: (i: Int, j: Int): Unit + f2(17, 27, k = 42) + ^ +48 errors diff --git a/test/files/neg/t8667.scala b/test/files/neg/t8667.scala index 09cf57041ebc..1640884e04b2 100644 --- a/test/files/neg/t8667.scala +++ b/test/files/neg/t8667.scala @@ -82,3 +82,12 @@ object app { x f0 } } + +trait Nuance { + def f2(i: Int, j: Int) = () + def `always report bad named arg`: Unit = { + f2(k = 42) + f2(17, k = 42) + f2(17, 27, k = 42) + } +} diff --git a/test/files/neg/t8700b-new.check b/test/files/neg/t8700b-new.check new file mode 100644 index 000000000000..172c0d0fab78 --- /dev/null +++ b/test/files/neg/t8700b-new.check @@ -0,0 +1,11 @@ +Bar_2.scala:4: warning: match may not be exhaustive. +It would fail on the following input: B + def bar1(foo: Foo_1) = foo match { + ^ +Bar_2.scala:8: warning: match may not be exhaustive. +It would fail on the following inputs: (_ : Baz_1$1), (_ : Baz_1$2), B + def bar2(foo: Baz_1) = foo match { + ^ +error: No warnings can be incurred under -Werror. +2 warnings +1 error diff --git a/test/files/neg/t8700b-new/Bar_2.scala b/test/files/neg/t8700b-new/Bar_2.scala new file mode 100644 index 000000000000..36e1e6d2ccc5 --- /dev/null +++ b/test/files/neg/t8700b-new/Bar_2.scala @@ -0,0 +1,11 @@ +// javaVersion: 17+ +// scalac: -Werror +object Bar { + def bar1(foo: Foo_1) = foo match { + case Foo_1.A => 1 + } + + def bar2(foo: Baz_1) = foo match { + case Baz_1.A => 1 + } +} diff --git a/test/files/neg/t8700b-new/Baz_1.java b/test/files/neg/t8700b-new/Baz_1.java new file mode 100644 index 000000000000..9398a24b5714 --- /dev/null +++ b/test/files/neg/t8700b-new/Baz_1.java @@ -0,0 +1,12 @@ +// javaVersion: 17+ +public enum Baz_1 { + A { + public void baz1() {} + }, + B { + public void baz1() {} + }; + + public abstract void baz1(); + public void baz2() {} +} diff --git a/test/files/neg/t8700b-new/Foo_1.java b/test/files/neg/t8700b-new/Foo_1.java new file mode 100644 index 000000000000..8694131d3eab --- /dev/null +++ b/test/files/neg/t8700b-new/Foo_1.java @@ -0,0 +1,5 @@ +// javaVersion: 17+ +public enum Foo_1 { + A, + B +} diff --git a/test/files/neg/t8700b.check b/test/files/neg/t8700b-old.check similarity index 72% rename from test/files/neg/t8700b.check rename to test/files/neg/t8700b-old.check index 65af1192f801..e061c0ce443b 100644 --- a/test/files/neg/t8700b.check +++ b/test/files/neg/t8700b-old.check @@ -1,8 +1,8 @@ -Bar_2.scala:3: warning: match may not be exhaustive. +Bar_2.scala:4: warning: match may not be exhaustive. It would fail on the following input: B def bar1(foo: Foo_1) = foo match { ^ -Bar_2.scala:7: warning: match may not be exhaustive. +Bar_2.scala:8: warning: match may not be exhaustive. It would fail on the following input: B def bar2(foo: Baz_1) = foo match { ^ diff --git a/test/files/neg/t8700b/Bar_2.scala b/test/files/neg/t8700b-old/Bar_2.scala similarity index 90% rename from test/files/neg/t8700b/Bar_2.scala rename to test/files/neg/t8700b-old/Bar_2.scala index 6e0114d76c9e..43b5463468f0 100644 --- a/test/files/neg/t8700b/Bar_2.scala +++ b/test/files/neg/t8700b-old/Bar_2.scala @@ -1,3 +1,4 @@ +// javaVersion: 8 // scalac: -Werror object Bar { def bar1(foo: Foo_1) = foo match { diff --git a/test/files/neg/t8700b/Baz_1.java b/test/files/neg/t8700b-old/Baz_1.java similarity index 90% rename from test/files/neg/t8700b/Baz_1.java rename to test/files/neg/t8700b-old/Baz_1.java index 6a057c2c9c07..6373780810b6 100644 --- a/test/files/neg/t8700b/Baz_1.java +++ b/test/files/neg/t8700b-old/Baz_1.java @@ -1,3 +1,4 @@ +// javaVersion: 8 public enum Baz_1 { A { public void baz1() {} diff --git a/test/files/neg/t8700b/Foo_1.java b/test/files/neg/t8700b-old/Foo_1.java similarity index 66% rename from test/files/neg/t8700b/Foo_1.java rename to test/files/neg/t8700b-old/Foo_1.java index 22656bdeddff..aa92556676f0 100644 --- a/test/files/neg/t8700b/Foo_1.java +++ b/test/files/neg/t8700b-old/Foo_1.java @@ -1,3 +1,4 @@ +// javaVersion: 8 public enum Foo_1 { A, B diff --git a/test/files/neg/t9617.check b/test/files/neg/t9617.check index 20e7baa87a72..84a286a25bb7 100644 --- a/test/files/neg/t9617.check +++ b/test/files/neg/t9617.check @@ -1,3 +1,6 @@ +Test.scala:4: warning: class DeprecatedClass in package p1 is deprecated +object Test extends p1.DeprecatedClass { + ^ Test.scala:5: warning: class DeprecatedClass in package p1 is deprecated def useC = p1.DeprecatedClass.foo ^ @@ -5,5 +8,5 @@ Test.scala:6: warning: method foo in class DeprecatedMethod is deprecated def useM = p1.DeprecatedMethod.foo ^ error: No warnings can be incurred under -Werror. -2 warnings +3 warnings 1 error diff --git a/test/files/neg/t9617/Test.scala b/test/files/neg/t9617/Test.scala index b84cb229d090..efdd94f8a15d 100644 --- a/test/files/neg/t9617/Test.scala +++ b/test/files/neg/t9617/Test.scala @@ -1,7 +1,7 @@ -// scalac: -Xfatal-warnings -deprecation +// scalac: -Werror -Xlint:deprecation // Joint-compilation copy of test/files/neg/t10752/Test_2.scala -object Test { +object Test extends p1.DeprecatedClass { def useC = p1.DeprecatedClass.foo def useM = p1.DeprecatedMethod.foo } diff --git a/test/files/neg/value-discard.scala b/test/files/neg/value-discard.scala index eed8a29a4d12..a6bde54878b3 100644 --- a/test/files/neg/value-discard.scala +++ b/test/files/neg/value-discard.scala @@ -18,4 +18,10 @@ final class UnusedTest { "": Unit // nowarn s.subtractOne("") // nowarn } + + def f(implicit x: Int): Boolean = x % 2 == 1 + + implicit def i: Int = 42 + + def u: Unit = f: Unit // nowarn } diff --git a/test/files/neg/warn-unused-explicits.check b/test/files/neg/warn-unused-explicits.check index 58c6912d34c7..9238b072a5ed 100644 --- a/test/files/neg/warn-unused-explicits.check +++ b/test/files/neg/warn-unused-explicits.check @@ -1,4 +1,4 @@ -warn-unused-explicits.scala:9: warning: parameter value x in method warn is never used +warn-unused-explicits.scala:9: warning: parameter x in method warn is never used def warn(x: Int) = answer ^ error: No warnings can be incurred under -Werror. diff --git a/test/files/neg/warn-unused-implicits.check b/test/files/neg/warn-unused-implicits.check index ee8fdfd924c6..b5c08f8d311e 100644 --- a/test/files/neg/warn-unused-implicits.check +++ b/test/files/neg/warn-unused-implicits.check @@ -1,7 +1,7 @@ -warn-unused-implicits.scala:13: warning: parameter value s in method f is never used +warn-unused-implicits.scala:13: warning: parameter s in method f is never used )(implicit s: String): Int = { // warn ^ -warn-unused-implicits.scala:33: warning: parameter value s in method i is never used +warn-unused-implicits.scala:33: warning: parameter s in method i is never used def i(implicit s: String, t: Int) = t // yes, warn ^ error: No warnings can be incurred under -Werror. diff --git a/test/files/neg/warn-unused-params.check b/test/files/neg/warn-unused-params.check index de20119fff4f..f18c7815af1b 100644 --- a/test/files/neg/warn-unused-params.check +++ b/test/files/neg/warn-unused-params.check @@ -1,34 +1,34 @@ -warn-unused-params.scala:13: warning: parameter value b in method f is never used +warn-unused-params.scala:13: warning: parameter b in method f is never used b: String, // warn ^ -warn-unused-params.scala:36: warning: parameter value s in method i is never used +warn-unused-params.scala:36: warning: parameter s in method i is never used def i(implicit s: String) = answer // yes, warn ^ -warn-unused-params.scala:53: warning: parameter value u in class Unusing is never used +warn-unused-params.scala:53: warning: parameter u in class Unusing is never used class Unusing(u: Int) { // warn ^ -warn-unused-params.scala:63: warning: parameter value s in class CaseyAtTheBat is never used +warn-unused-params.scala:63: warning: parameter s in class CaseyAtTheBat is never used case class CaseyAtTheBat(k: Int)(s: String) // warn ^ -warn-unused-params.scala:66: warning: parameter value readResolve in method f is never used +warn-unused-params.scala:66: warning: parameter readResolve in method f is never used def f(readResolve: Int) = answer // warn ^ -warn-unused-params.scala:81: warning: parameter value dummy in method g is never used +warn-unused-params.scala:81: warning: parameter dummy in method g is never used def g(dummy: DummyImplicit) = answer ^ -warn-unused-params.scala:86: warning: parameter value ev in method f2 is never used +warn-unused-params.scala:86: warning: parameter ev in method f2 is never used def f2[A, B](ev: A =:= B) = answer ^ -warn-unused-params.scala:87: warning: parameter value ev in method g2 is never used +warn-unused-params.scala:87: warning: parameter ev in method g2 is never used def g2[A, B](ev: A <:< B) = answer ^ -warn-unused-params.scala:91: warning: parameter value i in anonymous function is never used +warn-unused-params.scala:91: warning: parameter i in anonymous function is never used def f = (i: Int) => answer // warn ^ -warn-unused-params.scala:97: warning: parameter value i in anonymous function is never used +warn-unused-params.scala:97: warning: parameter i in anonymous function is never used def g = for (i <- List(1)) yield answer // warn map.(i => 42) ^ -warn-unused-params.scala:101: warning: parameter value ctx in method f is never used +warn-unused-params.scala:101: warning: parameter ctx in method f is never used def f[A](implicit ctx: Context[A]) = answer ^ warn-unused-params.scala:102: warning: evidence parameter evidence$1 of type Context[A] in method g is never used diff --git a/test/files/neg/wconfSource1.check b/test/files/neg/wconfSource1.check index 2e311ca41170..10e2b5233b6a 100644 --- a/test/files/neg/wconfSource1.check +++ b/test/files/neg/wconfSource1.check @@ -1,4 +1,4 @@ -wconfSource1.scala:9: error: method dep in class C is deprecated: +wconfSource1.scala:9: error: method dep in class C is deprecated def t = dep ^ wconfSource1.scala:10: a pure expression does nothing in statement position; multiline expressions might require enclosing parentheses diff --git a/test/files/neg/wconfSource2.check b/test/files/neg/wconfSource2.check index e80567394607..012f4eccf670 100644 --- a/test/files/neg/wconfSource2.check +++ b/test/files/neg/wconfSource2.check @@ -6,7 +6,7 @@ See the Scaladoc for value scala.language.reflectiveCalls for a discussion why the feature should be explicitly enabled. def v(a: { def f: Int }) = a.f ^ -wconfSource2.scala:5: error: method dep in class C is deprecated: +wconfSource2.scala:5: error: method dep in class C is deprecated def t = dep ^ wconfSource2.scala:6: a pure expression does nothing in statement position; multiline expressions might require enclosing parentheses diff --git a/test/files/pos/i10715a.scala b/test/files/pos/i10715a.scala new file mode 100644 index 000000000000..db1b243b2c78 --- /dev/null +++ b/test/files/pos/i10715a.scala @@ -0,0 +1,31 @@ +class Parent { + def f(x: Int): Parent = ??? + def f: Int = 0 + + def g[A](x: Int): Parent = ??? + def g[A]: Int = 0 +} + +// For the issue to show up, there must be a subclass that overrides +// one of the two methods. +class Sub extends Parent { + override def f(x: Int): Parent = ??? + override def g[A](x: Int): Parent = ??? +} + +class C { + def test(c: Sub): Unit = { + c.f(1) // already worked + c.f + c.f.+(0) + c.f.toString + + c.g(0) // already worked + c.g + c.g[Int] + c.g.+(0) + c.g.toString + c.g[Int].+(0) + c.g.toString + } +} diff --git a/test/files/pos/i10715b/C_1.java b/test/files/pos/i10715b/C_1.java new file mode 100644 index 000000000000..8973027a0c3c --- /dev/null +++ b/test/files/pos/i10715b/C_1.java @@ -0,0 +1,16 @@ +class C_1 { + + public int f() { + return 0; + } + public C_1 f(int x) { + return null; + } +} + +class Child extends C_1 { + @Override + public C_1 f(int x) { + return null; + } +} diff --git a/test/files/pos/i10715b/caller_2.scala b/test/files/pos/i10715b/caller_2.scala new file mode 100644 index 000000000000..141d606bf3c9 --- /dev/null +++ b/test/files/pos/i10715b/caller_2.scala @@ -0,0 +1,15 @@ +class C { + def test(c: Child): Unit = { + c.f() // always ok + c.f // should work too + c.f(1) + c.f.toString + } + + // The issue was first detected on NIO buffers, + // (on Java 11+), so these should pass now. + def buffer(c: java.nio.ByteBuffer): Unit = { + c.position + c.position(10).position.toString + } +} diff --git a/test/files/pos/i6705.scala b/test/files/pos/i6705.scala new file mode 100644 index 000000000000..6f0d0327b6d2 --- /dev/null +++ b/test/files/pos/i6705.scala @@ -0,0 +1,15 @@ +trait StringTempl { + def mkString: String + def mkString(x: String): String +} + +object Test { + implicit class extension(val x: String) { + def shouldBe(y: String): Boolean = ??? + } + + def test(tmpl: StringTempl): Unit = { + tmpl.mkString shouldBe "hello" // error + tmpl.mkString(", world") shouldBe "hello, world" + } +} diff --git a/test/files/pos/parens-for-params-silent.scala b/test/files/pos/parens-for-params-silent.scala new file mode 100644 index 000000000000..6eb07a5c3c8a --- /dev/null +++ b/test/files/pos/parens-for-params-silent.scala @@ -0,0 +1,8 @@ +// scalac: -Werror -Wconf:msg=lambda-parens:s -Xsource:3 + +class C { + def f = { + x: Int => x * 2 + } + def g = (x: Int) => x * 2 +} diff --git a/test/files/pos/t0165.scala b/test/files/pos/t0165.scala index 76aef8524017..ca47eb655102 100644 --- a/test/files/pos/t0165.scala +++ b/test/files/pos/t0165.scala @@ -4,7 +4,7 @@ import scala.collection.mutable.LinkedHashMap trait Main { def asMany : ArrayResult = { object result extends LinkedHashMap[String,String] with ArrayResult { - def current = result + def current = this.result } result } diff --git a/test/files/pos/t10211.scala b/test/files/pos/t10211.scala new file mode 100644 index 000000000000..709e7cf41c42 --- /dev/null +++ b/test/files/pos/t10211.scala @@ -0,0 +1,12 @@ +trait QuitePrivate[@specialized(Int) K] { + protected[this] def hasK(k :K) :Boolean +} + +trait MoreOpen extends QuitePrivate[Int] { + override def hasK(k :Int) :Boolean +} + + +object Playground extends App { + def check(guy :MoreOpen) = guy.hasK(42) +} \ No newline at end of file diff --git a/test/files/pos/t10589-case-implicit-param/cc_2.scala b/test/files/pos/t10589-case-implicit-param/cc_2.scala new file mode 100644 index 000000000000..17a1dce15ca9 --- /dev/null +++ b/test/files/pos/t10589-case-implicit-param/cc_2.scala @@ -0,0 +1,6 @@ +// scalac: -Ymacro-annotations +trait T[A] + +@macid +case class CC[A: T](x: A) + diff --git a/test/files/pos/t10589-case-implicit-param/macros_1.scala b/test/files/pos/t10589-case-implicit-param/macros_1.scala new file mode 100644 index 000000000000..700c925d879c --- /dev/null +++ b/test/files/pos/t10589-case-implicit-param/macros_1.scala @@ -0,0 +1,19 @@ +// scalac: -Ymacro-annotations + +import scala.annotation.StaticAnnotation +import scala.language.experimental.macros +import scala.reflect.macros.whitebox.Context + +class macid extends StaticAnnotation { + def macroTransform(annottees: Any*): Any = macro macidMacro.impl +} +object macidMacro { + def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = { + new Macros[c.type](c).macidMacroImpl(annottees.toList) + } +} +class Macros[C <: Context](val c: C) { + import c.universe._ + def macidMacroImpl(annottees: List[c.Expr[Any]]): c.Expr[Any] = + annottees(0) +} diff --git a/test/files/pos/t10639/Identifiable_1.java b/test/files/pos/t10639/Identifiable_1.java new file mode 100644 index 000000000000..56fe89c73b22 --- /dev/null +++ b/test/files/pos/t10639/Identifiable_1.java @@ -0,0 +1,14 @@ + +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Stream; + +import static java.util.stream.Collectors.toMap; + +public interface Identifiable_1 { + String name(); + + static & Identifiable_1> Map valuesByName(final E[] values) { + return Stream.of(values).collect(toMap(Identifiable_1::name, Function.identity())); + } +} diff --git a/test/files/pos/t10639/Size_1.java b/test/files/pos/t10639/Size_1.java new file mode 100644 index 000000000000..2141e41106b8 --- /dev/null +++ b/test/files/pos/t10639/Size_1.java @@ -0,0 +1,3 @@ +public enum Size_1 implements Identifiable_1 { + SMALL, MEDIUM, LARGE; +} diff --git a/test/files/pos/t10639/broken_2.scala b/test/files/pos/t10639/broken_2.scala new file mode 100644 index 000000000000..86739557d334 --- /dev/null +++ b/test/files/pos/t10639/broken_2.scala @@ -0,0 +1,4 @@ + +object Broken extends App { + println(Size_1.SMALL) +} diff --git a/test/files/pos/t11158/CharSeqJava.java b/test/files/pos/t11158/CharSeqJava.java new file mode 100644 index 000000000000..5abe16cd1372 --- /dev/null +++ b/test/files/pos/t11158/CharSeqJava.java @@ -0,0 +1,4 @@ + +public interface CharSeqJava { + int length(); +} diff --git a/test/files/pos/t11158/cs_1.scala b/test/files/pos/t11158/cs_1.scala new file mode 100644 index 000000000000..da6ce3f5109c --- /dev/null +++ b/test/files/pos/t11158/cs_1.scala @@ -0,0 +1,13 @@ + +// not computer science but the basis of the entire field, the character sequence +class cs extends CharSeqJava { + private var n = 42 + override def length: Int = n + def length_=(value: Int): Unit = n = value +} + +object Resetter extends App { + val cs = new cs + cs.length = 0 + assert(cs.length == 0) +} diff --git a/test/files/pos/t11158/sb_2.scala b/test/files/pos/t11158/sb_2.scala new file mode 100644 index 000000000000..246e32d42f70 --- /dev/null +++ b/test/files/pos/t11158/sb_2.scala @@ -0,0 +1,7 @@ + +// sbt is an acronym for StringBuilder test +class sbt { + val sb = new StringBuilder("There once was a man from Killarney / whose comments were nothing but blarney") + + def reset() = sb.length = 42 +} diff --git a/test/files/pos/t11921a.scala b/test/files/pos/t11921a.scala new file mode 100644 index 000000000000..5bee630e57c1 --- /dev/null +++ b/test/files/pos/t11921a.scala @@ -0,0 +1,18 @@ + +class C(x: Int) { + class D extends C(42) { + def f() = println(x) + } +} + +trait T { + val t: Int +} +trait U extends T { + val t: Int + import t._ +} +trait V { this: T => + val t: Int + import t._ +} diff --git a/test/files/pos/t11921b.scala b/test/files/pos/t11921b.scala new file mode 100644 index 000000000000..38870f0cb53b --- /dev/null +++ b/test/files/pos/t11921b.scala @@ -0,0 +1,51 @@ +// scalac: -Werror -Wconf:msg=legacy-binding:s -Xsource:3 + +object test1 { + + class C { + val x = 0 + } + object Test { + val x = 1 + class D extends C { + println(x) // error + } + def f() = + new C { + println(x) // error + } + } +} + +object test2 { + def c(y: Float) = { + class D { + val y = 2 + } + new D { + println(y) // error + } + } +} + +object test3 { + def c(y: Float) = { + class D { + val y = 2 + } + class E extends D { + class F { + println(y) // error + } + } + } +} + +object global + +class C { + val global = 42 +} +object D extends C { + println(global) // OK, since global is defined in package (https://github.com/scala/scala/pull/10220/files#r1109773904) +} diff --git a/test/files/pos/t11921c.scala b/test/files/pos/t11921c.scala new file mode 100644 index 000000000000..d78439424f4e --- /dev/null +++ b/test/files/pos/t11921c.scala @@ -0,0 +1,24 @@ +// scalac: -Xsource:3 + +// test/scaladoc/resources/t5784.scala + +package test.templates { + object `package` { + type String = java.lang.String + val String = new StringCompanion + class StringCompanion { def boo = ??? } + } + + trait Base { + type String = test.templates.String + type T <: Foo + val T: FooExtractor + trait Foo { def foo: Int } + trait FooExtractor { def apply(foo: Int): Unit; def unapply(t: Foo): Option[Int] } + } + + trait Api extends Base { + override type T <: FooApi + trait FooApi extends Foo { def bar: String } + } +} diff --git a/test/files/pos/t12159/H.java b/test/files/pos/t12159/H.java new file mode 100644 index 000000000000..3a15309f733e --- /dev/null +++ b/test/files/pos/t12159/H.java @@ -0,0 +1,19 @@ +// javaVersion: 17+ +package p; + +sealed public class H { +} + +final class K extends H { +} + +non-sealed class L extends H { +} + +sealed +class P extends H { +} + +final +class Q extends P { +} diff --git a/test/files/pos/t12159/I.java b/test/files/pos/t12159/I.java new file mode 100644 index 000000000000..f91c69dd7828 --- /dev/null +++ b/test/files/pos/t12159/I.java @@ -0,0 +1,6 @@ +// javaVersion: 17+ + +package p; + +sealed interface I permits J { +} diff --git a/test/files/pos/t12159/J.java b/test/files/pos/t12159/J.java new file mode 100644 index 000000000000..5bd2c4c92374 --- /dev/null +++ b/test/files/pos/t12159/J.java @@ -0,0 +1,6 @@ +// javaVersion: 17+ + +package p; + +sealed public class J implements I permits M { +} diff --git a/test/files/pos/t12159/M.java b/test/files/pos/t12159/M.java new file mode 100644 index 000000000000..245c79304d29 --- /dev/null +++ b/test/files/pos/t12159/M.java @@ -0,0 +1,9 @@ +// javaVersion: 17+ + +package p; + +public final class M extends J { +} + +final class N extends L { +} diff --git a/test/files/pos/t12159/s.scala b/test/files/pos/t12159/s.scala new file mode 100644 index 000000000000..29eb9518ea6c --- /dev/null +++ b/test/files/pos/t12159/s.scala @@ -0,0 +1,6 @@ +// javaVersion: 17+ + +package p + +class S extends L { +} diff --git a/test/files/pos/t12159b/H_1.java b/test/files/pos/t12159b/H_1.java new file mode 100644 index 000000000000..cb3ccb9749fc --- /dev/null +++ b/test/files/pos/t12159b/H_1.java @@ -0,0 +1,14 @@ +// javaVersion: 17+ +package p; + +sealed abstract public class H_1 { +} + +final class J extends H_1 { +} + +final class K extends H_1 { +} + +final class L extends H_1 { +} diff --git a/test/files/pos/t12159b/s_2.scala b/test/files/pos/t12159b/s_2.scala new file mode 100644 index 000000000000..881204f4d830 --- /dev/null +++ b/test/files/pos/t12159b/s_2.scala @@ -0,0 +1,13 @@ +// javaVersion: 17+ +// scalac: -Werror +package p + +class C { + def f(h: H_1) = + h match { + case j: J => j.toString + case k: K => k.toString + case l: L => l.toString + } +} + diff --git a/test/files/pos/t12474/Nat.java b/test/files/pos/t12474/Nat.java new file mode 100644 index 000000000000..c9de3f590d84 --- /dev/null +++ b/test/files/pos/t12474/Nat.java @@ -0,0 +1,6 @@ +// javaVersion: 17+ + +public sealed interface Nat permits Nat.Zero, Nat.Succ { + public static final record Zero() implements Nat {} + public static final record Succ(Nat pred) implements Nat {} +} diff --git a/test/files/pos/t12474/s.scala b/test/files/pos/t12474/s.scala new file mode 100644 index 000000000000..2f4e4ed3a9ad --- /dev/null +++ b/test/files/pos/t12474/s.scala @@ -0,0 +1,5 @@ +// javaVersion: 17+ + +class S { + def j: Nat = new Nat.Zero +} diff --git a/test/files/pos/t12513c/c.scala b/test/files/pos/t12513c/c.scala new file mode 100644 index 000000000000..927c87347983 --- /dev/null +++ b/test/files/pos/t12513c/c.scala @@ -0,0 +1 @@ +package p { class C } diff --git a/test/files/pos/t12513c/xy.scala b/test/files/pos/t12513c/xy.scala new file mode 100644 index 000000000000..d46ea3f556a8 --- /dev/null +++ b/test/files/pos/t12513c/xy.scala @@ -0,0 +1,3 @@ +import p._ +package p { class X extends C } // not ambiguous (compiles without the import) +package q { class Y extends C } // requires the import diff --git a/test/files/pos/t12623.scala b/test/files/pos/t12623.scala new file mode 100644 index 000000000000..a51be649dcec --- /dev/null +++ b/test/files/pos/t12623.scala @@ -0,0 +1,19 @@ + +trait MapI[C] { + def i: Int + def s: String + def copy(i: Int = this.i, s: String = this.s): C + def mapI(i: Int): C = copy(i) +} + +case class C(i: Int, s: String) extends MapI[C] + +/* +was: +t12623.scala:9: error: class C needs to be abstract. +Missing implementation for member of trait MapI: + def copy(i: Int, s: String): C = ??? + +case class C(i: Int, s: String) extends MapI[C] + ^ + */ diff --git a/test/files/pos/t12647/Macro_1.scala b/test/files/pos/t12647/Macro_1.scala new file mode 100644 index 000000000000..a808e46089a3 --- /dev/null +++ b/test/files/pos/t12647/Macro_1.scala @@ -0,0 +1,13 @@ + +// scalac: -Xsource:3 + +import scala.reflect.macros.whitebox.Context + +trait Result + +object Macros { + def impl(c: Context) = { + import c.universe._ + q"""new Result { def value = "Was this the answer you sought?" }""" + } +} diff --git a/test/files/pos/t12647/Resolve_2.scala b/test/files/pos/t12647/Resolve_2.scala new file mode 100644 index 000000000000..dab65c6a310b --- /dev/null +++ b/test/files/pos/t12647/Resolve_2.scala @@ -0,0 +1,13 @@ + +// scalac: -Xsource:3 + +import language.experimental.macros + +trait Resolver { + def resolve: Result = ??? +} + +class ValueResolver extends Resolver { + override def resolve = valueResult + def valueResult: Result = macro Macros.impl +} diff --git a/test/files/pos/t12647/Test_3.scala b/test/files/pos/t12647/Test_3.scala new file mode 100644 index 000000000000..152e5ddc4aa4 --- /dev/null +++ b/test/files/pos/t12647/Test_3.scala @@ -0,0 +1,7 @@ + +// scalac: -Xsource:3 + +object Test extends App { + val resolver = new ValueResolver + println(resolver.resolve.value) +} diff --git a/test/files/pos/t12647b/Macro_1.scala b/test/files/pos/t12647b/Macro_1.scala new file mode 100644 index 000000000000..e54621c987e4 --- /dev/null +++ b/test/files/pos/t12647b/Macro_1.scala @@ -0,0 +1,11 @@ + +import scala.reflect.macros.whitebox.Context + +trait Result + +object Macros { + def impl(c: Context): c.Tree = { + import c.universe._ + q"""new Result { def value = "Was this the answer you sought?" }""" + } +} diff --git a/test/files/pos/t12647b/Resolve_2.scala b/test/files/pos/t12647b/Resolve_2.scala new file mode 100644 index 000000000000..cbf1457b8635 --- /dev/null +++ b/test/files/pos/t12647b/Resolve_2.scala @@ -0,0 +1,11 @@ + +import language.experimental.macros + +abstract class Resolver { + def resolve: Result = ??? +} + +class ValueResolver extends Resolver { + override def resolve = valueResult + def valueResult: Result = macro Macros.impl +} diff --git a/test/files/pos/t12647b/Test_3.scala b/test/files/pos/t12647b/Test_3.scala new file mode 100644 index 000000000000..16149d9965bc --- /dev/null +++ b/test/files/pos/t12647b/Test_3.scala @@ -0,0 +1,5 @@ + +object Test extends App { + val resolver = new ValueResolver + println(resolver.resolve.value) +} diff --git a/test/files/pos/t12666.scala b/test/files/pos/t12666.scala new file mode 100644 index 000000000000..33d336b3224a --- /dev/null +++ b/test/files/pos/t12666.scala @@ -0,0 +1,32 @@ + +package foo { + + trait Baz[A] + object Baz { + implicit def instance[A]: Baz[A] = ??? + } + + package syntax { + object all { + implicit def ops1[A: Baz](a: A): BarOps1 = new BarOps1(a) + implicit def ops2[A: Baz](a: A): BarOps2 = new BarOps2(a) + } + + class BarOps1(val a: Any) extends AnyVal { + def bar(x: Int): String = ??? + } + + class BarOps2(val a: Any) extends AnyVal { + private[syntax] def bar(x: Int): String = ??? + } + } +} + +import foo.syntax.all._ + +object Main { + def main(args: Array[String]): Unit = { + val a = new Object + a.bar(42) + } +} diff --git a/test/files/pos/t12671.scala b/test/files/pos/t12671.scala new file mode 100644 index 000000000000..9db7ec760751 --- /dev/null +++ b/test/files/pos/t12671.scala @@ -0,0 +1,30 @@ + +// scalac: -Xsource:3 + +import scala.collection.{mutable, IterableOnce} +import scala.collection.immutable.{AbstractSet, Set, SetOps} + +final case class Foo[-T](components: IndexedSeq[Int]) + +sealed trait FooTrie[T] + extends AbstractSet[Foo[T]] + with SetOps[Foo[T], Set, FooTrie[T]] { + + override def fromSpecific( + coll: IterableOnce[Foo[T]] + ): FooTrie[T] = { + coll.iterator.foldLeft(empty)(_ incl _) // error here + } + + override def newSpecificBuilder + : mutable.Builder[Foo[T], FooTrie[T]] = ??? + + override def incl(elem: Foo[T]): FooTrie[T] = ??? + + override def empty = FooTrie.empty[T] + //override def empty: FooTrie[T] = FooTrie.empty[T] +} + +object FooTrie { + def empty[T]: FooTrie[T] = ??? +} diff --git a/test/files/pos/t12673/A.java b/test/files/pos/t12673/A.java new file mode 100644 index 000000000000..faaf0d26761f --- /dev/null +++ b/test/files/pos/t12673/A.java @@ -0,0 +1,4 @@ +package compiletest.a; +public class A { + protected static class InnerParent { } +} diff --git a/test/files/pos/t12673/B.java b/test/files/pos/t12673/B.java new file mode 100644 index 000000000000..d94349074731 --- /dev/null +++ b/test/files/pos/t12673/B.java @@ -0,0 +1,6 @@ +package compiletest.b; +import compiletest.a.A; + +public class B extends A { + public static class InnerChild extends InnerParent { } +} diff --git a/test/files/pos/t12673/Test.scala b/test/files/pos/t12673/Test.scala new file mode 100644 index 000000000000..1f867027731b --- /dev/null +++ b/test/files/pos/t12673/Test.scala @@ -0,0 +1,5 @@ +package client + +object Client { + new compiletest.b.B.InnerChild +} diff --git a/test/files/pos/t12712.scala b/test/files/pos/t12712.scala new file mode 100644 index 000000000000..195743b46770 --- /dev/null +++ b/test/files/pos/t12712.scala @@ -0,0 +1,16 @@ +// scalac: -Werror +object T { + private sealed trait T + private object O extends T + private trait U extends T + private object P extends U + + private def t(t: T) = t match { + case O => () + case _: U => println("hai") + } + + def main(args: Array[String]): Unit = { + t(P) + } +} diff --git a/test/files/pos/t12736/bar_2.scala b/test/files/pos/t12736/bar_2.scala new file mode 100644 index 000000000000..3e3bd2b25cf3 --- /dev/null +++ b/test/files/pos/t12736/bar_2.scala @@ -0,0 +1,3 @@ +package bar + +private object Foo diff --git a/test/files/pos/t12736/baz_2.scala b/test/files/pos/t12736/baz_2.scala new file mode 100644 index 000000000000..187c805bde2b --- /dev/null +++ b/test/files/pos/t12736/baz_2.scala @@ -0,0 +1,15 @@ +package bar +package baz + +import foo._ + +object Bar { + def test() = println(Foo) +} + +/* +baz_2.scala:8: error: object Foo in package foo cannot be accessed as a member of package foo from object Bar in package baz + def test() = println(Foo) + ^ +1 error +*/ diff --git a/test/files/pos/t12736/foo_1.scala b/test/files/pos/t12736/foo_1.scala new file mode 100644 index 000000000000..b7fd1b4355e5 --- /dev/null +++ b/test/files/pos/t12736/foo_1.scala @@ -0,0 +1,3 @@ +package foo + +private object Foo diff --git a/test/files/pos/t2799.scala b/test/files/pos/t2799.scala index b21f0739bc77..fb951471494f 100644 --- a/test/files/pos/t2799.scala +++ b/test/files/pos/t2799.scala @@ -1,11 +1,4 @@ - // scalac: -Xlint -Werror -@deprecated("hi mom", "") case class Bob () - -@deprecated("other mother", "") -trait T - -object T extends T { - def t = Bob() -} +@deprecated("hi mom", "") +case class Bob () diff --git a/test/files/pos/t3218.scala b/test/files/pos/t3218.scala new file mode 100644 index 000000000000..cbf9277dfdbe --- /dev/null +++ b/test/files/pos/t3218.scala @@ -0,0 +1,41 @@ + +import java.util.{List => JList} +import scala.collection.mutable +import scala.collection.JavaConverters.asScalaBuffer +import scala.language.existentials +import scala.language.implicitConversions + +object Test extends App { + + def fromJava[T](li: JList[T]): List[T] = asScalaBuffer(li).toList + + implicit def `list asScalaBuffer`[A](l: JList[A]): mutable.Buffer[A] = asScalaBuffer(l) + + // implicit conversion - ok + def test1(jList:JList[_]) = jList.filter(_.isInstanceOf[java.lang.Object]) + + // explicit conversion - ok + def test2(jList:JList[_]) = { + val f = fromJava(jList).filter _ + f(_.isInstanceOf[java.lang.Object]) + } + + // implicit conversion - error + def test3(jList:JList[_]) = { + val f = jList.filter _ + f(_.isInstanceOf[java.lang.Object]) + } + + val ss: JList[String] = { + val res = new java.util.ArrayList[String] + res.add("hello, world") + res + } + + println {( + test1(ss), + test2(ss), + test3(ss), + )} + +} diff --git a/test/files/pos/t3218b.scala b/test/files/pos/t3218b.scala new file mode 100644 index 000000000000..d9c6b8e6ecea --- /dev/null +++ b/test/files/pos/t3218b.scala @@ -0,0 +1,35 @@ + +import language.implicitConversions + +trait T +trait U { + def u(x: Any) = x.toString * 2 +} +class C { + implicit def cv(t: T): U = new U {} + def f(t: T): Any => String = cv(t).u _ + def g(t: T): Any => String = t.u _ + def h(t: T) = t.u _ +} + +object Test extends App { + val c = new C + val t = new T {} + println {( + c.f(t)("f"), + c.g(t)("g"), + c.h(t)("h"), + )} +} + +/* +2.11, 2.12 say: +t3218b.scala:11: error: _ must follow method; cannot follow Any => String + def g(t: T): Any => String = t.u _ + ^ +t3218b.scala:12: error: missing argument list for method u in trait U +Unapplied methods are only converted to functions when a function type is expected. +You can make this conversion explicit by writing `u _` or `u(_)` instead of `u`. + def h(t: T) = t.u _ + ^ +*/ diff --git a/test/files/pos/t3420b.scala b/test/files/pos/t3420b.scala new file mode 100644 index 000000000000..6b5f61019633 --- /dev/null +++ b/test/files/pos/t3420b.scala @@ -0,0 +1,8 @@ + +// scalac: --release 8 -opt:inline:** -Wopt -Werror +// +class C { + val cv = Map[Int, Int](1 -> 2) + lazy val cl = Map[Int, Int](1 -> 2) + def cd = Map[Int, Int](1 -> 2) +} diff --git a/test/files/pos/t3672.scala b/test/files/pos/t3672.scala index b2752ce21ff7..2c17a17ff8b1 100644 --- a/test/files/pos/t3672.scala +++ b/test/files/pos/t3672.scala @@ -1,4 +1,22 @@ object Test { - def foo(f: Int => Int) = () ; foo { implicit x : Int => x + 1 } - def bar(f: Int => Int) = () ; foo { x : Int => x + 1 } + def foo(f: Int => Int) = () + def test(): Unit = { + foo { x => x + 1 } + foo { implicit x => x + 1 } + foo { x: Int => x + 1 } + foo { implicit x: Int => x + 1 } + foo { _ => 42 } + foo { implicit _ => implicitly[Int] + 1 } // scala 2 deficit + foo { _: Int => 42 } + foo { implicit _: Int => implicitly[Int] + 1 } // scala 2 deficit + + foo(x => x + 1) + foo(implicit x => x + 1) + foo((x: Int) => x + 1) + foo(implicit (x: Int) => x + 1) // scala 3 + foo(_ => 42) + foo(implicit _ => implicitly[Int] + 1) // scala 2 deficit + foo((_: Int) => 42) + foo(implicit (_: Int) => implicitly[Int] + 1) // scala 3 + } } diff --git a/test/files/pos/t5265a.scala b/test/files/pos/t5265a.scala new file mode 100644 index 000000000000..95db6ec743ed --- /dev/null +++ b/test/files/pos/t5265a.scala @@ -0,0 +1,32 @@ +// scalac: -Werror -Wconf:cat=other-implicit-type:s +trait T[A] + +class C[A: T] + +trait Missing { + implicit val tsMissing = new T[String] {} // warn val in trait + def f = new C[String] +} +trait Local { + def f = { + implicit val tsLocal = new T[String] {} // nowarn because local + new C[String] + } +} +trait Parent { + def t: T[String] +} +trait Child extends Parent { + implicit val tsChild = new T[String] {} // warn because inferred from RHS + def f = new C[String] + implicit private[this] val pChild = 42 // also warn +} +class D { + implicit val tsD = new T[String] {} // warn val in class + def f = new C[String] + implicit private[this] val y = 42 // also warn +} +class X extends Missing +trait Z { + val z = 42 +} diff --git a/test/files/pos/t7212.scala b/test/files/pos/t7212.scala index 1c1a3f07a2c2..4b53c034ff1f 100644 --- a/test/files/pos/t7212.scala +++ b/test/files/pos/t7212.scala @@ -13,3 +13,14 @@ class C extends B { override def f: Option[String] = Some("goodbye, cruel world") override def remove(): Unit = println("removed! (not really)") } + +trait T { def f: Object } +class K extends T { def f = "" } +object K { + val k = new K + val s: Any = k.f +} + +trait U extends T { def f = "" } +trait V { var v: Any } +trait W extends V { var v = "" } diff --git a/test/files/pos/t8365.scala b/test/files/pos/t8365.scala new file mode 100644 index 000000000000..5f7547790e5f --- /dev/null +++ b/test/files/pos/t8365.scala @@ -0,0 +1,10 @@ + +class A { + def f = + this match { + case this => + } + val this = this +} + + diff --git a/test/files/pos/t9490.scala b/test/files/pos/t9490.scala new file mode 100644 index 000000000000..e874724d092b --- /dev/null +++ b/test/files/pos/t9490.scala @@ -0,0 +1,27 @@ +// scalac: -Werror -Xlint:inaccessible + +package ws { + private[ws] trait Foo + private[ws] object Test { + class Bar { + def apply(f: Foo) = ??? + } + } +} + +package p { + private[p] class D + sealed trait T { def f(d: D): Unit } + final class C extends T { def f(d: D) = () } +} + +/* was: +t9490.scala:7: warning: method apply in class Bar references private[ws] trait Foo. +Classes which cannot access Foo may be unable to override apply. + def apply(f: Foo) = ??? + ^ +t9490.scala:14: warning: method f in trait T references private[p] class D. +Classes which cannot access D may be unable to provide a concrete implementation of f. + sealed trait T { def f(d: D): Unit } + ^ + */ diff --git a/test/files/run/Meter.scala b/test/files/run/Meter.scala index 9fa8a7babd7d..7ef9d247e1ba 100644 --- a/test/files/run/Meter.scala +++ b/test/files/run/Meter.scala @@ -23,7 +23,7 @@ package a { def apply(x: Double): Meter = new Meter(x) - implicit val boxings = new BoxingConversions[Meter, Double] { + implicit val boxings: BoxingConversions[Meter, Double] = new BoxingConversions[Meter, Double] { def box(x: Double) = new Meter(x) def unbox(m: Meter) = m.underlying } @@ -35,7 +35,7 @@ package a { override def toString = unbox.toString+"ft" } object Foot { - implicit val boxings = new BoxingConversions[Foot, Double] { + implicit val boxings: BoxingConversions[Foot, Double] = new BoxingConversions[Foot, Double] { def box(x: Double) = new Foot(x) def unbox(m: Foot) = m.unbox } diff --git a/test/files/run/MeterCaseClass.scala b/test/files/run/MeterCaseClass.scala index 8c15bbb6d30b..1f40dc5e7dbc 100644 --- a/test/files/run/MeterCaseClass.scala +++ b/test/files/run/MeterCaseClass.scala @@ -20,7 +20,7 @@ package a { private[a] trait MeterArg - implicit val boxings = new BoxingConversions[Meter, Double] { + implicit val boxings: BoxingConversions[Meter, Double] = new BoxingConversions[Meter, Double] { def box(x: Double) = new Meter(x) def unbox(m: Meter) = m.underlying } @@ -32,7 +32,7 @@ package a { override def toString = unbox.toString+"ft" } object Foot { - implicit val boxings = new BoxingConversions[Foot, Double] { + implicit val boxings: BoxingConversions[Foot, Double] = new BoxingConversions[Foot, Double] { def box(x: Double) = new Foot(x) def unbox(m: Foot) = m.unbox } diff --git a/test/files/run/analyzerPlugins.check b/test/files/run/analyzerPlugins.check index 2659fd3b3e6e..7338a468ff1f 100644 --- a/test/files/run/analyzerPlugins.check +++ b/test/files/run/analyzerPlugins.check @@ -1,4 +1,3 @@ -warning: 1 deprecation (since 2.13.0); re-run with -deprecation for details adaptBoundsToAnnots(List( <: Int), List(type T), List(Int @testAnn)) [2] annotationsConform(Boolean @testAnn, Boolean @testAnn) [2] annotationsConform(Boolean @testAnn, Boolean) [1] diff --git a/test/files/run/analyzerPlugins.scala b/test/files/run/analyzerPlugins.scala index 81b085d74fcb..e5720649b7db 100644 --- a/test/files/run/analyzerPlugins.scala +++ b/test/files/run/analyzerPlugins.scala @@ -2,6 +2,7 @@ import scala.tools.partest._ import scala.tools.nsc._ object Test extends DirectTest { + override def extraSettings: String = s"${super.extraSettings} '-Wconf:cat=deprecation&msg=early initializers:s'" def code = """ class testAnn extends annotation.TypeConstraint diff --git a/test/files/run/argfile.scala b/test/files/run/argfile.scala index 6fffbac7e9bc..5fdb17fc988e 100644 --- a/test/files/run/argfile.scala +++ b/test/files/run/argfile.scala @@ -23,7 +23,8 @@ object Test extends DirectTest { } def code = sm""" - |@annotation.nowarn + |import annotation.* + |@nowarn |final class C { | def f: Int = "42".toInt |} diff --git a/test/files/run/blank.scala b/test/files/run/blank.scala new file mode 100644 index 000000000000..0a96ee5f5b49 --- /dev/null +++ b/test/files/run/blank.scala @@ -0,0 +1,11 @@ +// javaVersion: 11+ +// +// skalac: --release:8 +// trivial manual test for partest --realeasy, which sets --release:8. +// under --realeasy, skip this test because of the javaVersion, irrespective of JDK in use. +// otherwise, this test passes trivially on JDK11+ and is skipped on lesser JDKs. +// note that explicit --release:8 asks to compile against JDK8 but only run on the requested version. + +object Test extends App { + assert("".isBlank) // String#isBlank was added in JDK11 +} diff --git a/test/files/run/dotty-i11332.scala b/test/files/run/dotty-i11332.scala new file mode 100644 index 000000000000..9241b922504d --- /dev/null +++ b/test/files/run/dotty-i11332.scala @@ -0,0 +1,71 @@ +import java.lang.invoke._, MethodType.methodType + +class Foo { + def neg(x: Int): Int = -x + def rev(s: String): String = s.reverse + def over(l: Long): String = "long" + def over(i: Int): String = "int" + def unit(s: String): Unit = () + def obj(s: String): Object = s + def void(v: Void): String = "void" +} + +object Test { + def main(args: Array[String]): Unit = { + val l = MethodHandles.lookup() + val self = new Foo() + val mhNeg = l.findVirtual(classOf[Foo], "neg", methodType(classOf[Int], classOf[Int])) + val mhRev = l.findVirtual(classOf[Foo], "rev", methodType(classOf[String], classOf[String])) + val mhOverL = l.findVirtual(classOf[Foo], "over", methodType(classOf[String], classOf[Long])) + val mhOverI = l.findVirtual(classOf[Foo], "over", methodType(classOf[String], classOf[Int])) + val mhUnit = l.findVirtual(classOf[Foo], "unit", methodType(classOf[Unit], classOf[String])) + val mhObj = l.findVirtual(classOf[Foo], "obj", methodType(classOf[Any], classOf[String])) + val mhVoid = l.findVirtual(classOf[Foo], "void", methodType(classOf[String], classOf[Void])) + + assert(-42 == (mhNeg.invokeExact(self, 42): Int)) + assert(-33 == (mhNeg.invokeExact(self, 33): Int)) + + assert("oof" == (mhRev.invokeExact(self, "foo"): String)) + assert("rab" == (mhRev.invokeExact(self, "bar"): String)) + + assert("long" == (mhOverL.invokeExact(self, 1L): String)) + assert("int" == (mhOverI.invokeExact(self, 1): String)) + + assert(-3 == (id(mhNeg.invokeExact(self, 3)): Int)) + expectWrongMethod(mhNeg.invokeExact(self, 4)) + + { mhUnit.invokeExact(self, "hi"): Unit; () } // explicit block + val hi2: Unit = mhUnit.invokeExact(self, "hi2") + assert((()) == (hi2: Any)) + def hi3: Unit = mhUnit.invokeExact(self, "hi3") + assert((()) == (hi3: Any)) + + { mhObj.invokeExact(self, "any"); () } // explicit block + val any2 = mhObj.invokeExact(self, "any2") + assert("any2" == any2) + def any3 = mhObj.invokeExact(self, "any3") + assert("any3" == any3) + + assert("void" == (mhVoid.invokeExact(self, null: Void): String)) + + expectWrongMethod { + l // explicit chain method call + .findVirtual(classOf[Foo], "neg", methodType(classOf[Int], classOf[Int])) + .invokeExact(self, 3) + } + val res4 = { + l // explicit chain method call + .findVirtual(classOf[Foo], "neg", methodType(classOf[Int], classOf[Int])) + .invokeExact(self, 4): Int + } + assert(-4 == res4) + } + + def id[T](x: T): T = x + + def expectWrongMethod(op: => Any) = try { + op + throw new AssertionError("expected operation to fail but it didn't") + } catch { case expected: WrongMethodTypeException => () } + +} diff --git a/test/files/run/dotty-i11332b.scala b/test/files/run/dotty-i11332b.scala new file mode 100644 index 000000000000..2627029541fa --- /dev/null +++ b/test/files/run/dotty-i11332b.scala @@ -0,0 +1,19 @@ +// javaVersion: 11+ +// scalac: -release:11 + +import java.lang.invoke._, MethodType.methodType + +object Test { + def main(args: Array[String]): Unit = { + val l = MethodHandles.lookup() + val mhCL = l.findStatic(classOf[ClassLoader], "getPlatformClassLoader", methodType(classOf[ClassLoader])) + // `invoke` and `invokeExact` are both signature polymorphic + assert(null != (mhCL.invoke(): ClassLoader)) + assert(null != (mhCL.invoke().asInstanceOf[ClassLoader]: ClassLoader)) + assert(null != (mhCL.invokeExact(): ClassLoader)) + // I've commented out this part of the Dotty test because here in Scala 2, + // we didn't implement specifying a signature polymorphic method's return type + // via `asInstanceOf`; we only implemented specifying it via type ascription + // assert(null != (mhCL.invokeExact().asInstanceOf[ClassLoader]: ClassLoader)) + } +} diff --git a/test/files/run/dotty-t12348.scala b/test/files/run/dotty-t12348.scala new file mode 100644 index 000000000000..b655da3012dc --- /dev/null +++ b/test/files/run/dotty-t12348.scala @@ -0,0 +1,23 @@ +// javaVersion: 11+ +// scalac: -release:11 + +import java.lang.invoke._ +import scala.runtime.IntRef + +object Test { + def main(args: Array[String]): Unit = { + val ref = new scala.runtime.IntRef(0) + val varHandle = MethodHandles.lookup() + .in(classOf[IntRef]) + .findVarHandle(classOf[IntRef], "elem", classOf[Int]) + assert(0 == (varHandle.getAndSet(ref, 1): Int)) + assert(1 == (varHandle.getAndSet(ref, 2): Int)) + assert(2 == ref.elem) + + assert((()) == (varHandle.set(ref, 3): Any)) + assert(3 == (varHandle.get(ref): Int)) + + assert(true == (varHandle.compareAndSet(ref, 3, 4): Any)) + assert(4 == (varHandle.get(ref): Int)) + } +} diff --git a/test/files/run/eta-dependent.check b/test/files/run/eta-dependent.check new file mode 100644 index 000000000000..4d21f8dd08c4 --- /dev/null +++ b/test/files/run/eta-dependent.check @@ -0,0 +1,48 @@ + +scala> object defs { + val a = "obj" + def aa: a.type = a + def s = this + def f(x: Int): a.type = a + def g(x: Int)(y: x.type) = 0 + def h(x: a.type)(y: a.type) = 0 +} +object defs + +scala> import defs._ +import defs._ + +scala> val f1 = f _ +val f1: Int => defs.a.type = + +scala> val f2: Int => a.type = f +val f2: Int => defs.a.type = + +scala> val f3: Int => Object = f +val f3: Int => Object = + +scala> val g1 = g(10) _ +val g1: Int(10) => Int = + +scala> val g2: 10 => Int = g1 +val g2: 10 => Int = + +scala> val g3: 11 => Int = g(11) +val g3: 11 => Int = + +scala> val g4: Int => Int = g(11) // mismatch + ^ + error: type mismatch; + found : Int(11) => Int + required: Int => Int + +scala> val h1 = s.h(aa) _ +val h1: defs.a.type => Int = + +scala> val h2: a.type => Int = h1 +val h2: defs.a.type => Int = + +scala> val h3: a.type => Int = s.h(aa) +val h3: defs.a.type => Int = + +scala> :quit diff --git a/test/files/run/eta-dependent.scala b/test/files/run/eta-dependent.scala new file mode 100644 index 000000000000..1ceb5957cfd3 --- /dev/null +++ b/test/files/run/eta-dependent.scala @@ -0,0 +1,72 @@ +object NoMoreNeg { + def foo(x: AnyRef): x.type = x + val x: AnyRef => Any = foo +} + +object t12641 { + def f(sb: StringBuilder) = Option("").foreach(sb.append) +} + +object t12641a { + trait A { + def foo(s: String): this.type + def foo(s: Int): this.type + } + trait T { + val a1: A + val o: Option[String] + + def t(a2: A): Unit = { + o.foreach(a1.foo) + o.foreach(a2.foo) + + val f2: String => a2.type = a2.foo + val f3: String => A = a2.foo + } + } +} + +object t12641b { + trait A { + def foo(s: String): this.type + } + trait T { + val a1: A + val o: Option[String] + + def t(a2: A): Unit = { + o.foreach(a1.foo) + o.foreach(a2.foo) + + val f1 = a2.foo _ + val f2: String => a2.type = a2.foo + val f3: String => A = a2.foo + } + } +} + +import scala.tools.partest._ + +object Test extends ReplTest with Lambdaless { + def code = """ +object defs { + val a = "obj" + def aa: a.type = a + def s = this + def f(x: Int): a.type = a + def g(x: Int)(y: x.type) = 0 + def h(x: a.type)(y: a.type) = 0 +} +import defs._ +val f1 = f _ +val f2: Int => a.type = f +val f3: Int => Object = f +val g1 = g(10) _ +val g2: 10 => Int = g1 +val g3: 11 => Int = g(11) +val g4: Int => Int = g(11) // mismatch +val h1 = s.h(aa) _ +val h2: a.type => Int = h1 +val h3: a.type => Int = s.h(aa) +""".trim +} diff --git a/test/files/run/idempotency-lazy-vals.scala b/test/files/run/idempotency-lazy-vals.scala index 8688add3e699..236e1516137c 100644 --- a/test/files/run/idempotency-lazy-vals.scala +++ b/test/files/run/idempotency-lazy-vals.scala @@ -7,7 +7,7 @@ object Test extends App { val lazee = reify { class C { lazy val x = 2 - implicit lazy val y = 3 + implicit lazy val y: Int = 3 } val c = new C() import c._ diff --git a/test/files/run/impconvtimes.scala b/test/files/run/impconvtimes.scala index 477a16a8903d..d377685a9c77 100644 --- a/test/files/run/impconvtimes.scala +++ b/test/files/run/impconvtimes.scala @@ -9,7 +9,7 @@ object Test { def *(newUnit: Unit) = Measure(scalar, newUnit) } - implicit def double2Measure(scalar: Double) = + implicit def double2Measure(scalar: Double): Measure = Measure(scalar, NoUnit) diff --git a/test/files/run/implicits.scala b/test/files/run/implicits.scala index 5681a9d484dc..fbc8536c041b 100644 --- a/test/files/run/implicits.scala +++ b/test/files/run/implicits.scala @@ -2,7 +2,7 @@ import scala.language.implicitConversions object A { object B { - implicit def int2string(x: Int) = "["+x.toString+"]" + implicit def int2string(x: Int): String = "["+x.toString+"]" } } diff --git a/test/files/run/is-valid-num.scala b/test/files/run/is-valid-num.scala index eb364e055580..ef2388f8baa3 100644 --- a/test/files/run/is-valid-num.scala +++ b/test/files/run/is-valid-num.scala @@ -1,6 +1,5 @@ -/* - * filter: inliner warnings; re-run with - */ +// scalac: -Xlint -Werror +@annotation.nowarn("cat=deprecation&msg=isWhole") object Test { def x = BigInt("10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") def y = BigDecimal("" + (Short.MaxValue + 1) + ".0") @@ -11,7 +10,7 @@ object Test { def l2 = Int.MinValue.toLong - 1 def main(args: Array[String]): Unit = { -// assert(x.isWhole, x) + assert(x.isWhole, x) assert(!x.isValidDouble, x) assert(!x.isValidFloat, x) assert(!x.isValidLong, x) @@ -171,7 +170,7 @@ object Test { if (!d.isInfinity) { val bd = BigDecimal(new java.math.BigDecimal(d)) -// assert(!bd.isWhole, bd) + assert(!bd.isWhole, bd) assert(bd.isExactDouble, bd) assert(bd.isExactFloat == isFloat, bd) assert(!bd.isValidLong, bd) @@ -221,7 +220,7 @@ object Test { assert(bd.isValidShort == isShort, bd) assert(bd.isValidByte == isByte, bd) -// assert(bi.isWhole, bi) + assert(bi.isWhole, bi) assert(bi.isValidDouble == isDouble, bi) assert(bi.isValidFloat == isFloat, bi) assert(bi.isValidLong == isLong, bi) diff --git a/test/files/run/macro-expand-implicit-macro-has-implicit/Macros_Test_2.scala b/test/files/run/macro-expand-implicit-macro-has-implicit/Macros_Test_2.scala index 46367cd1a307..a126bd813d46 100644 --- a/test/files/run/macro-expand-implicit-macro-has-implicit/Macros_Test_2.scala +++ b/test/files/run/macro-expand-implicit-macro-has-implicit/Macros_Test_2.scala @@ -1,6 +1,6 @@ import scala.language.experimental.macros object Test extends App { - implicit val x = 42 + implicit val x: Int = 42 def foo(implicit x: Int): Unit = macro Impls.foo foo } diff --git a/test/files/run/macro-implicit-decorator/Test_2.scala b/test/files/run/macro-implicit-decorator/Test_2.scala index bfcb57986997..88c60d91b09f 100644 --- a/test/files/run/macro-implicit-decorator/Test_2.scala +++ b/test/files/run/macro-implicit-decorator/Test_2.scala @@ -5,9 +5,9 @@ class CustomClass trait MyTC[A] object MyTC { - implicit val forInt = new MyTC[Int] {} - implicit def forList[A](implicit a: Derivation[MyTC[A]]) = new MyTC[List[A]] {} - implicit def forCustomClass(implicit a: Derivation[MyTC[List[Boolean]]]) = new MyTC[CustomClass] {} + implicit val forInt: MyTC[Int] = new MyTC[Int] {} + implicit def forList[A](implicit a: Derivation[MyTC[A]]): MyTC[List[A]] = new MyTC[List[A]] {} + implicit def forCustomClass(implicit a: Derivation[MyTC[List[Boolean]]]): MyTC[CustomClass] = new MyTC[CustomClass] {} } object Test extends App { diff --git a/test/files/run/macro-typecheck-implicitsdisabled.check b/test/files/run/macro-typecheck-implicitsdisabled.check index d5f0afc71706..b937087183e4 100644 --- a/test/files/run/macro-typecheck-implicitsdisabled.check +++ b/test/files/run/macro-typecheck-implicitsdisabled.check @@ -1,3 +1,3 @@ scala.Predef.ArrowAssoc[Int](1).->[Int](2) scala.reflect.macros.TypecheckException: value -> is not a member of Int -did you mean >>>? +did you mean >>? diff --git a/test/files/run/macroPlugins-macroExpand/Plugin_1.scala b/test/files/run/macroPlugins-macroExpand/Plugin_1.scala index f62c34a1b102..bbccd32ededf 100644 --- a/test/files/run/macroPlugins-macroExpand/Plugin_1.scala +++ b/test/files/run/macroPlugins-macroExpand/Plugin_1.scala @@ -18,7 +18,7 @@ class Plugin(val global: Global) extends NscPlugin { object expander extends DefMacroExpander(typer, expandee, mode, pt) { override def onSuccess(expanded: Tree) = { val message = s"expanded into ${expanded.toString}" - typer.typed(q"println($message)") + this.typer.typed(q"println($message)") } } Some(expander(expandee)) diff --git a/test/files/run/reflection-implicit.scala b/test/files/run/reflection-implicit.scala index a6e939322ad5..1e104d1e5809 100644 --- a/test/files/run/reflection-implicit.scala +++ b/test/files/run/reflection-implicit.scala @@ -3,7 +3,7 @@ import scala.language.implicitConversions import scala.reflect.runtime.universe._ class C { - implicit val v = new C + implicit val v: C = new C implicit def d(x: C)(implicit c: C): Int = ??? implicit class X(val x: Int) } diff --git a/test/files/run/reflection-magicsymbols-invoke.check b/test/files/run/reflection-magicsymbols-invoke.check index 6759edfecff3..88a692e26e73 100644 --- a/test/files/run/reflection-magicsymbols-invoke.check +++ b/test/files/run/reflection-magicsymbols-invoke.check @@ -51,6 +51,9 @@ method notify: (): Unit method notifyAll: (): Unit method synchronized: [T0](x$1: T0): T0 method toString: (): String +#partest java20+ +method wait0: (x$1: Long): Unit +#partest method wait: (): Unit method wait: (x$1: Long): Unit method wait: (x$1: Long, x$2: Int): Unit diff --git a/test/files/run/repl-class-based-implicit-import.check b/test/files/run/repl-class-based-implicit-import.check index 9ad75cba7540..85d97a78c1fd 100644 --- a/test/files/run/repl-class-based-implicit-import.check +++ b/test/files/run/repl-class-based-implicit-import.check @@ -2,7 +2,7 @@ scala> def showInt(implicit x: Int) = println(x) def showInt(implicit x: Int): Unit -scala> object IntHolder { implicit val myInt = 5 } +scala> object IntHolder { implicit val myInt: Int = 5 } object IntHolder scala> import IntHolder.myInt diff --git a/test/files/run/repl-class-based-implicit-import.scala b/test/files/run/repl-class-based-implicit-import.scala index 9153c8e08f7f..133c99969f66 100644 --- a/test/files/run/repl-class-based-implicit-import.scala +++ b/test/files/run/repl-class-based-implicit-import.scala @@ -11,7 +11,7 @@ object Test extends ReplTest { def code = """ |def showInt(implicit x: Int) = println(x) - |object IntHolder { implicit val myInt = 5 } + |object IntHolder { implicit val myInt: Int = 5 } |import IntHolder.myInt |showInt |class A; showInt diff --git a/test/files/run/repl-no-imports-no-predef-power.check b/test/files/run/repl-no-imports-no-predef-power.check index 64c4b37b92e2..52140e1b5c2c 100644 --- a/test/files/run/repl-no-imports-no-predef-power.check +++ b/test/files/run/repl-no-imports-no-predef-power.check @@ -11,7 +11,6 @@ warning: 1 deprecation (since 2.11.0); for details, enable `:setting -deprecatio val res0: $r.global.noSelfType.type = private val _ = _ scala> val tp = ArrayClass[scala.util.Random] // magic with tags -warning: 1 feature warning; for details, enable `:setting -feature` or `:replay -feature` val tp: $r.global.Type = Array[scala.util.Random] scala> tp.memberType(Array_apply) // evidence diff --git a/test/files/run/repl-no-imports-no-predef.check b/test/files/run/repl-no-imports-no-predef.check index 380a21a41ff7..4dc6658ecf54 100644 --- a/test/files/run/repl-no-imports-no-predef.check +++ b/test/files/run/repl-no-imports-no-predef.check @@ -21,7 +21,7 @@ val res5: (Int, Int) = (1,2) scala> 1 -> 2 ^ error: value -> is not a member of Int - did you mean >>>? + did you mean >>? scala> diff --git a/test/files/run/repl-power.check b/test/files/run/repl-power.check index 64c4b37b92e2..52140e1b5c2c 100644 --- a/test/files/run/repl-power.check +++ b/test/files/run/repl-power.check @@ -11,7 +11,6 @@ warning: 1 deprecation (since 2.11.0); for details, enable `:setting -deprecatio val res0: $r.global.noSelfType.type = private val _ = _ scala> val tp = ArrayClass[scala.util.Random] // magic with tags -warning: 1 feature warning; for details, enable `:setting -feature` or `:replay -feature` val tp: $r.global.Type = Array[scala.util.Random] scala> tp.memberType(Array_apply) // evidence diff --git a/test/files/run/repl-release.check b/test/files/run/repl-release.check new file mode 100644 index 000000000000..4b245b2dd5da --- /dev/null +++ b/test/files/run/repl-release.check @@ -0,0 +1,42 @@ + +scala> def callerOfCaller = Thread.currentThread.getStackTrace.drop(2).head.getMethodName +def callerOfCaller: String + +scala> @noinline def g = callerOfCaller +def g: String + +scala> @noinline def h = g +def h: String + +scala> assert(h == "g", h) + +scala> @inline def g = callerOfCaller +def g: String + +scala> @noinline def h = g +def h: String + +scala> assert(h == "h", h) + +scala> :quit + +scala> def callerOfCaller = Thread.currentThread.getStackTrace.drop(2).head.getMethodName +def callerOfCaller: String + +scala> @noinline def g = callerOfCaller +def g: String + +scala> @noinline def h = g +def h: String + +scala> assert(h == "g", h) + +scala> @inline def g = callerOfCaller +def g: String + +scala> @noinline def h = g +def h: String + +scala> assert(h == "h", h) + +scala> :quit diff --git a/test/files/run/repl-release.scala b/test/files/run/repl-release.scala new file mode 100644 index 000000000000..82ab1807678e --- /dev/null +++ b/test/files/run/repl-release.scala @@ -0,0 +1,33 @@ +import scala.tools.partest.ReplTest +import scala.tools.nsc._ +import scala.tools.nsc.Settings +import scala.tools.nsc.interpreter.shell.ReplReporterImpl + +// cf run/repl-inline.scala +object Test extends ReplTest { + + var count = 0 + + override def transformSettings(s: Settings) = { + s.processArguments("-release:8" :: "-opt:inline:**" :: "-Wopt" :: Nil, processAll = true) + s.Yreplclassbased.value = count > 0 + count += 1 + s + } + + override def code = + """ +def callerOfCaller = Thread.currentThread.getStackTrace.drop(2).head.getMethodName +@noinline def g = callerOfCaller +@noinline def h = g +assert(h == "g", h) +@inline def g = callerOfCaller +@noinline def h = g +assert(h == "h", h) + """ + + override def show() = { + super.show() + super.show() + } +} diff --git a/test/files/run/repl-replay.check b/test/files/run/repl-replay.check index 15a46c07f82d..e8059184c308 100644 --- a/test/files/run/repl-replay.check +++ b/test/files/run/repl-replay.check @@ -6,6 +6,8 @@ scala> :replay -Xlint replay> locally { val x = 42 ; "$x" } ^ warning: possible missing interpolator: detected interpolated identifier `$x` + ^ + warning: local val x in value res0 is never used val res0: String = $x diff --git a/test/files/run/sammy_after_implicit_view.scala b/test/files/run/sammy_after_implicit_view.scala index 4d98754c48df..4d3d5a37cfd0 100644 --- a/test/files/run/sammy_after_implicit_view.scala +++ b/test/files/run/sammy_after_implicit_view.scala @@ -3,7 +3,7 @@ trait MySam { def apply(x: Int): String } // check that SAM conversion happens after implicit view application object Test extends App { final val AnonFunClass = "$anon$" - final val LMFClass = "$$Lambda$" // LambdaMetaFactory names classes like this + final val LMFClass = "$$Lambda" // LambdaMetaFactory names classes like this // if there's an implicit conversion, it does not takes precedence (because that's what dotty does) def implicitSam() = { diff --git a/test/files/run/sammy_restrictions_LMF.scala b/test/files/run/sammy_restrictions_LMF.scala index aa49e1411339..e8630beea1a6 100644 --- a/test/files/run/sammy_restrictions_LMF.scala +++ b/test/files/run/sammy_restrictions_LMF.scala @@ -15,7 +15,7 @@ trait TClassParent extends B { def apply(x: Int): String } object Test extends App { final val AnonFunClass = "$anonfun$" - final val LMFClass = "$$Lambda$" // LambdaMetaFactory names classes like this + final val LMFClass = "$$Lambda" // LambdaMetaFactory names classes like this private def LMF(f: Any): Unit = { val className = f.getClass.toString diff --git a/test/files/run/sip23-implicit-resolution.scala b/test/files/run/sip23-implicit-resolution.scala index 5d83f89c43b1..beef7ae73e9f 100644 --- a/test/files/run/sip23-implicit-resolution.scala +++ b/test/files/run/sip23-implicit-resolution.scala @@ -4,11 +4,11 @@ object Test extends App { def mkAssoc[K, V0](k: K, v0: V0): Assoc[k.type] { type V = V0 } = new Assoc[k.type] {type V = V0 ; val v = v0} def lookup[K](k: K)(implicit a: Assoc[k.type]): a.V = a.v - implicit def firstAssoc = mkAssoc(1, "Panda!") - implicit def secondAssoc = mkAssoc(2, "Kitty!") + implicit def firstAssoc: Assoc[1] { type V = String } = mkAssoc(1, "Panda!") + implicit def secondAssoc: Assoc[2] { type V = String } = mkAssoc(2, "Kitty!") - implicit def ageAssoc = mkAssoc("Age", 3) - implicit def nmAssoc = mkAssoc("Name", "Jane") + implicit def ageAssoc: Assoc["Age"] { type V = Int } = mkAssoc("Age", 3) + implicit def nmAssoc: Assoc["Name"] { type V = String } = mkAssoc("Name", "Jane") assert(lookup(1) == "Panda!") assert(lookup(2) == "Kitty!") diff --git a/test/files/run/splain-tree.check b/test/files/run/splain-tree.check index 2e3c5b2597db..ce4973924d5d 100644 --- a/test/files/run/splain-tree.check +++ b/test/files/run/splain-tree.check @@ -16,12 +16,10 @@ i1a invalid because !I p: tpes.I8 ――――――――――――――i8 invalid because !I p: tpes.I9 - ――――――――――i6b invalid because !I p: tpes.I8 ――――――――――――i8 invalid because !I p: tpes.I9 - ――――i3b invalid because !I p: tpes.I4 ――――――i4 invalid because @@ -34,7 +32,10 @@ i1a invalid because !I p: tpes.I8 ――――――――――――――i8 invalid because !I p: tpes.I9 - +――――――――――i6b invalid because + !I p: tpes.I8 +――――――――――――i8 invalid because + !I p: tpes.I9 i1b invalid because !I p: tpes.I6 ――i6a invalid because @@ -43,5 +44,51 @@ i1b invalid because !I p: tpes.I8 ――――――i8 invalid because !I p: tpes.I9 +――i6b invalid because + !I p: tpes.I8 +――――i8 invalid because + !I p: tpes.I9 implicitly[I1] ^ +newSource1.scala:28: error: implicit error; +!I e: tpes.I1 +i1a invalid because +!I p: tpes.I2 +⋮ +――i3a invalid because + !I p: tpes.I4 + ⋮ +――――i6a invalid because + !I p: tpes.I7 + ⋮ +――――――――i8 invalid because + !I p: tpes.I9 +――――i6b invalid because + !I p: tpes.I8 +――――――i8 invalid because + !I p: tpes.I9 +――i3b invalid because + !I p: tpes.I4 + ⋮ +――――i6a invalid because + !I p: tpes.I7 + ⋮ +――――――――i8 invalid because + !I p: tpes.I9 +――――i6b invalid because + !I p: tpes.I8 +――――――i8 invalid because + !I p: tpes.I9 +i1b invalid because +!I p: tpes.I6 +――i6a invalid because + !I p: tpes.I7 + ⋮ +――――――i8 invalid because + !I p: tpes.I9 +――i6b invalid because + !I p: tpes.I8 +――――i8 invalid because + !I p: tpes.I9 + implicitly[I1] + ^ \ No newline at end of file diff --git a/test/files/run/splain-tree.scala b/test/files/run/splain-tree.scala index d660ee85d3f2..56f9ff7a3f16 100644 --- a/test/files/run/splain-tree.scala +++ b/test/files/run/splain-tree.scala @@ -1,7 +1,7 @@ import scala.tools.partest._ object Test extends DirectTest { - override def extraSettings: String = "-usejavacp -Vimplicits -Vimplicits-verbose-tree" + override def extraSettings: String = "-usejavacp -Vimplicits" def code: String = "" @@ -39,9 +39,12 @@ object Tree def show(): Unit = { val global = newCompiler() + val globalVerbose = newCompiler("-Vimplicits-verbose-tree") - def run(code: String): Unit = + def run(code: String): Unit = { + compileString(globalVerbose)(code.trim) compileString(global)(code.trim) + } run(verboseTree) } diff --git a/test/files/run/splain.check b/test/files/run/splain.check index b3494546631b..9c41024605b2 100644 --- a/test/files/run/splain.check +++ b/test/files/run/splain.check @@ -2,7 +2,6 @@ newSource1.scala:13: error: implicit error; !I e: ImplicitChain.II ImplicitChain.g invalid because !I impPar3: ImplicitChain.I1 -⋮ ――ImplicitChain.i1 invalid because !I impPar7: ImplicitChain.I3 implicitly[II] @@ -11,12 +10,12 @@ newSource1.scala:6: error: type mismatch; FoundReq.L|FoundReq.R f(new L) ^ -newSource1.scala:5: error: type mismatch; - () => scala.Unit|Runnable - f(3.0, () => println("doesn't work")) - ^ newSource1.scala:7: error: implicit error; !I e: Bounds.F[Bounds.Arg] +Bounds.g invalid because +nonconformant bounds; +[Bounds.Arg, scala.Nothing] +[A <: Bounds.Base, B] implicitly[F[Arg]] ^ newSource1.scala:4: error: implicit error; @@ -120,7 +119,6 @@ Ordering.ordered invalid because !I asComparable: java.lang.Object => java.lang.Comparable[_$2] No implicit view available from Object => Comparable[_ >: Object]. -⋮ Ordering.comparatorToOrdering invalid because !I cmp: java.util.Comparator[java.lang.Object] ms.map(_ => o) diff --git a/test/files/run/splain.scala b/test/files/run/splain.scala index fbbbe2acec7e..c291c3338bbd 100644 --- a/test/files/run/splain.scala +++ b/test/files/run/splain.scala @@ -39,7 +39,7 @@ object FoundReq extends App { def f(x: AnyVal, f: Runnable) = 1 def f(x: Double, f: Runnable) = 2 - f(3.0, () => println("doesn't work")) + f(3.0, () => println("work")) } """ diff --git a/test/files/run/t10751.check b/test/files/run/t10751.check index 0142b6896a14..84258d9452e2 100644 --- a/test/files/run/t10751.check +++ b/test/files/run/t10751.check @@ -35,3 +35,4 @@ } } +warning: 4 deprecations (since 2.13.11); re-run with -deprecation for details diff --git a/test/files/run/t11385.scala b/test/files/run/t11385.scala index a46985706f70..dd2221bdaabd 100644 --- a/test/files/run/t11385.scala +++ b/test/files/run/t11385.scala @@ -2,6 +2,8 @@ import scala.tools.partest.DirectTest import java.nio.file.Files.{createDirectories, createTempDirectory} +import scala.tools.testkit.ReleasablePath._ +import scala.util.Using // an unfortunately-named resource dir on the classpath // @@ -10,9 +12,10 @@ object Test extends DirectTest { def code = "package acme { class C }" def show() = assert { - val tmp = createTempDirectory("t11385") - val pkg = createDirectories(tmp.resolve("acme").resolve("C").resolve("sub")) - compile("-classpath", tmp.toString) + Using.resource(createTempDirectory("t11385")) { tmp => + val pkg = createDirectories(tmp.resolve("acme").resolve("C").resolve("sub")) + compile("-classpath", tmp.toString) + } } } diff --git a/test/files/run/t12348.scala b/test/files/run/t12348.scala index fdbb4d9465df..eddac0626950 100644 --- a/test/files/run/t12348.scala +++ b/test/files/run/t12348.scala @@ -1,9 +1,26 @@ // javaVersion: 11+ +// scalac: -release:11 -object Test { - def main(args: Array[String]): Unit = { +// this a sequel to t7965. that one only tests MethodHandle. JDK 11 added +// signature polymorphic methods to VarHandle, so let's test that too + +// the reason to include `-release:11` is the problem Jason noticed and fixed in +// scala/scala#9930 + +import java.lang.invoke._ +import scala.runtime.IntRef + +object Test extends App { + locally { val a = new Array[Object](1) - val h = java.lang.invoke.MethodHandles.arrayElementVarHandle(a.getClass) + val h = MethodHandles.arrayElementVarHandle(a.getClass) val r = h.setVolatile(a, 0, "foo") // important: no expected type } + locally { + val ref = new IntRef(0) + val varHandle = MethodHandles.lookup().in(classOf[IntRef]).findVarHandle(classOf[IntRef], "elem", classOf[Int]) + assert(0 == (varHandle.getAndSet(ref, 1): Int)) + assert(1 == (varHandle.getAndSet(ref, 2): Int)) + assert(2 == ref.elem) + } } diff --git a/test/files/run/t12390.scala b/test/files/run/t12390.scala new file mode 100644 index 000000000000..edf7c5034d35 --- /dev/null +++ b/test/files/run/t12390.scala @@ -0,0 +1,47 @@ +import scala.tools.nsc.Settings +import scala.tools.nsc.interpreter._, shell._ +import scala.util.Properties.lineSeparator + +import java.io.{PrintWriter, StringWriter} + +object Test { + + def main(args: Array[String]): Unit = { + val code: String = + s"""|import scala.io.Source + |import scala.util.Properties.lineSeparator + |import scala.util.chaining._ + |Source.fromFile(sys.props("partest.test-path")).pipe(s => s.getLines().mkString(lineSeparator).tap(_ => s.close())) + |""".stripMargin.linesIterator.mkString(lineSeparator) + + val s = new Settings() + + s.processArguments( + List( + "-deprecation", + "-Yrepl-class-based", + "-Yrepl-outdir", "target" + ), processAll = true) + + val drain = new StringWriter //new OutputStream { override def write(byte: Int) = () } + val sinkWriter = new PrintWriter(drain) + val reporter = new ReplReporterImpl(ShellConfig(s), s, sinkWriter) + val repl = new IMain(s, reporter) + repl.settings.usejavacp.value = true + for (i <- 1 to 65) { + repl.interpret(code) match { + case Results.Success => + assert(repl.valueOfTerm(repl.mostRecentVar).get != null) // 2.12: null after 60 + case other => + println(drain.toString) + throw new MatchError(other) + } + } + } +} +/* + JavaMirror.scalaSimpleName throws on the long class name and valueOfTerm ignores the error. + + scalaSimpleName expects the wrapper class name to have its enclosing class name as a prefix, + but some special encoding kicks in at 64 chars +*/ diff --git a/test/files/run/t12494.scala b/test/files/run/t12494.scala new file mode 100644 index 000000000000..9994a2744e9c --- /dev/null +++ b/test/files/run/t12494.scala @@ -0,0 +1,24 @@ + +trait Base { + protected[Base] def f: Int +} +object Base { + class Child extends Base { + protected[Base] def f: Int = 42 + def test = f + } +} + +object Test extends App { + assert(new Base.Child().test == 42) +} + +/* +was: +t12494.scala:7: error: weaker access privileges in overriding +protected[trait Base] def f: Int (defined in trait Base) + override should at least be protected[Base] + protected[Base] def f: Int = 42 + ^ +1 error +*/ diff --git a/test/files/run/t12560.check b/test/files/run/t12560.check new file mode 100644 index 000000000000..7ac20ecffd35 --- /dev/null +++ b/test/files/run/t12560.check @@ -0,0 +1,5 @@ +2 +2 +2 +1 +1 diff --git a/test/files/run/t12560.scala b/test/files/run/t12560.scala new file mode 100644 index 000000000000..e4e913fd3eda --- /dev/null +++ b/test/files/run/t12560.scala @@ -0,0 +1,22 @@ +object Test extends App { + + def f(x: AnyVal, f: Runnable) = 1 + def f(x: Double, f: Runnable) = 2 + + val ret1 = f(3.0, () => println("work")) // 2 match Double + println(ret1) + + val ret2 = f(3, () => println("work")) // 2 match Double + println(ret2) + + + val ret3 = f('a', () => println("work")) // 2 match Double, char ==> Int => Double + println(ret3) + + val ret4 = f(false, () => println("work")) // 1 match AnyVal + println(ret4) + + val ret5 = f((), () => println("work")) // 1 match AnyVal + println(ret5) + +} \ No newline at end of file diff --git a/test/files/run/t12680/J_1.java b/test/files/run/t12680/J_1.java new file mode 100644 index 000000000000..4431282356bc --- /dev/null +++ b/test/files/run/t12680/J_1.java @@ -0,0 +1 @@ +public enum J_1 { FOO, BAR } diff --git a/test/files/run/t12680/c_3.scala b/test/files/run/t12680/c_3.scala new file mode 100644 index 000000000000..311da69beba0 --- /dev/null +++ b/test/files/run/t12680/c_3.scala @@ -0,0 +1,2 @@ + +class C diff --git a/test/files/run/t12680/m_2.scala b/test/files/run/t12680/m_2.scala new file mode 100644 index 000000000000..1ed2a5278a0d --- /dev/null +++ b/test/files/run/t12680/m_2.scala @@ -0,0 +1,15 @@ + +import language.experimental.macros +import scala.reflect.macros._ + +object M { + def f(clazz: Class[_], j: J_1): String = macro g + + def g(c: blackbox.Context)(clazz: c.Tree, j: c.Tree): c.Tree = { + import c.universe._ + val classValue = c.eval(c.Expr[Class[_]](c.untypecheck(clazz))) + val jValue = c.eval(c.Expr[J_1](c.untypecheck(j))) + + Literal(Constant(s"$classValue, $jValue")) + } +} diff --git a/test/files/run/t12680/test_4.scala b/test/files/run/t12680/test_4.scala new file mode 100644 index 000000000000..4b1e28164c04 --- /dev/null +++ b/test/files/run/t12680/test_4.scala @@ -0,0 +1,8 @@ + +object Test extends App { + import J_1._ + import M._ + + val res = f(classOf[C], FOO) + assert(res == "class C, FOO") +} diff --git a/test/files/run/t12702.check b/test/files/run/t12702.check new file mode 100644 index 000000000000..fd4efe73272d --- /dev/null +++ b/test/files/run/t12702.check @@ -0,0 +1,2 @@ +warning: 1 feature warning; re-run with -feature for details +IOS diff --git a/test/files/run/t12702.scala b/test/files/run/t12702.scala new file mode 100644 index 000000000000..8b9ba135a3c2 --- /dev/null +++ b/test/files/run/t12702.scala @@ -0,0 +1,17 @@ +object Test { + trait MFSS[X <: MFSS[_]] + trait CS extends MFSS[CS] + trait MFI { type ST } + case class MFSD[S](mFI: MFI {type ST = S}) + case object IOS extends MFI { type ST = CS } + type SD = MFSD[S] forSome { + type S <: MFSS[S] + } + def bad(sd: SD) = sd.mFI match { + case ios: IOS.type => println(ios) + } + def main(args: Array[String]): Unit = { + val x = MFSD(IOS) + bad(x) + } +} diff --git a/test/files/run/t12705.check b/test/files/run/t12705.check new file mode 100644 index 000000000000..3708fadad36c --- /dev/null +++ b/test/files/run/t12705.check @@ -0,0 +1,11 @@ + +scala> case class Person(name: String) +class Person + +scala> $intp.classLoader.getResource("Person.class") +val res0: java.net.URL = memory:(memory)/Person.class + +scala> $intp.classLoader.loadClass("Person") +val res1: Class[_] = class Person + +scala> :quit diff --git a/test/files/run/t12705.scala b/test/files/run/t12705.scala new file mode 100644 index 000000000000..f3532df82ccb --- /dev/null +++ b/test/files/run/t12705.scala @@ -0,0 +1,20 @@ + +import scala.tools.partest.ReplTest + +object Test extends ReplTest { + def code = + """|case class Person(name: String) + |$intp.classLoader.getResource("Person.class") + |$intp.classLoader.loadClass("Person")""".stripMargin + //|classOf[Person].getClassLoader.loadClass("Person")""".stripMargin +} + +/* +java.lang.NoClassDefFoundError: Person (wrong name: Person) + at java.base/java.lang.ClassLoader.defineClass1(Native Method) + at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1013) + at scala.reflect.internal.util.AbstractFileClassLoader.findClass(AbstractFileClassLoader.scala:77) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:588) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521) + ... 76 elided +*/ diff --git a/test/files/run/t1406b.check b/test/files/run/t1406b.check deleted file mode 100644 index 50a0e9217169..000000000000 --- a/test/files/run/t1406b.check +++ /dev/null @@ -1,9 +0,0 @@ -C(84) -C(1764) -C(1764) -C(1806) -C(1806) -C(3528) -C(3528) -C(1806) -C(3528) diff --git a/test/files/run/t1406b.scala b/test/files/run/t1406b.scala index ff16cd296478..9bc32d3246e5 100644 --- a/test/files/run/t1406b.scala +++ b/test/files/run/t1406b.scala @@ -8,16 +8,21 @@ case class C(n: Int) { def *(c: C): C = C(n * c.n) def +(c: C): C = C(n + c.n) } + object Test extends App { + val Sum = 84 + val Product = 1764 + val ProductSum = 1806 + val SumProduct = 3528 val c, d = C(42) - println(c + d) - println(c * d) - println(c ☀ d) - println(c * d + d) - println(c ☀ d + d) - println(c ☀= d + d) // assignment op is low precedence - println(c 𐀀 d + d) // the first one, letter should be low precedence - println(c 🌀d + d) // the second one, cyclone should be high precedence - println(c 🌀= d + d) // the second one, cyclone should be high precedence + def assertEquals(expected: Int, actual: C) = assert(expected == actual.n) + assertEquals(Sum, c + d) + assertEquals(Product, c * d) + assertEquals(Product, c ☀ d) + assertEquals(ProductSum, c * d + d) + assertEquals(ProductSum, c ☀ d + d) + assertEquals(SumProduct, c ☀= d + d) // assignment op is low precedence + assertEquals(SumProduct, c 𐀀 d + d) // the first one, letter should be low precedence + assertEquals(ProductSum, c 🌀d + d) // the second one, cyclone should be high precedence + assertEquals(SumProduct, c 🌀= d + d) // assignment op is low precedence } - diff --git a/test/files/run/t1980.check b/test/files/run/t1980.check index aebd8425db7c..c4e42211b36c 100644 --- a/test/files/run/t1980.check +++ b/test/files/run/t1980.check @@ -1,3 +1,4 @@ +warning: 1 deprecation (since 2.13.11); re-run with -deprecation for details 1. defining foo 1 foo 2 diff --git a/test/files/run/t2316_run.scala b/test/files/run/t2316_run.scala index 19d326f57f1a..2a17886aea46 100644 --- a/test/files/run/t2316_run.scala +++ b/test/files/run/t2316_run.scala @@ -1,7 +1,7 @@ case class T1(source: String) object T1 { - implicit def T1FromT2(implicit t2: T2) = new T1(t2.source) + implicit def T1FromT2(implicit t2: T2): T1 = new T1(t2.source) } case class T2(source: String) @@ -10,7 +10,7 @@ object A { def requireT1(implicit t1: T1) = t1 object B1 { - implicit val t2_b1 = new T2("from B1") + implicit val t2_b1: T2 = new T2("from B1") requireT1 } diff --git a/test/files/run/t2514.scala b/test/files/run/t2514.scala index 0bf716e8bbf6..6a58c8f63c36 100644 --- a/test/files/run/t2514.scala +++ b/test/files/run/t2514.scala @@ -2,9 +2,8 @@ import scala.language.{ implicitConversions, postfixOps, reflectiveCalls } -object Test -{ - implicit def x[A](a: A) = new { def xx = a } +object Test { + implicit def x[A](a: A): AnyRef{def xx: A} = new { def xx = a } def main(args: Array[String]): Unit = { val r1 = 12 xx; diff --git a/test/files/run/t2866.scala b/test/files/run/t2866.scala index c1583e91ace9..7c65e3c406bd 100644 --- a/test/files/run/t2866.scala +++ b/test/files/run/t2866.scala @@ -1,7 +1,7 @@ // for 2.7.x compatibility object A { - implicit val one = 1 + implicit val one: Int = 1 } object Test extends App { diff --git a/test/files/run/t3346d.scala b/test/files/run/t3346d.scala index 118318dee67d..261028dea3a9 100644 --- a/test/files/run/t3346d.scala +++ b/test/files/run/t3346d.scala @@ -11,7 +11,7 @@ object Test extends App { def create(v: A): Basket[A,B] } - implicit val bf = new BasketFactory[Int,TARInt] { + implicit val bf: BasketFactory[Int,TARInt] = new BasketFactory[Int,TARInt] { def create(v: Int): Basket[Int,TARInt] = new Basket[Int, TARInt]{} } diff --git a/test/files/run/t3346e.scala b/test/files/run/t3346e.scala index 8f75eb97edd3..66bba85818f6 100644 --- a/test/files/run/t3346e.scala +++ b/test/files/run/t3346e.scala @@ -47,9 +47,9 @@ class FilterMapFixed[A, Repr <% IterableOps[A, Iterable, _]](a: Repr) { } object MyEnhancements { - implicit def toQS[Coll](a: Coll) = new QuickSort(a) - implicit def toFM[Coll](a: Coll) = new FilterMap(a) - implicit def toFM2[A, Repr <% IterableOps[A, Iterable, _]](a: Repr) = new FilterMapFixed(a) + implicit def toQS[Coll](a: Coll): QuickSort[Coll] = new QuickSort(a) + implicit def toFM[Coll](a: Coll): FilterMap[Coll] = new FilterMap(a) + implicit def toFM2[A, Repr <% IterableOps[A, Iterable, _]](a: Repr): FilterMapFixed[A,Repr] = new FilterMapFixed(a) } object Test extends App { diff --git a/test/files/run/t3346f.scala b/test/files/run/t3346f.scala index 4799ca2ca9a5..b2dc16de5bfd 100644 --- a/test/files/run/t3346f.scala +++ b/test/files/run/t3346f.scala @@ -4,11 +4,11 @@ import scala.language.reflectiveCalls object Test extends App { trait Foo[A] implicit def fooString: Foo[String] = null - implicit def value[A](implicit foo: Foo[A]) = 5 + implicit def value[A](implicit foo: Foo[A]): Int = 5 println(implicitly[Int]) - implicit def conversion[A](x: Int)(implicit foo: Foo[A]) = new { + implicit def conversion[A](x: Int)(implicit foo: Foo[A]): AnyRef{def aMethod: Int} = new { def aMethod = 5 } println(1.aMethod) diff --git a/test/files/run/t3346h.check b/test/files/run/t3346h.check deleted file mode 100644 index 587be6b4c3f9..000000000000 --- a/test/files/run/t3346h.check +++ /dev/null @@ -1 +0,0 @@ -x diff --git a/test/files/run/t3346h.scala b/test/files/run/t3346h.scala index 642b7cf58505..3e94e25df7ed 100644 --- a/test/files/run/t3346h.scala +++ b/test/files/run/t3346h.scala @@ -3,7 +3,7 @@ import scala.language.implicitConversions object Test extends App { trait Fundep[T, U] { def u(t: T): U } class C { def y = "x" } - implicit val FundepStringC = new Fundep[String, C]{ def u(t: String) = new C } + implicit val FundepStringC: Fundep[String,C] = new Fundep[String, C]{ def u(t: String) = new C } implicit def foo[T, U](x: T)(implicit y: Fundep[T, U]): U = y.u(x) - println("x".y) + assert("x".y == "x") } diff --git a/test/files/run/t3346j.scala b/test/files/run/t3346j.scala index 719d11819af4..bfacb6d0e17a 100644 --- a/test/files/run/t3346j.scala +++ b/test/files/run/t3346j.scala @@ -5,7 +5,7 @@ import scala.reflect.runtime.universe._ object Test extends App { class A[T] class B[T] - implicit def foo[T: TypeTag](a: A[T])(implicit b: B[T]) = new { def baz = typeOf[T] } + implicit def foo[T: TypeTag](a: A[T])(implicit b: B[T]): AnyRef{def baz: reflect.runtime.universe.Type} = new { def baz = typeOf[T] } implicit def bar[T <: Int]: B[T] = new B[T]() println(new A[Int]().baz) } diff --git a/test/files/run/t5080.scala b/test/files/run/t5080.scala index acb6167f4652..8d0d04fd60ec 100644 --- a/test/files/run/t5080.scala +++ b/test/files/run/t5080.scala @@ -11,7 +11,7 @@ object Test extends App { override def toString = value.toString; } - implicit def conversions(x: Value) = new { + implicit def conversions(x: Value): AnyRef { def toInt: Int } = new { def toInt = x match { case Num(n) => n diff --git a/test/files/run/t5276_2b.scala b/test/files/run/t5276_2b.scala index 9cc789ec22c8..eb3d744befa4 100644 --- a/test/files/run/t5276_2b.scala +++ b/test/files/run/t5276_2b.scala @@ -4,7 +4,7 @@ import scala.tools.reflect.Eval object Test extends App { reify { class C { - implicit lazy val x = 2 + implicit lazy val x: Int = 2 def y = implicitly[Int] } diff --git a/test/files/run/t5565.scala b/test/files/run/t5565.scala index 9ced87ca21a7..3c9ed24003af 100644 --- a/test/files/run/t5565.scala +++ b/test/files/run/t5565.scala @@ -1,7 +1,9 @@ +import scala.annotation.nowarn import scala.language.reflectiveCalls import scala.language.implicitConversions object Test extends App { + @nowarn // the inferred type includes the default arg, which can't be written explicitly implicit def doubleWithApproxEquals(d: Double) = new { def ~==(v: Double, margin: Double = 0.001): Boolean = math.abs(d - v) < margin @@ -10,3 +12,11 @@ object Test extends App { assert(math.abs(-4.0) ~== (4.0, 0.001)) assert(math.abs(-4.0) ~== 4.0) } +/* +was: +Exception in thread "main" java.lang.IllegalAccessError: tried to access field illegal_access_error_test_case$.reflParams$Cache2 from class illegal_access_error_test_case$delayedInit$body + at illegal_access_error_test_case$delayedInit$body.(illegal_access_error_test_case.scala:8) + at illegal_access_error_test_case$.(illegal_access_error_test_case.scala:1) + at illegal_access_error_test_case$.(illegal_access_error_test_case.scala) + at illegal_access_error_test_case.main(illegal_access_error_test_case.scala) + */ diff --git a/test/files/run/t6028.check b/test/files/run/t6028.check index 771a7cbff201..2707d3507765 100644 --- a/test/files/run/t6028.check +++ b/test/files/run/t6028.check @@ -30,7 +30,7 @@ package { () }; final def apply(): Int = $anonfun$foo$1.this.apply$mcI$sp(); - def apply$mcI$sp(): Int = $anonfun$foo$1.this.$outer.classParam.+($anonfun$foo$1.this.$outer.field()).+($anonfun$foo$1.this.methodParam$1).+($anonfun$foo$1.this.methodLocal$1); + final def apply$mcI$sp(): Int = $anonfun$foo$1.this.$outer.classParam.+($anonfun$foo$1.this.$outer.field()).+($anonfun$foo$1.this.methodParam$1).+($anonfun$foo$1.this.methodLocal$1); private[this] val $outer: T = _; def $outer(): T = $anonfun$foo$1.this.$outer; def apply(): Object = scala.Int.box($anonfun$foo$1.this.apply()); @@ -68,7 +68,7 @@ package { () }; final def apply(): Unit = $anonfun$tryy$1.this.apply$mcV$sp(); - def apply$mcV$sp(): Unit = try { + final def apply$mcV$sp(): Unit = try { $anonfun$tryy$1.this.tryyLocal$1.elem = $anonfun$tryy$1.this.tryyParam$1 } finally (); private[this] val $outer: T = _; diff --git a/test/files/run/t6290.scala b/test/files/run/t6290.scala index 9d05db0d1885..4a5ac1cf4d14 100644 --- a/test/files/run/t6290.scala +++ b/test/files/run/t6290.scala @@ -1,4 +1,5 @@ object Test { - implicit val foo = language.dynamics + import languageFeature._ + implicit val foo: dynamics = language.dynamics def main(args: Array[String]): Unit = () } diff --git a/test/files/run/t6327.scala b/test/files/run/t6327.scala index 4a2f6c3ee88f..7ebba247b871 100644 --- a/test/files/run/t6327.scala +++ b/test/files/run/t6327.scala @@ -7,8 +7,8 @@ object Test extends App { case class R[+T](s: String) { def x() = println(s) } // Implicits in contention; StringR is nested to avoid ambiguity - object R { implicit val StringR = R[String]("A") } - implicit val Default = R[Any]("B") + object R { implicit val StringR: R[String] = R[String]("A") } + implicit val Default: R[Any] = R[Any]("B") class B() extends Dynamic { def selectDynamic[T](f: String)(implicit r: R[T]): Unit = r.x() diff --git a/test/files/run/t6555.check b/test/files/run/t6555.check index 71ed070c83d7..d20c1fe9ca67 100644 --- a/test/files/run/t6555.check +++ b/test/files/run/t6555.check @@ -12,7 +12,7 @@ package { () }; final def apply(param: Int): Int = $anonfun.this.apply$mcII$sp(param); - def apply$mcII$sp(param: Int): Int = param + final def apply$mcII$sp(param: Int): Int = param }; (new <$anon: Int => Int>(): Int => Int) }; diff --git a/test/files/run/t657.scala b/test/files/run/t657.scala index d7f882b4342a..882c9bf3fb71 100644 --- a/test/files/run/t657.scala +++ b/test/files/run/t657.scala @@ -4,7 +4,7 @@ import scala.language.{ implicitConversions } abstract class BaseList { type Node <: BaseNodeImpl - implicit def convertNode(ni : BaseNodeImpl) = ni.asInstanceOf[Node]; + implicit def convertNode(ni : BaseNodeImpl): Node = ni.asInstanceOf[Node]; abstract class BaseNodeImpl } abstract class LinkedList extends BaseList { @@ -33,7 +33,7 @@ trait Matcher extends PrecedenceParser { trait NodeImpl4 extends super.NodeImpl3 type Matchable <: Node with MatchableImpl0 - implicit def convertMatchable(m : MatchableImpl0) = m.asInstanceOf[Matchable] + implicit def convertMatchable(m : MatchableImpl0): Matchable = m.asInstanceOf[Matchable] trait MatchableImpl0 extends NodeImpl4 { override def chop : Node = { Console.println("passed"); super.chop; diff --git a/test/files/run/t6989.check b/test/files/run/t6989.check index a51db3067d30..3537181b2d7f 100644 --- a/test/files/run/t6989.check +++ b/test/files/run/t6989.check @@ -107,7 +107,7 @@ isProtected = false isPublic = true privateWithin = ============ -#partest !java18 +#partest !java18+ sym = value this$0, signature = foo.JavaClass_1, owner = class $PrivateJavaClass isPrivate = false isProtected = false @@ -133,7 +133,7 @@ isProtected = false isPublic = true privateWithin = ============ -#partest !java18 +#partest !java18+ sym = value this$0, signature = foo.JavaClass_1, owner = class $ProtectedJavaClass isPrivate = false isProtected = false @@ -159,7 +159,7 @@ isProtected = false isPublic = true privateWithin = ============ -#partest !java18 +#partest !java18+ sym = value this$0, signature = foo.JavaClass_1, owner = class $PublicJavaClass isPrivate = false isProtected = false diff --git a/test/files/run/t7965.scala b/test/files/run/t7965.scala index fc031930510e..571f963d5447 100644 --- a/test/files/run/t7965.scala +++ b/test/files/run/t7965.scala @@ -1,52 +1,35 @@ // Test that scala doesn't apply boxing or varargs conversions to the // @PolymorphicSignature magical methods, MethodHandle#{invoke, invokeExact} -object Test { - val code = """ + +import java.lang.invoke._ object O { private def foo = "foo" private def bar(x: Int): Int = -x private def baz(x: Box): Unit = x.a = "present" - val lookup = java.lang.invoke.MethodHandles.lookup + val lookup = MethodHandles.lookup } -import java.lang.invoke._ class Box(var a: Any) -object Test { - def main(args: Array[String]): Unit = { - def lookup(name: String, params: Array[Class[_]], ret: Class[_]) = { - val mt = MethodType.methodType(ret, params) - O.lookup.findVirtual(O.getClass, name, mt) - } - val fooResult = (lookup("foo", Array(), classOf[String]).invokeExact(O): Int) - assert(fooResult == "foo") - - val barResult = (lookup("bar", Array(classOf[Int]), classOf[Int]).invokeExact(O, 42): Int) - assert(barResult == -42) - - val box = new Box(null) - (lookup("baz", Array(classOf[Box]), Void.TYPE).invokeExact(O, box) : Unit) - assert(box.a == "present") - - // Note: Application in statement position in a block in Java also infers return type of Unit, - // but we don't support that, ascribe the type to Unit as above. - // as done in Java. - // lookup("baz", Array(classOf[Box]), Void.TYPE).invokeExact(O, box) - () +object Test extends App { + def lookup(name: String, params: Array[Class[_]], ret: Class[_]) = { + val mt = MethodType.methodType(ret, params) + O.lookup.findVirtual(O.getClass, name, mt) } -} + val fooResult = (lookup("foo", Array(), classOf[String]).invokeExact(O): String) + assert(fooResult == "foo") -""" - def main(args: Array[String]): Unit = test() + val barResult = (lookup("bar", Array(classOf[Int]), classOf[Int]).invokeExact(O, 42): Int) + assert(barResult == -42) - def test(): Unit = { - import scala.reflect.runtime._ - import scala.tools.reflect.ToolBox + val box = new Box(null) + (lookup("baz", Array(classOf[Box]), Void.TYPE).invokeExact(O, box) : Unit) + assert(box.a == "present") + + // Note: Application in statement position in a block in Java also infers return type of Unit, + // but we don't support that, ascribe the type to Unit as above. + // as done in Java. + // lookup("baz", Array(classOf[Box]), Void.TYPE).invokeExact(O, box) - val m = currentMirror - val tb = m.mkToolBox() - import tb._ - eval(parse(code)) - } } diff --git a/test/files/run/t8564.scala b/test/files/run/t8564.scala index 6d7d1178c4a0..6ffaa75938e2 100644 --- a/test/files/run/t8564.scala +++ b/test/files/run/t8564.scala @@ -13,8 +13,8 @@ object Test extends App { def lookup(k: String)(implicit assoc: Assoc[k.type]): assoc.V = assoc.value - implicit def nameAssoc = mkAssoc("Name", "Mary") - implicit def ageAssoc = mkAssoc("Age", 23) + implicit def nameAssoc: Assoc["Name"] { type V = String } = mkAssoc("Name", "Mary") + implicit def ageAssoc: Assoc["Age"] { type V = Int } = mkAssoc("Age", 23) assert((lookup("Name"): String) == "Mary") assert((lookup("Age"): Int) == 23) diff --git a/test/files/run/t9529.check b/test/files/run/t9529.check index 38ad198f56ba..666a758485b0 100644 --- a/test/files/run/t9529.check +++ b/test/files/run/t9529.check @@ -32,7 +32,7 @@ u: List(@anns.Ann_0$Container(value={@anns.Ann_0(name="u", value="you"), @anns.A List(@anns.Ann_0$Container(value={@anns.Ann_0(name="", value="constructor"), @anns.Ann_0(name="", value="initializer")})) -#partest java15+ +#partest java17 A: List() B: List(@java.lang.Deprecated(forRemoval=false, since="")) C: List(@anns.Ann_0(name="C", value="see")) @@ -49,3 +49,20 @@ u: List(@anns.Ann_0$Container({@anns.Ann_0(name="u", value="you"), @anns.Ann_0(n List(@anns.Ann_0$Container({@anns.Ann_0(name="", value="constructor"), @anns.Ann_0(name="", value="initializer")})) +#partest java20+ +A: List() +B: List(@java.lang.Deprecated(forRemoval=false, since="")) +C: List(@anns.Ann_0(name="C", value="see")) +D: List(@anns.Ann_0.Container({@anns.Ann_0(name="D", value="dee"), @anns.Ann_0(name="D", value="dye")})) + +x: List(@anns.Ann_0(name="x", value="eks")) +y: List(@anns.Ann_0.Container({@anns.Ann_0(name="y", value="why"), @anns.Ann_0(name="y", value="wye")})) + +t: List(@anns.Ann_0(name="t", value="tee")) +u: List(@anns.Ann_0.Container({@anns.Ann_0(name="u", value="you"), @anns.Ann_0(name="u", value="yew")})) + +1: List(@anns.Ann_0(name="1", value="one")) +2: List(@anns.Ann_0.Container({@anns.Ann_0(name="2", value="two"), @anns.Ann_0(name="2", value="tew")})) + +List(@anns.Ann_0.Container({@anns.Ann_0(name="", value="constructor"), @anns.Ann_0(name="", value="initializer")})) + diff --git a/test/files/run/tcpoly_parseridioms.scala b/test/files/run/tcpoly_parseridioms.scala index 38455f05eebb..6144fb30dba0 100644 --- a/test/files/run/tcpoly_parseridioms.scala +++ b/test/files/run/tcpoly_parseridioms.scala @@ -103,8 +103,8 @@ trait ParserIdioms extends Parsers with Idioms { // TODO: how can parserIdiom(curry2(_)) be omitted? def expr: Parser[Expr] = parserIdiomFun(curry2(Plus)) <| num <> num |> - implicit def curry2[s, t, u](fun: (s, t) => u)(a: s)(b: t) = fun(a, b) - implicit def curry3[r, s, t, u](fun: (r, s, t) => u)(a: r)(b: s)(c: t) = fun(a, b, c) + implicit def curry2[s, t, u](fun: (s, t) => u)(a: s)(b: t): u = fun(a, b) + implicit def curry3[r, s, t, u](fun: (r, s, t) => u)(a: r)(b: s)(c: t): u = fun(a, b, c) } object Test extends ParserIdioms with App { diff --git a/test/files/run/toolbox_typecheck_implicitsdisabled.check b/test/files/run/toolbox_typecheck_implicitsdisabled.check index 6aeebb1b2bd7..35d0300e2f71 100644 --- a/test/files/run/toolbox_typecheck_implicitsdisabled.check +++ b/test/files/run/toolbox_typecheck_implicitsdisabled.check @@ -3,4 +3,4 @@ scala.Predef.ArrowAssoc[Int](1).->[Int](2) } scala.tools.reflect.ToolBoxError: reflective typecheck has failed: value -> is not a member of Int -did you mean >>>? +did you mean >>? diff --git a/test/files/specialized/t11489.scala b/test/files/specialized/t11489.scala new file mode 100644 index 000000000000..5ccd04ddb2a4 --- /dev/null +++ b/test/files/specialized/t11489.scala @@ -0,0 +1,8 @@ +class Parent { + def twice[@scala.specialized(Int) I <: Int : ValueOf]: Int = valueOf[I] * 2 +} + +object Test extends App { + val actualMethods = (new Parent).getClass.getDeclaredMethods + assert(actualMethods.count(_.getName == "twice$mIc$sp") == 1) +} \ No newline at end of file diff --git a/test/junit/scala/ArrayTest.scala b/test/junit/scala/ArrayTest.scala index ef0ff59f1bb5..dc80d0e4e1cf 100644 --- a/test/junit/scala/ArrayTest.scala +++ b/test/junit/scala/ArrayTest.scala @@ -4,11 +4,13 @@ import org.junit.Assert.{ assertArrayEquals, assertFalse, assertTrue } import org.junit.Test import scala.runtime.BoxedUnit +import scala.util.chaining._ class ArrayTest { @Test def testArrayCopyOfUnit(): Unit = { - val expected = new Array[BoxedUnit](32).asInstanceOf[Array[AnyRef]]; java.util.Arrays.fill(expected, ().asInstanceOf[AnyRef]) + val expected = new Array[BoxedUnit](32).asInstanceOf[Array[AnyRef]] + .tap(array => java.util.Arrays.fill(array, (): Any)) assertArrayEquals(expected, Array.copyOf(Array[Unit](), 32).asInstanceOf[Array[AnyRef]]) assertArrayEquals(expected, Array.copyAs[Unit](Array[Nothing](), 32).asInstanceOf[Array[AnyRef]]) assertArrayEquals(expected, Array.copyAs[Unit](Array[Unit](), 32).asInstanceOf[Array[AnyRef]]) diff --git a/test/junit/scala/collection/BuildFromTest.scala b/test/junit/scala/collection/BuildFromTest.scala index fedbe1b9042c..12fe6d9595b2 100644 --- a/test/junit/scala/collection/BuildFromTest.scala +++ b/test/junit/scala/collection/BuildFromTest.scala @@ -176,4 +176,41 @@ class BuildFromTest { immutable.LongMap: BuildFrom[_, (Long, String), immutable.LongMap[String]] mutable.LongMap: BuildFrom[_, (Long, String), mutable.LongMap[String]] mutable.AnyRefMap: BuildFrom[_, (String, String), mutable.AnyRefMap[String, String]] + + // Check that we don't get an implicit divergence in a futile part of the search tree: + { + sealed trait GPoint + sealed trait HNil extends GPoint + class HCons[H, +T <: GPoint] extends GPoint + abstract class ExtendsOrdered extends Ordered[ExtendsOrdered] + + + // In scala 2.13, this implicit search considers BuildFrom.buildFromSortedSetOps + // which looks for a dep. implicit of type Ordering[(Int, HCons[ExtendsOrdered, HNil])] + implicitly[collection.BuildFrom[Seq[Any], (Int, HCons[ExtendsOrdered, HNil]), Seq[(Int, HCons[ExtendsOrdered, HNil])]]] + + // + // In Scala 2.12, buildFromSortedSetOps is not a candidate because if it is in the companion object of + // the SortedSet heirarchy, which is not part of the implicit scope for this search. + // In 2.13, the implicit was moved to `object BuildFrom`, so _is_ considered + // + // The dependent implicit search: + // + // implicitly[(Int, HCons[ExtendsOrdered, HNil])] + // + // ... diverges on both Scala 2.12 and 2.13 + // + // error: diverging implicit expansion for type scala.math.Ordering.AsComparable[(Int, HCons[ExtendsOrdered,HNil])] + // starting with method orderingToOrdered in object Ordered + // + // Divergences in Ordering implicits are a long standing problem, but I always thought it too hard to + // fix while retaining source compatibility. + // + // Removing `extends Ordered[X]` avoids divergence, but I'm not sure why. I diffed the -Vtyper log but + // can't figure out why that is relevant. + // + // (In the original code, ExtendsOrdered was actually scala.Enumeration.Value, which does extends Ordered. + // + // + } } diff --git a/test/junit/scala/collection/IterableTest.scala b/test/junit/scala/collection/IterableTest.scala index 3a3495d2602b..424c5cc9dfad 100644 --- a/test/junit/scala/collection/IterableTest.scala +++ b/test/junit/scala/collection/IterableTest.scala @@ -1,7 +1,7 @@ package scala.collection import org.junit.{Assert, Test} -import Assert.{assertEquals, assertTrue} +import Assert.{assertEquals, assertFalse, assertTrue} import scala.annotation.nowarn import scala.collection.immutable.{ArraySeq, List, Range, Vector} @@ -275,8 +275,8 @@ class IterableTest { case i: Int => Left(i) case s: String => Right(s) } - assertEquals(left, Seq(1, 2, 3, 4 ,5)) - assertEquals(right, Seq("1", "2", "3", "4" ,"5")) + assertEquals(Seq(1, 2, 3, 4 ,5), left) + assertEquals(Seq("1", "2", "3", "4" ,"5"), right) } @deprecated("Uses deprecated hasDefiniteSize, extends HashMap", since="2.13.0") @@ -284,81 +284,206 @@ class IterableTest { def hasDefiniteSize(): Unit = { import scala.{collection => c} import scala.collection.{mutable => m, immutable => i} - assertEquals(true, Some(1).hasDefiniteSize) - assertEquals(true, None.hasDefiniteSize) - assertEquals(true, Option(1).hasDefiniteSize) - assertEquals(true, Array(1).hasDefiniteSize) - assertEquals(true, "a".hasDefiniteSize) - assertEquals(true, c.BitSet(1).hasDefiniteSize) - assertEquals(false, scala.io.Source.fromString("a").buffered.hasDefiniteSize) - assertEquals(true, c.IndexedSeq(1).hasDefiniteSize) - assertEquals(true, c.IndexedSeq(1).view.hasDefiniteSize) - assertEquals(true, c.Iterable(1).hasDefiniteSize) - assertEquals(false, c.Iterator(1).hasDefiniteSize) - assertEquals(true, c.LinearSeq(1).hasDefiniteSize) - assertEquals(true, c.Map(1 -> 1).hasDefiniteSize) - assertEquals(true, c.Map(1 -> 1).view.hasDefiniteSize) - assertEquals(true, c.Seq(1).hasDefiniteSize) - assertEquals(true, c.Seq(1).view.hasDefiniteSize) - assertEquals(true, c.Set(1).hasDefiniteSize) - assertEquals(true, c.SortedMap(1 -> 1).hasDefiniteSize) - assertEquals(true, c.SortedSet(1).hasDefiniteSize) - assertEquals(true, i.BitSet(1).hasDefiniteSize) - assertEquals(true, i.HashMap(1 -> 1).hasDefiniteSize) - assertEquals(true, i.HashSet(1).hasDefiniteSize) - assertEquals(true, i.IndexedSeq(1).hasDefiniteSize) - assertEquals(true, i.IntMap(1 -> 1).hasDefiniteSize) - assertEquals(true, i.Iterable(1).hasDefiniteSize) - assertEquals(true, i.LinearSeq(1).hasDefiniteSize) - assertEquals(true, i.List(1).hasDefiniteSize) - assertEquals(true, i.ListMap(1 -> 1).hasDefiniteSize) - assertEquals(true, i.ListSet(1).hasDefiniteSize) - assertEquals(true, i.LongMap(1L -> 1).hasDefiniteSize) - assertEquals(true, i.Map(1 -> 1).hasDefiniteSize) - assertEquals(true, i.Nil.hasDefiniteSize) - assertEquals(true, (1L to 1L).hasDefiniteSize) - assertEquals(true, i.Queue(1).hasDefiniteSize) - assertEquals(true, (1 to 1).hasDefiniteSize) - assertEquals(true, i.Seq(1).hasDefiniteSize) - assertEquals(true, i.Set(1).hasDefiniteSize) - assertEquals(true, i.SortedMap(1 -> 1).hasDefiniteSize) - assertEquals(true, i.SortedSet(1).hasDefiniteSize) - assertEquals(false, i.Stream(1).hasDefiniteSize) - assertEquals(true, i.TreeMap(1 -> 1).hasDefiniteSize) - assertEquals(true, i.TreeSet(1).hasDefiniteSize) - assertEquals(true, i.Vector(1).hasDefiniteSize) - assertEquals(false, i.Vector(1).iterator.hasDefiniteSize) - assertEquals(true, m.AnyRefMap(Nil -> 1).hasDefiniteSize) - assertEquals(true, m.ArrayBuffer(1).hasDefiniteSize) - assertEquals(true, m.ArrayBuffer(1).view.hasDefiniteSize) - assertEquals(true, m.BitSet(1).hasDefiniteSize) - assertEquals(true, m.Buffer(1).hasDefiniteSize) - assertEquals(true, m.HashMap(1 -> 1).hasDefiniteSize) - assertEquals(true, m.HashSet(1).hasDefiniteSize) - assertEquals(true, m.IndexedSeq(1).hasDefiniteSize) - assertEquals(true, m.Iterable(1).hasDefiniteSize) - assertEquals(true, m.LinkedHashMap(1 -> 1).hasDefiniteSize) - assertEquals(true, m.LinkedHashSet(1).hasDefiniteSize) - assertEquals(true, m.ListBuffer(1).hasDefiniteSize) - assertEquals(true, m.ListMap(1 -> 1).hasDefiniteSize) - assertEquals(true, m.LongMap(1L -> 1).hasDefiniteSize) - assertEquals(true, m.Map(1 -> 1).hasDefiniteSize) + assertTrue(Some(1).hasDefiniteSize) + assertTrue(None.hasDefiniteSize) + assertTrue(Option(1).hasDefiniteSize) + assertTrue(Array(1).hasDefiniteSize) + assertTrue("a".hasDefiniteSize) + assertTrue(c.BitSet(1).hasDefiniteSize) + assertFalse(scala.io.Source.fromString("a").buffered.hasDefiniteSize) + assertTrue(c.IndexedSeq(1).hasDefiniteSize) + assertTrue(c.IndexedSeq(1).view.hasDefiniteSize) + assertTrue(c.Iterable(1).hasDefiniteSize) + assertFalse(c.Iterator(1).hasDefiniteSize) + assertTrue(c.LinearSeq(1).hasDefiniteSize) + assertTrue(c.Map(1 -> 1).hasDefiniteSize) + assertTrue(c.Map(1 -> 1).view.hasDefiniteSize) + assertTrue(c.Seq(1).hasDefiniteSize) + assertTrue(c.Seq(1).view.hasDefiniteSize) + assertTrue(c.Set(1).hasDefiniteSize) + assertTrue(c.SortedMap(1 -> 1).hasDefiniteSize) + assertTrue(c.SortedSet(1).hasDefiniteSize) + assertTrue(i.BitSet(1).hasDefiniteSize) + assertTrue(i.HashMap(1 -> 1).hasDefiniteSize) + assertTrue(i.HashSet(1).hasDefiniteSize) + assertTrue(i.IndexedSeq(1).hasDefiniteSize) + assertTrue(i.IntMap(1 -> 1).hasDefiniteSize) + assertTrue(i.Iterable(1).hasDefiniteSize) + assertTrue(i.LinearSeq(1).hasDefiniteSize) + assertTrue(i.List(1).hasDefiniteSize) + assertTrue(i.ListMap(1 -> 1).hasDefiniteSize) + assertTrue(i.ListSet(1).hasDefiniteSize) + assertTrue(i.LongMap(1L -> 1).hasDefiniteSize) + assertTrue(i.Map(1 -> 1).hasDefiniteSize) + assertTrue(i.Nil.hasDefiniteSize) + assertTrue((1L to 1L).hasDefiniteSize) + assertTrue(i.Queue(1).hasDefiniteSize) + assertTrue((1 to 1).hasDefiniteSize) + assertTrue(i.Seq(1).hasDefiniteSize) + assertTrue(i.Set(1).hasDefiniteSize) + assertTrue(i.SortedMap(1 -> 1).hasDefiniteSize) + assertTrue(i.SortedSet(1).hasDefiniteSize) + assertFalse(i.Stream(1).hasDefiniteSize) + assertTrue(i.TreeMap(1 -> 1).hasDefiniteSize) + assertTrue(i.TreeSet(1).hasDefiniteSize) + assertTrue(i.Vector(1).hasDefiniteSize) + assertFalse(i.Vector(1).iterator.hasDefiniteSize) + assertTrue(m.AnyRefMap(Nil -> 1).hasDefiniteSize) + assertTrue(m.ArrayBuffer(1).hasDefiniteSize) + assertTrue(m.ArrayBuffer(1).view.hasDefiniteSize) + assertTrue(m.BitSet(1).hasDefiniteSize) + assertTrue(m.Buffer(1).hasDefiniteSize) + assertTrue(m.HashMap(1 -> 1).hasDefiniteSize) + assertTrue(m.HashSet(1).hasDefiniteSize) + assertTrue(m.IndexedSeq(1).hasDefiniteSize) + assertTrue(m.Iterable(1).hasDefiniteSize) + assertTrue(m.LinkedHashMap(1 -> 1).hasDefiniteSize) + assertTrue(m.LinkedHashSet(1).hasDefiniteSize) + assertTrue(m.ListBuffer(1).hasDefiniteSize) + assertTrue(m.ListMap(1 -> 1).hasDefiniteSize) + assertTrue(m.LongMap(1L -> 1).hasDefiniteSize) + assertTrue(m.Map(1 -> 1).hasDefiniteSize) assertTrue((new m.HashMap[Int, m.Set[Int]] with m.MultiMap[Int, Int]).hasDefiniteSize) // deprecated extension - assertEquals(true, m.OpenHashMap(1 -> 1).hasDefiniteSize) - assertEquals(true, m.PriorityQueue(1).hasDefiniteSize) - assertEquals(true, m.Queue(1).hasDefiniteSize) - assertEquals(true, m.Seq(1).hasDefiniteSize) - assertEquals(true, m.Set(1).hasDefiniteSize) - assertEquals(true, m.SortedMap(1 -> 1).hasDefiniteSize) - assertEquals(true, m.SortedSet(1).hasDefiniteSize) - assertEquals(true, m.Stack(1).hasDefiniteSize) - assertEquals(true, (new m.StringBuilder()).hasDefiniteSize) - assertEquals(true, m.TreeMap(1 -> 1).hasDefiniteSize) - assertEquals(true, m.TreeSet(1).hasDefiniteSize) - assertEquals(true, m.UnrolledBuffer(1).hasDefiniteSize) - assertEquals(true, m.WeakHashMap(1 -> 1).hasDefiniteSize) - assertEquals(false, scala.io.Source.fromString("hello").hasDefiniteSize) - assertEquals(true, (List(1), List(2)).zipped.hasDefiniteSize) - assertEquals(true, (List(1), List(2), List(3)).zipped.hasDefiniteSize) + assertTrue(m.OpenHashMap(1 -> 1).hasDefiniteSize) + assertTrue(m.PriorityQueue(1).hasDefiniteSize) + assertTrue(m.Queue(1).hasDefiniteSize) + assertTrue(m.Seq(1).hasDefiniteSize) + assertTrue(m.Set(1).hasDefiniteSize) + assertTrue(m.SortedMap(1 -> 1).hasDefiniteSize) + assertTrue(m.SortedSet(1).hasDefiniteSize) + assertTrue(m.Stack(1).hasDefiniteSize) + assertTrue((new m.StringBuilder()).hasDefiniteSize) + assertTrue(m.TreeMap(1 -> 1).hasDefiniteSize) + assertTrue(m.TreeSet(1).hasDefiniteSize) + assertTrue(m.UnrolledBuffer(1).hasDefiniteSize) + assertTrue(m.WeakHashMap(1 -> 1).hasDefiniteSize) + assertFalse(scala.io.Source.fromString("hello").hasDefiniteSize) + assertTrue((List(1), List(2)).zipped.hasDefiniteSize) + assertTrue((List(1), List(2), List(3)).zipped.hasDefiniteSize) + } + + class SingleUseIterable[A] private (xs: A*) extends IterableOnce[A] with IterableOnceOps[A, SingleUseIterable, SingleUseIterable[A]] with UseIterableOps[A, SingleUseIterable, SingleUseIterable[A]] { + private var iterated = false + override def iterator = { + assertFalse("Attempted to re-iterate!", iterated) + iterated = true + Iterator(xs: _*) + } + } + object SingleUseIterable { + def apply[A](xs: A*): SingleUseIterable[A] = new SingleUseIterable(xs: _*) + } + trait UseIterableOps[A, CC[_], C] { + def collect[B](pf: PartialFunction[A,B]): CC[B] = ??? + def drop(n: Int): C = ??? + def dropWhile(p: A => Boolean): C = ??? + def filter(p: A => Boolean): C = ??? + def filterNot(pred: A => Boolean): C = ??? + def flatMap[B](f: A => scala.collection.IterableOnce[B]): CC[B] = ??? + def flatten[B](implicit asIterable: A => scala.collection.IterableOnce[B]): CC[B] = ??? + def map[B](f: A => B): CC[B] = ??? + def scanLeft[B](z: B)(op: (B, A) => B): CC[B] = ??? + def slice(from: Int, until: Int): C = ??? + def span(p: A => Boolean): (C, C) = ??? + def take(n: Int): C = ??? + def takeWhile(p: A => Boolean): C = ??? + def tapEach[U](f: A => U): C = ??? + def zipWithIndex: CC[(A, Int)] = ??? + } + class ZeroUseIterable[A] private () extends IterableOnce[A] with IterableOnceOps[A, ZeroUseIterable, ZeroUseIterable[A]] with UseIterableOps[A, ZeroUseIterable, ZeroUseIterable[A]] { + override def iterator = fail("Attempted to iterate!") + override def knownSize = 0 + } + object ZeroUseIterable { + def apply[A](): ZeroUseIterable[A] = new ZeroUseIterable() + } + + // testing unknown size where iterator isEmpty/nonEmpty and iterator is traversed only once; + // testing knownSize == 0 and iterator is not queried. + + @Test def `IterableOnceOps.sum consumes one iterator`: Unit = assertEquals(10, SingleUseIterable(1, 2, 3, 4).sum) + @Test def `IterableOnceOps.sum of empty iterator`: Unit = assertEquals(0, SingleUseIterable[Int]().sum) + @Test def `IterableOnceOps.sum consumes no iterator`: Unit = assertEquals(0, ZeroUseIterable[Int]().sum) + @Test def `IterableOnceOps.sum of one iterator`: Unit = assertEquals(42, SingleUseIterable[Int](42).sum) + + @Test def `IterableOnceOps.product consumes one iterator`: Unit = assertEquals(24, SingleUseIterable(1, 2, 3, 4).product) + @Test def `IterableOnceOps.product of empty iterator`: Unit = assertEquals(1, SingleUseIterable[Int]().product) + @Test def `IterableOnceOps.product consumes no iterator`: Unit = assertEquals(1, ZeroUseIterable[Int]().product) + @Test def `IterableOnceOps.product of one iterator`: Unit = assertEquals(42, SingleUseIterable[Int](42).product) + + @Test def `IterableOnceOps.min consumes one iterator`: Unit = assertEquals(27, SingleUseIterable(42, 27, 37).min) + @Test def `IterableOnceOps.min of empty iterator`: Unit = + assertThrows[UnsupportedOperationException](SingleUseIterable[Int]().min, _.contains("min")) + @Test def `IterableOnceOps.min consumes no iterator`: Unit = + assertThrows[UnsupportedOperationException](ZeroUseIterable[Int]().min, _.contains("min")) + + @Test def `IterableOnceOps.minBy consumes one iterator`: Unit = assertEquals(27, SingleUseIterable(42, 27, 37).minBy(_ * 2)) + @Test def `IterableOnceOps.minBy of empty iterator`: Unit = + assertThrows[UnsupportedOperationException](SingleUseIterable[Int]().minBy(_ * 2), _.contains("minBy")) + @Test def `IterableOnceOps.minBy consumes no iterator`: Unit = + assertThrows[UnsupportedOperationException](ZeroUseIterable[Int]().minBy(_ * 2), _.contains("minBy")) + + @Test def `IterableOnceOps.minOption consumes one iterator`: Unit = assertEquals(Some(27), SingleUseIterable(42, 27, 37).minOption) + @Test def `IterableOnceOps.minOption of empty iterator`: Unit = assertEquals(None, SingleUseIterable[Int]().minOption) + @Test def `IterableOnceOps.minOption consumes no iterator`: Unit = assertEquals(None, ZeroUseIterable[Int]().minOption) + + @Test def `IterableOnceOps.minByOption consumes one iterator`: Unit = assertEquals(Some(27), SingleUseIterable(42, 27, 37).minByOption(_ * 2)) + @Test def `IterableOnceOps.minByOption of empty iterator`: Unit = assertEquals(None, SingleUseIterable[Int]().minByOption(_ * 2)) + @Test def `IterableOnceOps.minByOption consumes no iterator`: Unit = assertEquals(None, ZeroUseIterable[Int]().minByOption(_ * 2)) + + @Test def `IterableOnceOps.max consumes one iterator`: Unit = assertEquals(42, SingleUseIterable(42, 27, 37).max) + @Test def `IterableOnceOps.max of empty iterator`: Unit = + assertThrows[UnsupportedOperationException](SingleUseIterable[Int]().max, _.contains("max")) + @Test def `IterableOnceOps.max consumes no iterator`: Unit = + assertThrows[UnsupportedOperationException](ZeroUseIterable[Int]().max, _.contains("max")) + + @Test def `IterableOnceOps.maxBy consumes one iterator`: Unit = assertEquals(42, SingleUseIterable(42, 27, 37).maxBy(_ * 2)) + @Test def `IterableOnceOps.maxBy of empty iterator`: Unit = + assertThrows[UnsupportedOperationException](SingleUseIterable[Int]().maxBy(_ * 2), _.contains("maxBy")) + @Test def `IterableOnceOps.maxBy consumes no iterator`: Unit = + assertThrows[UnsupportedOperationException](ZeroUseIterable[Int]().maxBy(_ * 2), _.contains("maxBy")) + + @Test def `IterableOnceOps.maxOption consumes one iterator`: Unit = assertEquals(Some(42), SingleUseIterable(42, 27, 37).maxOption) + @Test def `IterableOnceOps.maxOption of empty iterator`: Unit = assertEquals(None, SingleUseIterable[Int]().maxOption) + @Test def `IterableOnceOps.maxOption consumes no iterator`: Unit = assertEquals(None, ZeroUseIterable[Int]().maxOption) + + @Test def `IterableOnceOps.maxByOption consumes one iterator`: Unit = assertEquals(Some(42), SingleUseIterable(42, 27, 37).maxByOption(_ * 2)) + @Test def `IterableOnceOps.maxByOption of empty iterator`: Unit = assertEquals(None, SingleUseIterable[Int]().maxByOption(_ * 2)) + @Test def `IterableOnceOps.maxByOption consumes no iterator`: Unit = assertEquals(None, ZeroUseIterable[Int]().maxByOption(_ * 2)) + + @Test def `IterableOnceOps.reduceLeft consumes one iterator`: Unit = assertEquals(106, SingleUseIterable(42, 27, 37).reduceLeft(_ + _)) + @Test def `IterableOnceOps.reduceLeft of empty iterator`: Unit = + assertThrows[UnsupportedOperationException](SingleUseIterable[Int]().reduceLeft(_ + _), _.contains("reduceLeft")) + @Test def `IterableOnceOps.reduceLeft consumes no iterator`: Unit = + assertThrows[UnsupportedOperationException](ZeroUseIterable[Int]().reduceLeft(_ + _), _.contains("reduceLeft")) + + @Test def `IterableOnceOps.reduceLeftOption consumes one iterator`: Unit = assertEquals(Some(10), SingleUseIterable(1, 2, 3, 4).reduceLeftOption(_ + _)) + @Test def `IterableOnceOps.reduceLeftOption of empty iterator`: Unit = assertEquals(None, SingleUseIterable[Int]().reduceLeftOption(_ + _)) + @Test def `IterableOnceOps.reduceLeftOption consumes no iterator`: Unit = assertEquals(None, ZeroUseIterable[Int]().reduceLeftOption(_ + _)) + + @Test def `IterableOnceOps.reduceRight consumes one iterator`: Unit = assertEquals(106, SingleUseIterable(42, 27, 37).reduceRight(_ + _)) + @Test def `IterableOnceOps.reduceRight of empty iterator`: Unit = + assertThrows[UnsupportedOperationException](SingleUseIterable[Int]().reduceRight(_ + _), _.contains("reduceLeft")) + @Test def `IterableOnceOps.reduceRight consumes no iterator`: Unit = + assertThrows[UnsupportedOperationException](ZeroUseIterable[Int]().reduceRight(_ + _), _.contains("reduceRight")) + + @Test def `IterableOnceOps.reduceRightOption consumes one iterator`: Unit = assertEquals(Some(10), SingleUseIterable(1, 2, 3, 4).reduceRightOption(_ + _)) + @Test def `IterableOnceOps.reduceRightOption of empty iterator`: Unit = assertEquals(None, SingleUseIterable[Int]().reduceRightOption(_ + _)) + @Test def `IterableOnceOps.reduceRightOption consumes no iterator`: Unit = assertEquals(None, ZeroUseIterable[Int]().reduceRightOption(_ + _)) + + @Test def `IterableOnceOps.isEmpty consumes no iterator`: Unit = assertTrue(ZeroUseIterable[Int]().isEmpty) + + @Test def `sum uses my reduce if knownSize > 0`: Unit = { + val reductive = new AbstractIterable[Int] { + val values = List(1, 2, 3, 4, 32) + override def iterator = Iterator.empty + override def reduce[B >: Int](op: (B, B) => B): B = values.reduce(op) // sum, produce + override def reduceLeft[B >: Int](op: (B, Int) => B): B = values.reduceLeft(op) // min, max + override def knownSize = values.size + } + assertEquals(42, reductive.sum) + assertEquals(768, reductive.product) + assertEquals(1, reductive.min) + assertEquals(32, reductive.max) } } diff --git a/test/junit/scala/collection/SeqViewTest.scala b/test/junit/scala/collection/SeqViewTest.scala index 6b91a34e790d..2bcc46c4b03b 100644 --- a/test/junit/scala/collection/SeqViewTest.scala +++ b/test/junit/scala/collection/SeqViewTest.scala @@ -1,14 +1,51 @@ package scala.collection -import org.junit.Assert._ +import org.junit.Assert.{assertThrows => _, _} import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 +import scala.tools.testkit.AssertUtil.assertThrows + @RunWith(classOf[JUnit4]) class SeqViewTest { @Test def _toString(): Unit = { assertEquals("SeqView()", Seq(1, 2, 3).view.toString) } + + private def seq(n: Int, maxIterate: Int = -1): Seq[Int] = + new AbstractSeq[Int] { + val values = 1 to n + var remaining = maxIterate + def iterator = { + if (remaining == 0) throw new IllegalStateException() + if (remaining > 0) remaining -= 1 + values.iterator + } + def apply(i: Int) = values(i) + def length = values.length + } + private def seqk(n: Int, maxIterate: Int): Seq[Int] = + new AbstractSeq[Int] { + val values = 1 to n + var remaining = maxIterate + def iterator = { + if (remaining == 0) throw new IllegalStateException() + if (remaining > 0) remaining -= 1 + values.iterator + } + def apply(i: Int) = values(i) + def length = values.length + override def knownSize = length + } + + @Test def `sum of seq view`: Unit = { + assertEquals(10, seq(4).view.sum) + } + @Test def `sum of seq view iterates once`: Unit = { + val sut = seqk(4, 1).view + assertEquals(10, sut.sum) + assertThrows[IllegalStateException](sut.sum) + } } diff --git a/test/junit/scala/collection/convert/EqualsTest.scala b/test/junit/scala/collection/convert/EqualsTest.scala new file mode 100644 index 000000000000..b3f9ae17176b --- /dev/null +++ b/test/junit/scala/collection/convert/EqualsTest.scala @@ -0,0 +1,78 @@ + +package scala.collection.convert + +import org.junit.Test +import org.junit.Assert._ + +import scala.jdk.CollectionConverters._ +import JavaCollectionWrappers._ + +import java.util.{AbstractList, AbstractSet, List => JList, Set => JSet} + +class JTestList(vs: Int*) extends AbstractList[Int] { + def this() = this(Nil: _*) + override def size = vs.size + override def get(i: Int) = vs(i) +} +class JTestSet(vs: Int*) extends AbstractSet[Int] { + def this() = this(Nil: _*) + require(vs.toSet.size == vs.size) + override def size = vs.size + override def iterator = vs.iterator.asJava +} + +/** Test that collection wrappers forward equals and hashCode where appropriate. */ +class EqualsTest { + + def jlstOf(vs: Int*): JList[Int] = new JTestList(vs: _*) + def jsetOf(vs: Int*): JSet[Int] = new JTestSet(vs: _*) + + // Seq extending AbstractList inherits equals + + @Test def `List as JList has equals`: Unit = { + val list = List(1, 2, 3) + val jlst = new SeqWrapper(list) + assertEquals(jlstOf(1, 2, 3), jlst) + assertEquals(jlst, jlstOf(1, 2, 3)) + assertTrue(jlst == jlstOf(1, 2, 3)) + assertEquals(jlst.hashCode, jlst.hashCode) + } + + @Test def `Set as JSet has equals`: Unit = { + val set = Set(1, 2, 3) + val jset = new SetWrapper(set) + assertEquals(jsetOf(1, 2, 3), jset) + assertEquals(jset, jsetOf(1, 2, 3)) + assertTrue(jset == jsetOf(1, 2, 3)) + assertEquals(jset.hashCode, jset.hashCode) + } + + @Test def `Map as JMap has equals`: Unit = { + val map = Map(1 -> "one", 2 -> "two", 3 -> "three") + val jmap = new MapWrapper(map) + assertEquals(jmap, jmap) + } + + @Test def `Anything as Collection is equal to Anything`: Unit = { + def set = Set(1, 2, 3) + def jset = new IterableWrapper(set) + assertTrue(jset == jset) + assertEquals(jset, jset) + assertNotEquals(jset, set) + assertEquals(jset.hashCode, jset.hashCode) + } + + @Test def `Iterator wrapper does not compare equal`: Unit = { + def it = List(1, 2, 3).iterator + def jit = new IteratorWrapper(it) + assertNotEquals(jit, jit) + assertNotEquals(jit.hashCode, jit.hashCode) + } + + @Test def `Anything.asScala Iterable has case equals`: Unit = { + def vs = jlstOf(42, 27, 37) + def it = new JListWrapper(vs) + assertEquals(it, it) + assertEquals(it.hashCode, it.hashCode) + } +} diff --git a/test/junit/scala/collection/immutable/ListSetTest.scala b/test/junit/scala/collection/immutable/ListSetTest.scala index 4ce4fc5a6206..54acd617d46c 100644 --- a/test/junit/scala/collection/immutable/ListSetTest.scala +++ b/test/junit/scala/collection/immutable/ListSetTest.scala @@ -1,6 +1,8 @@ package scala.collection.immutable -import org.junit.Assert._ +import scala.tools.testkit.AssertUtil.{assertSameElements, fail} + +import org.junit.Assert.{assertEquals, assertSame, fail => _} import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 @@ -79,6 +81,17 @@ class ListSetTest { bar = foo ++ ListSet(1, 2, 3, 4, 5, 6) assertEquals(List(1, 2, 3, 4, 5, 6), bar.iterator.toList) + assertSameElements(List(1, 2, 3, 4, 5, 6), bar.iterator) + + assertSame(foo, foo ++ foo) + assertSame(foo, foo ++ ListSet.empty) + assertSame(foo, foo ++ Nil) + } + + @Test def `t12316 ++ is correctly ordered`: Unit = { + // was: ListSet(1, 2, 3, 42, 43, 44, 29, 28, 27, 12, 11, 10) + assertEquals(ListSet(1,2,3,42,43,44,10,11,12,27,28,29), ListSet(1,2,3,42,43,44) ++ ListSet(10,11,12,42,43,44,27,28,29)) + assertSameElements(List(1,2,3,42,43,44,10,11,12,27,28,29), ListSet(1,2,3,42,43,44) ++ ListSet(10,11,12,42,43,44,27,28,29)) } @Test diff --git a/test/junit/scala/collection/immutable/MapTest.scala b/test/junit/scala/collection/immutable/MapTest.scala index 6846a3bcf0c2..28b6fba9fba5 100644 --- a/test/junit/scala/collection/immutable/MapTest.scala +++ b/test/junit/scala/collection/immutable/MapTest.scala @@ -3,6 +3,8 @@ package scala.collection.immutable import org.junit.Assert.assertEquals import org.junit.Test +import scala.annotation.nowarn + class MapTest { @Test def builderCompare1(): Unit = { @@ -141,4 +143,11 @@ class MapTest { } } } + + @Test @nowarn("cat=deprecation") + def t12699(): Unit = { + val m1: HashMap[Int, Int] = HashMap(1 -> 1) + assertEquals(7, m1.+(elem1 = 2 -> 2, elem2 = 3 -> 3, elems = List( 4 -> 4, 5 -> 5, 6 -> 6, 7 -> 7): _*).size) + assertEquals(7, m1.+(2 -> 2, 3 -> 3, 4 -> 4, 5 -> 5, 6 -> 6, 7 -> 7).size) + } } diff --git a/test/junit/scala/collection/immutable/NumericRangeTest.scala b/test/junit/scala/collection/immutable/NumericRangeTest.scala index c784ea8d9559..be36632a95da 100644 --- a/test/junit/scala/collection/immutable/NumericRangeTest.scala +++ b/test/junit/scala/collection/immutable/NumericRangeTest.scala @@ -157,4 +157,220 @@ class NumericRangeTest { assertEquals(Nil, check(testDecreaseCase.start to testIncreaseCase.last by testIncreaseCase.step)) assertEquals(Nil, check(testDecreaseCase.inclusive)) } + + @Test + def numericRangeWithMoreThanMaxIntTake() = { + val start = BigInt(0) + val step = BigInt(1) + val end = BigInt(Int.MaxValue) * 5 + + // evaluation of length causes IllegalArgumentException because it cannot be represented as Int number + // Yet using `take` should be independent of evaluating length when it's not necessary. + assertThrows[IllegalArgumentException]((start until end by step).length) + val range = start until end by step + + val take = 100 + val smallChunkOfRange = range.take(take) + val expected = start until BigInt(100) by step + assertTrue(smallChunkOfRange equals expected) + assertTrue(smallChunkOfRange.length == take) + } + + @Test + def numericRangeWithMoreThanMaxIntDrop() = { + val start = BigInt(0) + val step = BigInt(1) + val toAddToMaxInt = 50 + val end = BigInt(Int.MaxValue) + toAddToMaxInt + + val drop = Int.MaxValue + + // evaluation of length causes IllegalArgumentException because it cannot be represented as Int number + // Yet using `drop` should be independent of evaluating length when it's not necessary. + assertThrows[IllegalArgumentException]((start until end by step).length) + val range = start until end by step + + val smallChunkOfRange = range.drop(drop) + val expected = BigInt(Int.MaxValue) until end by step + assertTrue(smallChunkOfRange equals expected) + assertTrue(smallChunkOfRange.length == toAddToMaxInt) + } + + @Test + def numericRangeSmallTypesDrop() = { + val byteStart: Byte = Byte.MinValue + val byteEnd: Byte = Byte.MaxValue + val drop = 10 + + val byteRange = NumericRange(byteStart, byteEnd, (1: Byte)) + val byteRangeChunk = byteRange.drop(drop) + assertTrue(byteRangeChunk.length == byteRange.length - drop) + assertTrue(byteRangeChunk.end == byteEnd) + assertTrue(byteRangeChunk.start == (byteStart + drop.toByte)) + + val shortStart: Short = Short.MinValue + val shortEnd: Short = Short.MaxValue + + val shortRange = NumericRange(shortStart, shortEnd, (1: Short)) + val shortRangeChunk = shortRange.drop(drop) + assertTrue(shortRangeChunk.length == shortRange.length - drop) + assertTrue(shortRangeChunk.end == shortEnd) + assertTrue(shortRangeChunk.start == (shortStart + drop.toShort)) + } + + @Test + def numericRangeSmallTypesTake() = { + val byteStart: Byte = Byte.MinValue + val byteEnd: Byte = Byte.MaxValue + val take = 10 + + val byteRange = NumericRange(byteStart, byteEnd, (1: Byte)) + val byteRangeChunk = byteRange.take(take) + assertTrue(byteRangeChunk.length == take) + assertTrue(byteRangeChunk.end == byteStart + take.toByte - 1) + assertTrue(byteRangeChunk.start == byteStart) + + val shortStart: Short = Short.MinValue + val shortEnd: Short = Short.MaxValue + + val shortRange = NumericRange(shortStart, shortEnd, (1: Short)) + val shortRangeChunk = shortRange.take(take) + assertTrue(shortRangeChunk.length == take) + assertTrue(shortRangeChunk.end == shortStart + take.toShort - 1) + assertTrue(shortRangeChunk.start == shortStart) + } + + @Test + def takeAndDropForCustomTypes() = { + import NumericRangeTest._ + + // smaller than Int + val start = NumericWrapper(Byte.MinValue) + val step = NumericWrapper(1: Byte) + val end = NumericWrapper(Byte.MaxValue) + val range = NumericRange.inclusive(start, end, step) + + val amount = 20 + + val taken = range.take(amount) + val dropped = range.drop(amount) + + assertTrue(taken.start == range.start && taken.length == amount) + assertTrue(dropped.end == range.end && dropped.length == range.length - amount) + + // Int + val startInt = NumericWrapper(Int.MinValue) + val endInt = NumericWrapper(Int.MaxValue) + val stepInt = NumericWrapper(1) + + val intRange = NumericRange.inclusive(startInt, endInt, stepInt) + + val amountForInts = 40 + + val takenInts = intRange.take(amountForInts) + val droppedInts = intRange.drop(amountForInts) + + assertTrue(takenInts.start == intRange.start && takenInts.length == amountForInts) + assertTrue(droppedInts.end == intRange.end && droppedInts.start.value == intRange.start.value + (amountForInts * intRange.step.value)) + + // Larger than int + + val startLong = NumericWrapper(Long.MinValue) + val stepLong = NumericWrapper(1L) + val endLong = NumericWrapper(Long.MaxValue) + + val longRange = NumericRange.inclusive(startLong, endLong, stepLong) + + val amountForLongs = 50 + + val takenLongs = longRange.take(amountForLongs) + val droppedLongs = longRange.drop(amountForLongs) + + assertTrue(takenLongs.start == longRange.start && takenLongs.length == amountForLongs) + assertTrue(droppedLongs.end == longRange.end && droppedLongs.start.value == longRange.start.value + (amountForLongs * longRange.step.value)) + } + + @Test + def wideIntNumericRangeTakeAndDrop() = { + val start = Int.MinValue + val end = Int.MaxValue + val step = 1 + + val amount = 50 + + val range = NumericRange(start, end, step) + + assertThrows[IllegalArgumentException](range.length) + assertTrue(range.take(amount).length == amount) + val dropped = range.drop(amount) + assertTrue(dropped.end == range.end && dropped.step == range.step && dropped.start == (amount * step) + range.start) + } + + @Test + def wideNegativeNumericRangeUntilZeroShouldNotOverflow() = { + val start = Int.MinValue + val end = 0 + val step = 1 + + val amount = 50 + + val range = NumericRange.inclusive(start, end, step) + + assertThrows[IllegalArgumentException](range.length) + + val taken = range.take(amount) + assertTrue(taken.length == amount) + assertTrue(taken.start == range.start && taken.step == range.step) + + + val dropped = range.drop(amount) + assertTrue(dropped.end == range.end && dropped.step == range.step && dropped.start == (amount * step) + range.start) + } + +} + +object NumericRangeTest { + + private case class NumericWrapper[T](value: T) + private object NumericWrapper { + implicit def isIntegral[T](implicit tNum: Integral[T]): Integral[NumericWrapper[T]] = new Integral[NumericWrapper[T]] { + override def quot(x: NumericWrapper[T], y: NumericWrapper[T]): NumericWrapper[T] = + NumericWrapper(tNum.quot(x.value, y.value)) + + override def rem(x: NumericWrapper[T], y: NumericWrapper[T]): NumericWrapper[T] = + NumericWrapper(tNum.rem(x.value, y.value)) + + override def plus(x: NumericWrapper[T], y: NumericWrapper[T]): NumericWrapper[T] = + NumericWrapper(tNum.plus(x.value, y.value)) + + override def minus(x: NumericWrapper[T], y: NumericWrapper[T]): NumericWrapper[T] = + NumericWrapper(tNum.minus(x.value, y.value)) + + override def times(x: NumericWrapper[T], y: NumericWrapper[T]): NumericWrapper[T] = + NumericWrapper(tNum.times(x.value, y.value)) + + override def negate(x: NumericWrapper[T]): NumericWrapper[T] = + NumericWrapper(tNum.negate(x.value)) + + override def fromInt(x: Int): NumericWrapper[T] = + NumericWrapper(tNum.fromInt(x)) + + override def parseString(str: String): Option[NumericWrapper[T]] = + tNum.parseString(str).map(NumericWrapper.apply) + + override def toInt(x: NumericWrapper[T]): Int = + tNum.toInt(x.value) + + override def toLong(x: NumericWrapper[T]): Long = + tNum.toLong(x.value) + + override def toFloat(x: NumericWrapper[T]): Float = + tNum.toFloat(x.value) + + override def toDouble(x: NumericWrapper[T]): Double = + tNum.toDouble(x.value) + + override def compare(x: NumericWrapper[T], y: NumericWrapper[T]): Int = tNum.compare(x.value, y.value) + } + } } \ No newline at end of file diff --git a/test/junit/scala/collection/immutable/SetTest.scala b/test/junit/scala/collection/immutable/SetTest.scala index 736c1e7e2e81..6bc08f19d93c 100644 --- a/test/junit/scala/collection/immutable/SetTest.scala +++ b/test/junit/scala/collection/immutable/SetTest.scala @@ -160,4 +160,17 @@ class SetTest { testCorrectness() testNoHashCodeInvocationsDuringSubsetOf() } + + @Test def pr10238(): Unit = { + assertEquals(BitSet(0), BitSet(0, 128) diff BitSet(128)) + + val us = scala.collection.immutable.BitSet(39, 41, 44, 46, 256) + val vs = scala.collection.immutable.BitSet(39, 41, 44, 46, 64, 256) + val xs = scala.collection.immutable.BitSet.fromBitMask(us.toBitMask.take(3)) + val ys = scala.collection.immutable.BitSet.fromBitMask(vs.toBitMask.take(3)) + val diff = ys diff xs + val expected = scala.collection.immutable.BitSet(64) + assertEquals(diff, expected) + } + } diff --git a/test/junit/scala/collection/immutable/VectorTest.scala b/test/junit/scala/collection/immutable/VectorTest.scala index d5487d2ffe5b..f599c3a706d5 100644 --- a/test/junit/scala/collection/immutable/VectorTest.scala +++ b/test/junit/scala/collection/immutable/VectorTest.scala @@ -6,6 +6,7 @@ import org.junit.runners.JUnit4 import org.junit.Test import scala.annotation.unused +import scala.collection.immutable.VectorInline.{WIDTH3, WIDTH4, WIDTH5} import scala.collection.mutable.{ListBuffer, StringBuilder} import scala.tools.testkit.AssertUtil.intercept @@ -47,7 +48,7 @@ class VectorTest { @Test def hasCorrectAppendedAndPrependedAll(): Unit = { - val els = Vector(1 to 1000: _*) + val els = Vector(1 to 1200: _*) for (i <- 0 until els.size) { val (prefix, suffix) = els.splitAt(i) @@ -61,6 +62,200 @@ class VectorTest { } } + @Test + def testBuilderAddVector(): Unit = { + import VectorInline._ + val b = new VectorBuilder[Int] + val expected1 = Vector.from(1 to 32).asInstanceOf[Vector1[Int]] + b.initFrom(expected1) + val expected2 = Vector.from(33 to 128).asInstanceOf[Vector2[Int]] + b.addAll(expected2) // uses addVector with Vector2, aligned + b.addOne(129) + val expected3 = Vector.from(130 to 224).asInstanceOf[Vector2[Int]] + b.addAll(expected3) // uses addVector with Vector2, but misaligned + b.addAll(Vector.from(225 to 4096)) // uses addVector with Vector3, aligned to 32, but not 1024 + b.addAll(Vector.from(4097 to 8192)) // uses addVector with Vector3, aligned to 1024 + b.addOne(8193) // builder still working for single element? + b.addAll(Vector.from(8193 to 234567).tail) // aligned to 1024, split at arbitrary number 234567 + b.addAll(Vector.from(1 to 42 * WIDTH3).drop(234567)) // aligned to pow(32,3) + val res = b.result().asInstanceOf[Vector5[Int]] + assertEquals(1 to 42 * WIDTH3, res) + + assertSame("b.initFrom did not keep original array but copied instead", expected1.prefix1, res.prefix1) + +// assertSame(s"b.addVector did not keep original array but copied instead (${expected2.prefix1.head},...,${expected2.prefix1.last} vs ${res.prefix2(0).head},...,${res.prefix2(0).last}).", expected2.prefix1, res.prefix2(0)) // prefix1 is not reused, as addArr1 is called + assertSame("b.addVector did not keep original array but copied instead", expected2.data2(0), res.prefix2(1)) // expected2's arrays are reused + + assertTrue("", expected3.suffix1.length != 32) // expected3 is misaligned + } + + @Test + def testBuilderAlignTo1(): Unit = { + // v3 == Vector(0, 1, ..., 1999), but alignment is no multiple of 32 or 1024 + val v3 = Vector.tabulate(2042)(i => (i - 42).toString).drop(42).asInstanceOf[Vector3[AnyRef]] + for (i <- Seq(0, 5, 123, 949, 950, 982, 1024, 1999, 2000)) { + val (a, b) = v3.splitAt(i) + val res = new VectorBuilder[AnyRef] + .alignTo(i, b) + .addAll(a.toList) // ensure there is no alignment in a + .addAll(b) + .result() + .asInstanceOf[Vector3[AnyRef]] + assertEquals(s"values equal when split at $i", v3, res) + if (i < 950) // (v3.prefix1++v3.prefix2).size == 982. So when dropping >= 950 elements, and keeping prefix1 nonempty (=> has 32 elements), prefix2 would be empty. Instead, suffix2's content is stored in prefix2, so the alignment (len12) changes and it's okay. + assertEquals(s"alignment is the same when split at $i", v3.len12, res.len12) + } + } + + @Test + def testBuilderAlignTo2(): Unit = { + val Large = 1 << 20 + for ( + size <- Seq(0, 1, 31, 1 << 5, 1 << 10, 1 << 15, 1 << 20, 9 << 20, 1 << 25, 9 << 25, 50 << 25, 1 << 30, (1 << 31) - (1 << 26) - 1000); + i <- Seq(0, 1, 5, 123) + ) { +// println((i, size)) + val v = if (size < Large) Vector.tabulate(size)(_.toString) else Vector.fillSparse(size)("v") + val prefix = Vector.fill(i)("prefix") + val vb = new VectorBuilder[AnyRef] + .alignTo(i, v) + .addAll(prefix) + .addAll(v) + val res = vb.result() + val vDesc = if (v.headOption.contains("v")) s"Vector(\"v\")*$size" else s"Vector(0,..,${size-1})" + assertEquals(s"(Vector(\"prefix\")*$i ++ $vDesc).size", size + i, res.size) + assertEquals(s"(Vector(\"prefix\")*$i ++ $vDesc).take($i)", prefix, res.take(i)) + assertEquals(s"(Vector(\"prefix\")*$i ++ $vDesc).drop($i)", v.take(Large), res.drop(i).take(Large)) + + if (size == 9 << 20) { + val v4 = Vector.fillSparse(WIDTH3 * 2)("v4") + val suffix = (v4 ++ v).take(size) + val res2 = vb.addAll(suffix).result() + assertEquals(s"(Vector(\"prefix\")*$i ++ $vDesc ++ v4 ++ $vDesc).size", 2 * size + i, res2.size) + assertEquals(s"(Vector(\"prefix\")*$i ++ $vDesc ++ v4 ++ $vDesc).take($i)", prefix, res2.take(i)) + assertEquals(s"(Vector(\"prefix\")*$i ++ $vDesc ++ v4 ++ $vDesc).drop($i).take($size)", v.take(Large), res2.drop(i).take(Large)) + assertEquals(s"(Vector(\"prefix\")*$i ++ $vDesc ++ v4 ++ $vDesc).drop(${i + size}).take($size)", suffix.take(Large), res2.drop(i + size).take(Large)) + assertEquals(s"(Vector(\"prefix\")*$i ++ $vDesc ++ v4 ++ $vDesc).drop(${i + 2 * size})", suffix.drop(size).take(Large), res2.drop(i + 2 * size).take(Large)) + } else if (size == 9 << 25) { + val v4 = Vector.fillSparse(WIDTH4 * 2)("v4") + val suffix = (v4 ++ v).take(size) + val res2 = vb.addAll(suffix).result() + assertEquals(s"(Vector(\"prefix\")*$i ++ $vDesc ++ v4 ++ $vDesc).size", 2 * size + i, res2.size) + assertEquals(s"(Vector(\"prefix\")*$i ++ $vDesc ++ v4 ++ $vDesc).take($i)", prefix, res2.take(i)) + assertEquals(s"(Vector(\"prefix\")*$i ++ $vDesc ++ v4 ++ $vDesc).drop($i).take($size)", v.take(Large), res2.drop(i).take(Large)) + assertEquals(s"(Vector(\"prefix\")*$i ++ $vDesc ++ v4 ++ $vDesc).drop(${i + size}).take($size)", suffix.take(Large), res2.drop(i + size).take(Large)) + assertEquals(s"(Vector(\"prefix\")*$i ++ $vDesc ++ v4 ++ $vDesc).drop(${i + 2 * size})", suffix.drop(size).take(Large), res2.drop(i + 2 * size).take(Large)) + } else if (size == 50 << 25) { + assertThrows(classOf[IllegalArgumentException], () => vb.addAll(v)) + } + } + } + + @Test + def testBuilderInitWithLargeVector(): Unit = { + val v = Vector.fillSparse(Int.MaxValue / 4 * 3)("v") + val copy = + new VectorBuilder[String] + .initFrom(v) + .result() + assertEquals(copy.size, v.size) + assertEquals(copy.take(500), v.take(500)) + } + + @Test + def testWeirdAlignments1(): Unit = { + val v3 = Vector.tabulate(2042)(i => (i - 42).toString).drop(42).asInstanceOf[Vector3[AnyRef]] + for (i <- Seq(0, 1, 5, 41, 42, 43, 123, 949, 950, 982, 1024, 1999, 2000)) { + val res = new VectorBuilder[AnyRef] + .alignTo(i, v3) // pretend to add i elements before v3, but then decide not to... this is slow, but should not fail + .addAll(v3) + .result() + .asInstanceOf[Vector3[AnyRef]] + assertEquals(s"vectors equal", v3, res) + } + } + + @Test + def testWeirdAlignments2(): Unit = { + val v3 = Vector.tabulate(2042)(i => (i - 42).toString).drop(42).asInstanceOf[Vector3[AnyRef]] + val v2 = Vector.tabulate(765)(i => (i - 123).toString).drop(123).asInstanceOf[Vector2[AnyRef]] + for (i <- Seq(-1234, -42, -1, 0, 1, 5, 41, 42, 43, 123, 949, 950, 982, 1024, 1999, 2000)) { + val res = new VectorBuilder[AnyRef] + .alignTo(i, v3) // pretend to add v3 ... + .addOne("a") + .addAll(v2) // ... but then add completely unrelated v2 instead! + .result() + assertEquals(s"vectors equal", "a" +: v2, res) + } + } + + @Test + def testWeirdAlignments3(): Unit = for (n <- allSizes; m <- verySmallSizes) { + val vPretend = + if (smallSizes.contains(n)) + Vector.tabulate(n + 1337)(i => (i - 1337).toString).drop(1337) + else + Vector.fillSparse(n + 1337)("v").drop(1337) + val vReal = Vector.tabulate(m + 42)(i => (i - 42).toString).drop(42) + for (i <- Seq(-1234, -42, -1, 0, 1, 5, 41, 42, 43, 123, 949, 950, 982, 1024, 1999, 2000, 1234567)) { + val vb = new VectorBuilder[AnyRef] + .alignTo(i, vPretend) // pretend to add vPretend ... + .addAll(vReal) // ... but then add completely unrelated vReal instead! + val res1 = vb.result() + assertEquals(s"vectors not equal, n=$n, m=$m, i=$i", vReal, res1) + val res2 = vb + .addOne("a") + .addAll(vReal) // reuse the builder + .result() + assertEquals(s"vectors not equal, n=$n, m=$m, i=$i", (vReal :+ "a") ++ vReal, res2) + } + } + + @Test + def testWeirdAlignments4(): Unit = { + var lengths = Set[Int]() + for ( + n <- allSizes.init :+ (allSizes.last - WIDTH5 - WIDTH3 + 41); // we need WIDTH5 for prefix, add 1+WIDTH3 and get 42 in suffix free + m <- List(WIDTH4, WIDTH5, Int.MaxValue - WIDTH5) + ) { + val vPretend = Vector.fillSparse(42)("v0") ++ Vector.fillSparse(m - 42)("v1") + val vReal = vPretend.take(n) + // if (n==1073741824 && m==2046820352) + // println(s"n=$n, m=$m") + val vb = new VectorBuilder[AnyRef] + .alignTo(0, vPretend) // pretend to add vPretend ... + .addAll(vReal) // ... but then add only a subsequence! + val res1 = vb.result() + assertEquals(s"vectors not equal, n=$n, m=$m", vReal.length, res1.length) + assertEquals(s"vectors not equal, n=$n, m=$m", vReal.take(WIDTH3), res1.take(WIDTH3)) + assertEquals(s"vectors not equal, n=$n, m=$m", vReal.takeRight(WIDTH3), res1.takeRight(WIDTH3)) + val res2 = vb + .addOne("a") + .addAll(vReal.take(WIDTH3)) // whole vector may take too long + .result() + val expected = (vReal :+ "a") ++ (vReal.take(WIDTH3)) + assertEquals(s"vectors not equal, n=$n, m=$m", expected.length, res2.length) + assertEquals(s"vectors not equal, n=$n, m=$m", expected.take(WIDTH3), res2.take(WIDTH3)) + assertEquals(s"vectors not equal, n=$n, m=$m", expected.takeRight(WIDTH3), res2.takeRight(WIDTH3)) + lengths += res2.length + } + assertEquals(15, lengths.size) + assertEquals(Int.MaxValue - WIDTH5 + 42, lengths.max) + } + + @Test + def testNegativeAlignment(): Unit = for (size <- allSizes; i <- allSizes) { + val v = Vector.fillSparse(math.min(63 * WIDTH5, size))("v") + val expected = v.drop(i) + val vb = new VectorBuilder[AnyRef] + .alignTo(-i, v) + .addAll(expected) + val res = vb.result() + assertEquals("lengths not equal", expected.length, res.length) + assertEquals(s"vectors not equal", expected.take(WIDTH3), res.take(WIDTH3)) + assertEquals(s"vectors not equal", expected.takeRight(WIDTH3), res.takeRight(WIDTH3)) + } + @Test def factoryReuse(): Unit = { assertSame(Vector.empty, Vector.empty) @@ -384,9 +579,10 @@ class VectorTest { } } - @Test - def testSlice3(): Unit = { - assertEquals(Vector(1).slice(1, -2147483648), Vector()) + @Test def `test slice to MinValue`: Unit = { + assertTrue(Vector(42).slice(1, Int.MinValue).isEmpty) + assertTrue("slice almost max to min should be empty", Vector(42).slice(Int.MaxValue-1, Int.MinValue).isEmpty) + assertTrue("slice max to min should be empty", Vector(42).slice(Int.MaxValue, Int.MinValue).isEmpty) } @Test diff --git a/test/junit/scala/collection/mutable/ArrayBufferTest.scala b/test/junit/scala/collection/mutable/ArrayBufferTest.scala index 082fa6d0f807..933ea7fae5ce 100644 --- a/test/junit/scala/collection/mutable/ArrayBufferTest.scala +++ b/test/junit/scala/collection/mutable/ArrayBufferTest.scala @@ -7,20 +7,17 @@ import java.lang.reflect.InvocationTargetException import scala.annotation.nowarn import scala.tools.testkit.AssertUtil.{assertSameElements, assertThrows, fail} import scala.tools.testkit.ReflectUtil.{getMethodAccessible, _} +import scala.util.chaining._ class ArrayBufferTest { /* Test for scala/bug#9043 */ @Test - def testInsertAll(): Unit = { - val traver = ArrayBuffer(2, 4, 5, 7) + def testInsertAll: Unit = { + def traver = ArrayBuffer(2, 4, 5, 7) val testSeq = List(1, 3, 6, 9) - def insertAt(x: Int) = { - val clone = traver.clone() - clone.insertAll(x, testSeq) - clone - } + def insertAt(x: Int) = traver.tap(_.insertAll(x, testSeq)) // Just insert some at position 0 assertEquals(ArrayBuffer(1, 3, 6, 9, 2, 4, 5, 7), insertAt(0)) @@ -34,6 +31,9 @@ class ArrayBufferTest { // Overflow is caught assertThrows[IndexOutOfBoundsException] { insertAt(-1) } assertThrows[IndexOutOfBoundsException] { insertAt(traver.size + 10) } + + val xs = new Iterable[Int] { def iterator = Iterator(42); override def knownSize = 10 } + assertThrows[IllegalStateException] { traver.tap(_.insertAll(0, xs)) } } @Test diff --git a/test/junit/scala/collection/mutable/HashSetTest.scala b/test/junit/scala/collection/mutable/HashSetTest.scala index 502b8faf3527..36ed3400f1a9 100644 --- a/test/junit/scala/collection/mutable/HashSetTest.scala +++ b/test/junit/scala/collection/mutable/HashSetTest.scala @@ -86,28 +86,19 @@ class HashSetTest { } class OnceOnly extends IterableOnce[Int] { - override def knownSize: Int = 1 - - var iterated:Boolean = false - - override def iterator: Iterator[Int] = { - new Iterator[Int] { - assert(!iterated) - iterated = true - private var v = Option(42) - override def hasNext: Boolean = v.nonEmpty && knownSize > 0 - override def next(): Int = { - val res = v.get - v = None - res - } - } + var iterated = false + + override def iterator = { + assertFalse("Attempt to re-iterate!", iterated) + iterated = true + Iterator.single(42) } } @Test - def addAllTest2(): Unit = { + def `addAll adds exactly once`: Unit = { val hs = HashSet.empty[Int] hs.addAll(new OnceOnly) + assertEquals(1, hs.size) } } diff --git a/test/junit/scala/collection/mutable/LinkedHashMapTest.scala b/test/junit/scala/collection/mutable/LinkedHashMapTest.scala index 5b76a43cb427..6467bd299459 100644 --- a/test/junit/scala/collection/mutable/LinkedHashMapTest.scala +++ b/test/junit/scala/collection/mutable/LinkedHashMapTest.scala @@ -3,13 +3,15 @@ package scala.collection.mutable import org.junit.Assert._ import org.junit.runner.RunWith import org.junit.runners.JUnit4 -import org.junit.{ Assert, Test } +import org.junit.{Assert, Test} +import scala.annotation.nowarn import scala.collection.mutable /* Test for scala/bug#9095 */ @RunWith(classOf[JUnit4]) class LinkedHashMapTest { + @nowarn("msg=inheritance from class LinkedHashMap") class TestClass extends mutable.LinkedHashMap[String, Int] { def lastItemRef = lastEntry } @@ -167,4 +169,18 @@ class LinkedHashMapTest { assertEquals(hashMapCount4, mutable.LinkedHashMap(countingKey1 -> "a")) } + + @Test + def testfromfactorymethod(): Unit = { + val data = List((1, 'a'), (2,'b'),(3,'c'), (4,'d'),(5,'e'),(6,'f')) + val lhm = new mutable.LinkedHashMap[Int, Char] + data.foreach(x => lhm.addOne(x)) + + val fromlhm1 = LinkedHashMap.from(data) + assertEquals(fromlhm1, lhm) + + val fromlhm2 = LinkedHashMap.from(lhm) + assertEquals(fromlhm2, lhm) + assertFalse(fromlhm2 eq lhm) + } } diff --git a/test/junit/scala/collection/mutable/LinkedHashSetTest.scala b/test/junit/scala/collection/mutable/LinkedHashSetTest.scala index 92a5de20745d..8e812c4c6c4a 100644 --- a/test/junit/scala/collection/mutable/LinkedHashSetTest.scala +++ b/test/junit/scala/collection/mutable/LinkedHashSetTest.scala @@ -2,13 +2,15 @@ package scala.collection.mutable import org.junit.runner.RunWith import org.junit.runners.JUnit4 -import org.junit.{ Assert, Test } +import org.junit.{Assert, Test} +import scala.annotation.nowarn import scala.collection.mutable /* Test for scala/bug#9095 */ @RunWith(classOf[JUnit4]) class LinkedHashSetTest { + @nowarn("msg=inheritance from class LinkedHashSet") class TestClass extends mutable.LinkedHashSet[String] { def lastItemRef = lastEntry } @@ -22,4 +24,18 @@ class LinkedHashSetTest { lhs.clear() Assert.assertNull(lhs.lastItemRef) } + + @Test + def testfromfactorymethod(): Unit = { + val data = List(1,2,3,4,5,6,7,8) + val lhs = new mutable.LinkedHashSet[Int] + data.foreach(x => lhs.addOne(x)) + + val fromlhs1 = LinkedHashSet.from(data) + Assert.assertEquals(fromlhs1, lhs) + + val fromlhs2 = LinkedHashSet.from(lhs) + Assert.assertEquals(fromlhs2, lhs) + Assert.assertFalse(fromlhs2 eq lhs) + } } diff --git a/test/junit/scala/io/SourceTest.scala b/test/junit/scala/io/SourceTest.scala index 0c4a996d1c9f..593b990d0413 100644 --- a/test/junit/scala/io/SourceTest.scala +++ b/test/junit/scala/io/SourceTest.scala @@ -10,7 +10,7 @@ import java.io.{Console => _, _} class SourceTest { - private implicit val `our codec` = Codec.UTF8 + private implicit val `our codec`: Codec = Codec.UTF8 private val charSet = Codec.UTF8.charSet.name private def sampler = """ diff --git a/test/junit/scala/jdk/javaapi/StreamConvertersJavaTest.java b/test/junit/scala/jdk/javaapi/StreamConvertersJavaTest.java index eec35cef5a0e..e128f87fd6ce 100644 --- a/test/junit/scala/jdk/javaapi/StreamConvertersJavaTest.java +++ b/test/junit/scala/jdk/javaapi/StreamConvertersJavaTest.java @@ -29,7 +29,7 @@ public class StreamConvertersJavaTest { public List li = CollectionConverters.asScala(Arrays.asList(1, 2)).toList(); public List lf = CollectionConverters.asScala(Arrays.asList(1f, 2f)).toList(); - public Map mkm(K k1, V v1, K k2, V v2) { + public static Map mkm(K k1, V v1, K k2, V v2) { java.util.Map m = new java.util.HashMap(); m.put(k1, v1); m.put(k2, v2); diff --git a/test/junit/scala/math/BigDecimalTest.scala b/test/junit/scala/math/BigDecimalTest.scala index 2c003b6451a0..1655cbf38d13 100644 --- a/test/junit/scala/math/BigDecimalTest.scala +++ b/test/junit/scala/math/BigDecimalTest.scala @@ -1,7 +1,9 @@ package scala.math import org.junit.Test +import org.junit.Assert.{assertEquals, assertNotEquals, assertNull, assertTrue} import java.math.{BigDecimal => BD, MathContext => MC} +import scala.tools.testkit.AssertUtil.{assertEqualsAny, assertNotEqualsAny} /* Tests various maps by making sure they all agree on the same answers. */ class BigDecimalTest { @@ -25,7 +27,7 @@ class BigDecimalTest { BigDecimal("14.19238571927581e6"), BigDecimal("912834718237510238591285")/2 ) - assert(wholes.forall(_.isWhole) && fracs.forall(! _.isWhole)) + assertTrue(wholes.forall(_.isWhole) && fracs.forall(! _.isWhole)) } // Motivated by scala/bug#6699: BigDecimal.isValidDouble behaves unexpectedly @@ -42,10 +44,8 @@ class BigDecimalTest { BigDecimal("10e1000000"), BigDecimal("10e-1000000") ) - assert( - valids.forall(_.isDecimalDouble) && - invalids.forall(! _.isDecimalDouble) - ) + assertTrue(valids.forall(_.isDecimalDouble)) + assertTrue(invalids.forall(! _.isDecimalDouble)) } // Motivated by scala/bug#6173: BigDecimal#isWhole implementation is very heap intensive @@ -54,7 +54,7 @@ class BigDecimalTest { val troublemaker = BigDecimal("1e1000000000") val reasonable = BigDecimal("1e1000") val reasonableInt = reasonable.toBigInt - assert( + assertTrue( reasonable.hashCode == reasonableInt.hashCode && reasonable == reasonableInt && reasonableInt == reasonable && @@ -69,7 +69,7 @@ class BigDecimalTest { def refusesNullTest(): Unit = { def isIAE[A](a: => A) = try { a; false } catch { case iae: IllegalArgumentException => true } def isNPE[A](a: => A) = try { a; false } catch { case npe: NullPointerException => true } - assert( + assertTrue( isIAE(new BigDecimal(null: BD, new MC(2))) && isIAE(new BigDecimal(new BD("5.7"), null: MC)) && isNPE(BigDecimal(null: BigInt)) && @@ -85,7 +85,7 @@ class BigDecimalTest { val bd: BigDecimal = 100000 val l: Long = 100000 val d: Double = 100000 - assert( + assertTrue( d.## == l.## && l.## == bd.## && bd.## == bi.## && @@ -109,7 +109,7 @@ class BigDecimalTest { BigDecimal(1) / BigDecimal(10), BigDecimal(10).pow(-1) ) - for (a <- tenths; b <- tenths) assert(a == b, s"$a != $b but both should be 0.1") + for (a <- tenths; b <- tenths) assertEqualsAny(s"$a != $b but both should be 0.1", a, b) } // Motivated by noticing BigDecimal(123456789, mc6) != BigDecimal(123456789L, mc6) @@ -153,12 +153,12 @@ class BigDecimalTest { ) sameRounding.map(_.zipWithIndex).foreach{ case xs => for ((a,i) <- xs; (b,j) <- xs) { - assert(a == b, s"$a != $b (#$i != #$j) but should be the same") - assert(a.## == b.##, s"Hash code mismatch in equal BigDecimals: #$i != #$j") + assertEqualsAny(s"$a != $b (#$i != #$j) but should be the same", a, b) + assertEquals(s"Hash code mismatch in equal BigDecimals: #$i != #$j", a.##, b.##) } } val List(xs, ys) = sameRounding.map(_.zipWithIndex) - for ((a,i) <- xs; (b,j) <- ys) assert(a != b, s"$a == $b (#$i == #$j) but should be different") + for ((a,i) <- xs; (b,j) <- ys) assertNotEqualsAny(s"$a == $b (#$i == #$j) but should be different", a, b) } // This was unexpectedly truncated in 2.10 @@ -168,7 +168,7 @@ class BigDecimalTest { val same = List[Any]( BigInt(text), BigDecimal(text), BigDecimal(new BD(text)) ) - for (a <- same; b <- same) assert(a == b, s"$a != $b but should be the same") + for (a <- same; b <- same) assertEqualsAny(s"$a != $b but should be the same", a, b) } // Tests attempts to make sane the representation of IEEE binary32 and binary64 @@ -217,43 +217,36 @@ class BigDecimalTest { BigDecimal.decimal((0.1f).toDouble) ) for (a <- different; b <- different if (a ne b)) - assert(a != b, "BigDecimal representations of Double mistakenly conflated") + assertNotEquals("BigDecimal representations of Double mistakenly conflated", a, b) } // Make sure hash code agrees with decimal representation of Double - @Test - def test_SI8970(): Unit = { - assert((0.1).## == BigDecimal(0.1).##) - } + @Test def test_SI8970(): Unit = assertEquals((0.1).##, BigDecimal(0.1).##) // Motivated by the problem of MathContext lost @Test - def testMathContext(): Unit = { - def testPrecision(): Unit = { - val p = 1000 - val n = BigDecimal("1.1", MC.UNLIMITED).pow(p) + def testMathContextPrecision(): Unit = { + val p = 1000 + val n = BigDecimal("1.1", MC.UNLIMITED).pow(p) - // BigDecimal(x: Float, mc: MC), which may not do what you want, is deprecated - assert(BigDecimal(1.1d, MC.UNLIMITED).pow(p) == n) - assert(new BigDecimal(new BD("1.1"), MC.UNLIMITED).pow(p) == n) + // BigDecimal(x: Float, mc: MC), which may not do what you want, is deprecated + assertEquals(n, BigDecimal(1.1d, MC.UNLIMITED).pow(p)) + assertEquals(n, new BigDecimal(new BD("1.1"), MC.UNLIMITED).pow(p)) - assert(BigDecimal.decimal(1.1f, MC.UNLIMITED).pow(p) == n) - assert(BigDecimal.decimal(1.1d, MC.UNLIMITED).pow(p) == n) - assert(BigDecimal.decimal(new BD("1.1"), MC.UNLIMITED).pow(p) == n) - - assert((BigDecimal(11, MC.UNLIMITED) / 10).pow(p) == n) - assert((BigDecimal.decimal(11, MC.UNLIMITED) / 10).pow(p) == n) - } + assertEquals(n, BigDecimal.decimal(1.1f, MC.UNLIMITED).pow(p)) + assertEquals(n, BigDecimal.decimal(1.1d, MC.UNLIMITED).pow(p)) + assertEquals(n, BigDecimal.decimal(new BD("1.1"), MC.UNLIMITED).pow(p)) - def testRounded(): Unit = { - // the default rounding mode is HALF_UP - assert((BigDecimal(1.23d, new MC(3)) + BigDecimal("0.005")).rounded == BigDecimal("1.24")) - assert((BigDecimal.decimal(1.23f, new MC(3)) + BigDecimal("0.005")).rounded == BigDecimal("1.24")) - assert((BigDecimal.decimal(1.23d, new MC(3)) + BigDecimal("0.005")).rounded == BigDecimal("1.24")) - } + assertEquals(n, (BigDecimal(11, MC.UNLIMITED) / 10).pow(p)) + assertEquals(n, (BigDecimal.decimal(11, MC.UNLIMITED) / 10).pow(p)) + } - testPrecision() - testRounded() + // the default rounding mode is HALF_UP + @Test + def testMathContextRounded(): Unit = { + assertEquals(BigDecimal("1.24"), (BigDecimal(1.23d, new MC(3)) + BigDecimal("0.005")).rounded) + assertEquals(BigDecimal("1.24"), (BigDecimal.decimal(1.23f, new MC(3)) + BigDecimal("0.005")).rounded) + assertEquals(BigDecimal("1.24"), (BigDecimal.decimal(1.23d, new MC(3)) + BigDecimal("0.005")).rounded) } // Motivated by scala/bug#10882: Operators for BigDecimal don't use value of mc (MathContext) @@ -262,38 +255,34 @@ class BigDecimalTest { def isAE[A](a: => A): Boolean = try { a; false } catch { case e: ArithmeticException => true } val bd128 = BigDecimal("4.2e1000", MC.DECIMAL128) - assert(bd128 + 10 == bd128) - assert(bd128 - 10 == bd128) - assert(bd128 + BigDecimal("1e100", MC.UNLIMITED) == bd128) - assert(bd128 - BigDecimal("1e100", MC.UNLIMITED) == bd128) - assert(bd128.quot(BigDecimal("1e100", MC.UNLIMITED)) == BigDecimal("4.2e900", MC.DECIMAL128)) - assert(isAE(bd128.quot(BigDecimal("1e100", MC.UNLIMITED) + 1))) - assert(isAE(bd128 % (BigDecimal("1e100", MC.UNLIMITED) + 1))) - assert(isAE(bd128 /% (BigDecimal("1e100", MC.UNLIMITED) + 1))) + assertTrue(bd128 + 10 == bd128) + assertTrue(bd128 - 10 == bd128) + assertTrue(bd128 + BigDecimal("1e100", MC.UNLIMITED) == bd128) + assertTrue(bd128 - BigDecimal("1e100", MC.UNLIMITED) == bd128) + assertTrue(bd128.quot(BigDecimal("1e100", MC.UNLIMITED)) == BigDecimal("4.2e900", MC.DECIMAL128)) + assertTrue(isAE(bd128.quot(BigDecimal("1e100", MC.UNLIMITED) + 1))) + assertTrue(isAE(bd128 % (BigDecimal("1e100", MC.UNLIMITED) + 1))) + assertTrue(isAE(bd128 /% (BigDecimal("1e100", MC.UNLIMITED) + 1))) val bdUnlimited = BigDecimal("4.2e1000", MC.UNLIMITED) - assert(bdUnlimited + 10 > bdUnlimited) - assert(bdUnlimited - 10 < bdUnlimited) - assert(bdUnlimited + BigDecimal("1e100", MC.DECIMAL128) > bdUnlimited) - assert(bdUnlimited - BigDecimal("1e100", MC.DECIMAL128) < bdUnlimited) - assert(bdUnlimited.quot(BigDecimal("1e100", MC.DECIMAL128)) == BigDecimal("4.2e900", MC.UNLIMITED)) + assertTrue(bdUnlimited + 10 > bdUnlimited) + assertTrue(bdUnlimited - 10 < bdUnlimited) + assertTrue(bdUnlimited + BigDecimal("1e100", MC.DECIMAL128) > bdUnlimited) + assertTrue(bdUnlimited - BigDecimal("1e100", MC.DECIMAL128) < bdUnlimited) + assertTrue(bdUnlimited.quot(BigDecimal("1e100", MC.DECIMAL128)) == BigDecimal("4.2e900", MC.UNLIMITED)) } @Test def testIsComparable(): Unit = { - assert(BigDecimal(0.1).isInstanceOf[java.lang.Comparable[_]]) + assertTrue(BigDecimal(0.1).isInstanceOf[java.lang.Comparable[_]]) } - - - @Test - def testBigDecimalSumInList(): Unit = { - + // trick sum into using foldLeft, viz, because the size is unknown + @Test def testBigDecimalSumInList(): Unit = { val bds = List( BigDecimal("1000000000000000000000000.1", MC.UNLIMITED), BigDecimal("9.0000000000000000000000009", MC.UNLIMITED)) - assert(bds.sum == BigDecimal("1000000000000000000000009.1000000000000000000000009", MC.UNLIMITED)) - + assertEquals(BigDecimal("1000000000000000000000009.1000000000000000000000009", MC.UNLIMITED), bds.sum) } @Test @@ -303,17 +292,31 @@ class BigDecimalTest { BigDecimal("9.00000000000000000000000091", MC.UNLIMITED)) val prod = bds.foldLeft(BigDecimal(1, MC.UNLIMITED))(_ * _) - assert(prod == BigDecimal("9000000000000000000000001.810000000000000000000000091", MC.UNLIMITED)) + assertEquals(BigDecimal("9000000000000000000000001.810000000000000000000000091", MC.UNLIMITED), prod) + assertEquals(prod, bds.product) + } - assert(bds.product == prod) + // trick sum into using foldLeft, viz, because the size is unknown, oh wait this iterator knows its size... + @Test def `sum of iterator of BigDecimal`: Unit = { + val bds = Iterator( + BigDecimal("1000000000000000000000000.1", MC.UNLIMITED), + BigDecimal("9.0000000000000000000000009", MC.UNLIMITED)) + assertEquals(BigDecimal("1000000000000000000000009.1000000000000000000000009", MC.UNLIMITED), bds.sum) + } + // trick sum into using reduce, viz, because the size is known + @Test def `sum of vector of BigDecimal`: Unit = { + val bds = Vector( + BigDecimal("1000000000000000000000000.1", MC.UNLIMITED), + BigDecimal("9.0000000000000000000000009", MC.UNLIMITED)) + assertEquals(BigDecimal("1000000000000000000000009.1000000000000000000000009", MC.UNLIMITED), bds.sum) } @Test def testImplicitBigDecimalConversionJavaToScalaHandlesNull(): Unit = { val bdNull: BigDecimal = (null: java.math.BigDecimal): BigDecimal - assert(bdNull == null) + assertNull(bdNull) val bdValue: BigDecimal = (BD.ONE: java.math.BigDecimal): BigDecimal - assert(bdValue.bigDecimal == BD.ONE) + assertEquals(BD.ONE, bdValue.bigDecimal) } } diff --git a/test/junit/scala/reflect/internal/util/AbstractFileClassLoaderTest.scala b/test/junit/scala/reflect/internal/util/AbstractFileClassLoaderTest.scala index c57ca039a144..0248aebdb990 100644 --- a/test/junit/scala/reflect/internal/util/AbstractFileClassLoaderTest.scala +++ b/test/junit/scala/reflect/internal/util/AbstractFileClassLoaderTest.scala @@ -10,11 +10,11 @@ class AbstractFileClassLoaderTest { import scala.reflect.io._ import scala.io.Source - import scala.io.Codec.UTF8 + import scala.io.Codec, Codec.UTF8 import scala.reflect.io.Streamable import java.net.{ URLClassLoader, URL } - implicit def `we love utf8` = UTF8 + implicit def `we love utf8`: Codec = UTF8 implicit class `abs file ops`(f: AbstractFile) { def writeContent(s: String): Unit = Streamable.closing(f.bufferedOutput)(os => os write s.getBytes(UTF8.charSet)) } diff --git a/test/junit/scala/reflect/io/ZipArchiveTest.scala b/test/junit/scala/reflect/io/ZipArchiveTest.scala index ec7ede4348b6..aaa14018b75a 100644 --- a/test/junit/scala/reflect/io/ZipArchiveTest.scala +++ b/test/junit/scala/reflect/io/ZipArchiveTest.scala @@ -8,7 +8,6 @@ import java.util.jar.{Attributes, Manifest, JarEntry, JarOutputStream} import org.junit.Assert._ import org.junit.Test -import scala.reflect.internal.util.ScalaClassLoader import scala.tools.testkit.AssertUtil._ import scala.tools.testkit.ForDeletion import scala.tools.testkit.Releasables._ @@ -50,12 +49,12 @@ class ZipArchiveTest { assertThrown[IOException](_.getMessage.contains(f.toString))(fza.iterator) } - private def manifestAt(location: URI): URL = ScalaClassLoader.fromURLs(List(location.toURL), null).getResource("META-INF/MANIFEST.MF"); + private def manifestAt(location: Path): URL = URI.create(s"jar:file:${location.toUri.getPath}!/META-INF/MANIFEST.MF").toURL // ZipArchive.fromManifestURL(URL) @Test def `manifest resources just works`(): Unit = { val jar = createTestJar() - Using.resources(ForDeletion(jar), new ManifestResources(manifestAt(jar.toUri))) { (_, archive) => + Using.resources(ForDeletion(jar), new ManifestResources(manifestAt(jar.toAbsolutePath))) { (_, archive) => val it = archive.iterator assertTrue(it.hasNext) val f = it.next() diff --git a/test/junit/scala/runtime/FloatBoxingTest.scala b/test/junit/scala/runtime/FloatBoxingTest.scala index 4a4adbc2ee94..ffe8ca3b560e 100644 --- a/test/junit/scala/runtime/FloatBoxingTest.scala +++ b/test/junit/scala/runtime/FloatBoxingTest.scala @@ -45,7 +45,7 @@ class FloatBoxingTest extends SideEffectTest with AllocationTest { assertEquals(57, nonAllocating(value.byteValue())) } @Test def str(): Unit = { - val cost = allocationInfo(java.lang.Double.toString(value)) + val cost = allocationInfo(java.lang.Float.toString(value)) assertEquals("12345.0", exactAllocates(cost.min)(value.toString())) } @Test def hash1(): Unit = { diff --git a/test/junit/scala/tools/nsc/MainRunnerTest.scala b/test/junit/scala/tools/nsc/MainRunnerTest.scala new file mode 100644 index 000000000000..5520a8d9bf4e --- /dev/null +++ b/test/junit/scala/tools/nsc/MainRunnerTest.scala @@ -0,0 +1,26 @@ +package scala.tools.nsc + +import org.junit.Assert._ +import org.junit.Test + +class MainRunnerTest { + @Test + def `command reports no class`: Unit = { + import GenericRunnerCommand._ + var message: String = "" + assertEquals(Error, new GenericRunnerCommand(List("Junk"), err => message = err).howToRun) + assertEquals("No such file or class on classpath: Junk", message) + } + object TestGenericRunner extends MainGenericRunner { + override def errorFn(str: String, e: Option[Throwable], isFailure: Boolean): Boolean = { + assertTrue(isFailure) + !isFailure + } + } + @Test + def `scala Junk should fail`: Unit = + assertFalse(TestGenericRunner.process(Array("Junk"))) + @Test + def `scala junk.jar should fail`: Unit = + assertFalse(TestGenericRunner.process(Array("junk.jar"))) +} diff --git a/test/junit/scala/tools/nsc/backend/jvm/BytecodeTest.scala b/test/junit/scala/tools/nsc/backend/jvm/BytecodeTest.scala index 4d62bf396115..ccab9555aecb 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/BytecodeTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/BytecodeTest.scala @@ -317,9 +317,8 @@ class BytecodeTest extends BytecodeTesting { ("x$1", true), // captured, assigned in constructor ("y$1", true) // captured ), fs) - val assignedInConstr = getMethod(k, "").instructions.filter(_.opcode == Opcodes.PUTFIELD) map { - case f: Field => f.name - case _ => ??? // @unchecked + val assignedInConstr = getMethod(k, "").instructions.collect { + case f: Field if f.opcode == Opcodes.PUTFIELD => f.name } assertEquals(List("$outer", "x$1", "y$1"), assignedInConstr.sorted) } diff --git a/test/junit/scala/tools/nsc/backend/jvm/PerRunInitTest.scala b/test/junit/scala/tools/nsc/backend/jvm/PerRunInitTest.scala index bd3b313548f8..64d7a764c327 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/PerRunInitTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/PerRunInitTest.scala @@ -77,6 +77,7 @@ abstract class PerRunInitTest { def clearCaches() = underTest.global.perRunCaches.clearAll() + @annotation.nowarn("cat=deprecation") def doGc() = { System.gc() System.runFinalization() diff --git a/test/junit/scala/tools/nsc/classpath/MultiReleaseJarTest.scala b/test/junit/scala/tools/nsc/classpath/MultiReleaseJarTest.scala index 542408f6b1cd..0736e53cdf05 100644 --- a/test/junit/scala/tools/nsc/classpath/MultiReleaseJarTest.scala +++ b/test/junit/scala/tools/nsc/classpath/MultiReleaseJarTest.scala @@ -64,7 +64,8 @@ class MultiReleaseJarTest extends BytecodeTesting { } try { Assert.assertTrue(lookup("java.lang.invoke.LambdaMetafactory", "8")) - Assert.assertFalse(lookup("java.lang.invoke.LambdaMetafactory", "7")) + if (!Properties.isJavaAtLeast("20")) + Assert.assertFalse(lookup("java.lang.invoke.LambdaMetafactory", "7")) Assert.assertTrue(lookup("java.lang.invoke.LambdaMetafactory", "9")) } finally { cleanup.close() diff --git a/test/junit/scala/tools/nsc/classpath/ZipAndJarFileLookupFactoryTest.scala b/test/junit/scala/tools/nsc/classpath/ZipAndJarFileLookupFactoryTest.scala index f152d27473f4..dee4611ed227 100644 --- a/test/junit/scala/tools/nsc/classpath/ZipAndJarFileLookupFactoryTest.scala +++ b/test/junit/scala/tools/nsc/classpath/ZipAndJarFileLookupFactoryTest.scala @@ -6,7 +6,6 @@ import org.junit.Test import java.net.{URI, URL} import java.nio.file._ import java.nio.file.attribute.FileTime -import scala.reflect.internal.util.ScalaClassLoader import scala.reflect.io.AbstractFile import scala.tools.testkit.ForDeletion import scala.tools.testkit.Releasables._ @@ -82,10 +81,10 @@ class ZipAndJarFileLookupFactoryTest { () } } - def manifestAt(location: URI): URL = ScalaClassLoader.fromURLs(List(location.toURL), null).getResource("META-INF/MANIFEST.MF"); + def manifestAt(location: Path): URL = URI.create(s"jar:file:${location.toUri.getPath}!/META-INF/MANIFEST.MF").toURL val j = createTestJar(); - Using.resources(ForDeletion(j), new ManifestResources(manifestAt(j.toUri)), new CloseableRegistry) { (_, archive, closeableRegistry) => + Using.resources(ForDeletion(j), new ManifestResources(manifestAt(j.toAbsolutePath)), new CloseableRegistry) { (_, archive, closeableRegistry) => val settings = new Settings val cp = ZipAndJarClassPathFactory.create(archive, settings, closeableRegistry) assertTrue(cp.findClass("foo").isDefined) diff --git a/test/junit/scala/tools/nsc/settings/SettingsTest.scala b/test/junit/scala/tools/nsc/settings/SettingsTest.scala index 3d63d7fe75c6..ce46108f09db 100644 --- a/test/junit/scala/tools/nsc/settings/SettingsTest.scala +++ b/test/junit/scala/tools/nsc/settings/SettingsTest.scala @@ -354,6 +354,54 @@ class SettingsTest { assertFalse(s.isInfo) assertTrue(s.printArgs.isSetByUser) } + @Test def `name-based phases setting accepts tilde prefix`: Unit = { + val start = new Phase(null) { def name = "start"; def run() = () } + val chunk = new Phase(start) { def name = "chunker"; def run() = () } + val clean = new Phase(chunk) { def name = "clean"; def run() = () } + val go = new Phase(clean) { def name = "go"; def run() = () } + val end = new Phase(go) { def name = "end"; def run() = () } + val s = new MutableSettings(msg => throw new IllegalArgumentException(msg)) + val ps = new s.PhasesSetting("-Yp", descr="", default="") + s.allSettings(ps.name) = ps + val args = List("-Yp:clean,~chunker,3") + val (ok, residual) = s.processArguments(args, processAll = true) + assertTrue(ok) + assertTrue(residual.isEmpty) + assertTrue(ps.contains("clean")) + assertFalse(ps.contains("chunker")) + assertTrue(ps.contains("~chunker")) + assertFalse(ps.contains("start")) + assertFalse(ps.contains("end")) + assertTrue(ps.containsPhase(clean)) + assertTrue(ps.containsPhase(chunk)) + assertTrue(ps.containsPhase(start)) + assertTrue(ps.containsPhase(start.next)) + assertTrue(ps.contains(s"~${start.next.name}")) + assertTrue(ps.containsPhase(go)) + assertTrue(ps.containsId(go.id)) + assertFalse(ps.containsPhase(end)) + assertFalse(ps.containsId(end.id)) + } + @Test def `phases setting accepts all or underscore`: Unit = { + val start = new Phase(null) { def name = "start"; def run() = () } + def check(args: String*): MutableSettings#PhasesSetting = { + val s = new MutableSettings(msg => throw new IllegalArgumentException(msg)) + val ps = new s.PhasesSetting("-Yp", descr="", default="") + s.allSettings(ps.name) = ps + val (ok, residual) = s.processArguments(args.toList, processAll = true) + assertTrue(ok) + assertTrue(residual.isEmpty) + ps + } + assertTrue(check("-Yp:start").containsPhase(start)) + assertTrue(check("-Yp:start").contains("start")) + assertTrue(check("-Yp:start").containsName("start")) + assertTrue(check("-Yp:all").containsPhase(start)) + assertTrue(check("-Yp:all").contains("start")) + assertFalse(check("-Yp:all").containsName("start")) + assertTrue(check("-Yp:_").containsPhase(start)) + assertTrue(check("-Yp:junk,_").containsPhase(start)) + } } object SettingsTest { import language.implicitConversions diff --git a/test/junit/scala/tools/nsc/settings/TargetTest.scala b/test/junit/scala/tools/nsc/settings/TargetTest.scala index 87eb32db9a65..abf6d9ea67c8 100644 --- a/test/junit/scala/tools/nsc/settings/TargetTest.scala +++ b/test/junit/scala/tools/nsc/settings/TargetTest.scala @@ -18,12 +18,18 @@ import org.junit.runner.RunWith import org.junit.runners.JUnit4 import scala.collection.mutable.ListBuffer +import scala.tools.nsc.settings.StandardScalaSettings._ +import scala.tools.testkit.AssertUtil.assertFails import scala.util.Properties.isJavaAtLeast import scala.util.Try @RunWith(classOf[JUnit4]) class TargetTest { + private def lately[A](body: => A): Unit = if (isJavaAtLeast(11)) body: Unit + + private def eightly[A](body: => A): Unit = if (!isJavaAtLeast(9)) body: Unit + @Test def testSettingTargetSetting(): Unit = { def goodVersion(v: String): Boolean = Try(isJavaAtLeast(v)).getOrElse(false) def check(in: String, expect: String) = if (goodVersion(expect)) checkSuccess(in, expect) else checkFail(in) @@ -53,33 +59,46 @@ class TargetTest { checkFail("-target:1.9") // it's not Java 1.9, you reprobates! checkFail("-target:jvm-1.9") - check("-target:jvm-10", "10") - check("-target:10", "10") - - check("-target:jvm-11", "11") - check("-target:11", "11") - - check("-target:jvm-12", "12") - check("-target:12", "12") - - // (scene missing) - - check("-target:jvm-16", "16") - check("-target:16", "16") - - check("-target:jvm-17", "17") - check("-target:17", "17") - - check("-target:jvm-18", "18") - check("-target:18", "18") - - check("-target:jvm-19", "19") - check("-target:19", "19") - + (MinTargetVersion to MaxTargetVersion).map(_.toString).foreach { v => + check(s"-target:jvm-$v", v) + check(s"-target:$v", v) + } + checkFail(s"-target:jvm-${MaxTargetVersion+1}") + checkFail(s"-target:${MaxTargetVersion+1}") checkFail("-target:jvm-6") // no longer checkFail("-target:jvm-7") // no longer - checkFail("-target:jvm-20") // not yet... checkFail("-target:jvm-3000") // not in our lifetime checkFail("-target:msil") // really? } + @Test def `respect user target`: Unit = lately { + val settings = new Settings(err => fail(s"Error output: $err")) + val (ok, _) = settings.processArgumentString("--release:11 --target:8") + assertTrue(ok) + assertEquals("8", settings.target.value) + assertEquals("11", settings.release.value) + assertEquals("8", settings.targetValue) + assertEquals(Some("11"), settings.releaseValue) + } + @Test def `target is release if specified`: Unit = lately { + val settings = new Settings(err => fail(s"Error output: $err")) + val (ok, _) = settings.processArgumentString("--release:11") + assertTrue(ok) + assertEquals("8", settings.target.value) // default + assertEquals(None, settings.target.valueSetByUser) + assertEquals("11", settings.release.value) + assertEquals("11", settings.targetValue) // because --release:11 + assertEquals(Some("11"), settings.releaseValue) + } + @Test def `disrespect user target`: Unit = lately { + val settings = new Settings(err => fail(s"Error output: $err")) + assertFails(_.contains("-release cannot be less than -target")) { + settings.processArgumentString("--release:8 --target:11") + } + } + @Test def `cannot bump release on jdk 8`: Unit = eightly { + val settings = new Settings(err => fail(s"Error output: $err")) + assertFails(_.contains("'9' is not a valid choice for '-release'")) { + settings.processArgumentString("--release:9") + } + } } diff --git a/test/junit/scala/tools/nsc/symtab/SymbolTableTest.scala b/test/junit/scala/tools/nsc/symtab/SymbolTableTest.scala index 8df5eb34eaf0..3f95929166ef 100644 --- a/test/junit/scala/tools/nsc/symtab/SymbolTableTest.scala +++ b/test/junit/scala/tools/nsc/symtab/SymbolTableTest.scala @@ -49,4 +49,33 @@ class SymbolTableTest { import symbolTable._ assertEquals(NoSymbol, NoSymbol.outerClass) } + + @Test def t12702_glb(): Unit = { + import symbolTable._ + import SymbolTableTest.t12702._ + val t1 = typeOf[IOS.type] + val t2 = { + val sd = typeOf[SD] + sd.memberType(sd.member(TermName("mFI"))).finalResultType + } + // t1: Test.IOS.type + // t2: Test.MFI{type +ST = S} + + // Ends up in `throw GlbFailure` in glb => Null + assertTrue(definitions.NullTpe =:= glb(t1 :: t2 :: Nil)) + } +} + +object SymbolTableTest { + object t12702 { + import scala.language.existentials + trait MFSS[X <: MFSS[_]] + trait CS extends MFSS[CS] + trait MFI { type ST } + case class MFSD[S](mFI: MFI {type ST = S}) + case object IOS extends MFI { type ST = CS } + type SD = MFSD[S] forSome { + type S <: MFSS[S] + } + } } diff --git a/test/junit/scala/tools/nsc/transform/patmat/SolvingTest.scala b/test/junit/scala/tools/nsc/transform/patmat/SolvingTest.scala index b5289263f390..fa7ce9091d96 100644 --- a/test/junit/scala/tools/nsc/transform/patmat/SolvingTest.scala +++ b/test/junit/scala/tools/nsc/transform/patmat/SolvingTest.scala @@ -5,6 +5,7 @@ import org.junit.Test import scala.collection.mutable import scala.reflect.internal.util.Position +import scala.tools.nsc.transform.patmat.Logic.LogicLinkedHashSet import scala.tools.nsc.{Global, Settings} object TestSolver extends Logic with Solving { @@ -578,8 +579,8 @@ class SolvingTest { def pairWiseEncoding(ops: List[Sym]) = { And(ops.combinations(2).collect { - case a :: b :: Nil => Or(Not(a), Not(b)) - }.toSet[TestSolver.TestSolver.Prop]) + case a :: b :: Nil => Or(Not(a), Not(b)): Prop + }.to(LogicLinkedHashSet)) } @Test @@ -593,5 +594,3 @@ class SolvingTest { assertEquals(expected.toSet, actual.toSet) } } - - diff --git a/test/junit/scala/tools/nsc/typechecker/TreeAttachmentTest.scala b/test/junit/scala/tools/nsc/typechecker/TreeAttachmentTest.scala new file mode 100644 index 000000000000..7374c3246077 --- /dev/null +++ b/test/junit/scala/tools/nsc/typechecker/TreeAttachmentTest.scala @@ -0,0 +1,46 @@ +package scala.tools.nsc.typechecker + +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +import scala.tools.testkit.BytecodeTesting + +@RunWith(classOf[JUnit4]) +class TreeAttachmentTest extends BytecodeTesting { + + import compiler.global._ + + override def compilerArgs: String = "-Ystop-after:typer" + + @Test + def namedApplyInfoAttachmentPreserved(): Unit = { + def compile(testMethodCode: String, call: String): Option[analyzer.NamedApplyInfo] = { + val code = + s""" + |class C { + | $testMethodCode + | def s = "" + | def useTest = $call + |} + |""".stripMargin + + compiler.compileClasses(code) + val run = compiler.newRun() + run.compileUnits(newCompilationUnit(code) :: Nil, run.parserPhase) + val tree : Tree = run.units.next().body + val block: Tree = tree.collect { case b: Block => b }.last + block.attachments.get[analyzer.NamedApplyInfo] + } + + + def vargss(x: Option[analyzer.NamedApplyInfo]): List[List[String]] = x.map(x => mmap(x.vargss)(_.symbol.name.toString)).getOrElse(Nil) + + assertEquals(None, compile("def test(a: Any, b: Any)(c: Any)(implicit d: DummyImplicit) = 0", "test(s, s)(s)")) + + val expected = List(List("x$2", "x$1"), List("x$3")) + assertEquals(expected, vargss(compile("def test(a: Any, b: Any)(c: Any) = 0", "test(b = s, a = s)(s)"))) + assertEquals(expected, vargss(compile("def test(a: Any, b: Any)(c: Any)(implicit d: DummyImplicit) = 0", "test(b = s, a = s)(s)"))) + } +} diff --git a/test/junit/scala/tools/nsc/typechecker/TypedTreeTest.scala b/test/junit/scala/tools/nsc/typechecker/TypedTreeTest.scala index 0c0a6b96f286..a90ca57a0531 100644 --- a/test/junit/scala/tools/nsc/typechecker/TypedTreeTest.scala +++ b/test/junit/scala/tools/nsc/typechecker/TypedTreeTest.scala @@ -4,10 +4,57 @@ import org.junit.Assert.{assertEquals, assertNotEquals} import org.junit.Test import scala.tools.testkit.BytecodeTesting +import scala.tools.testkit.BytecodeTesting._ class TypedTreeTest extends BytecodeTesting { override def compilerArgs = "-Ystop-after:typer" + @Test def t12703(): Unit = { + import compiler._ + val a = + """object Foo { + | sealed trait A + | sealed trait B + | final case class C(x: Int) extends A with B + |} + |""".stripMargin + val b = + """object T { + | import Foo._ + | def f(a: A) = a match { + | case b: B => 0 + | case _ => 1 + | } + |} + |""".stripMargin + + def check(a: String, b: String) = { + val r = newRun() + r.compileSources(makeSourceFile(a, "A.scala") :: makeSourceFile(b, "B.scala") :: Nil) + checkReport() + } + + check(a, b) + check(b, a) + } + + @Test + def keepBlockUndetparams(): Unit = { + import compiler.global._ + val code = + """class C { + | def f = Option(Map("a" -> "c")).getOrElse { println(); Map.empty } // was: Map[_ >: String with K, Any] + | def g = Option(Map("a" -> "c")).getOrElse { Map.empty } + |} + |""".stripMargin + val run = compiler.newRun() + run.compileSources(List(BytecodeTesting.makeSourceFile(code, "UnitTestSource.scala"))) + val t: Tree = run.units.next().body + val c: Symbol = t.collect { case cd: ClassDef => cd.symbol }.head + for (m <- List("f", "g")) + assertEquals(c.info.member(TermName("f")).tpe.toString, "scala.collection.immutable.Map[String,String]") + } + @Test def constantFoldedOriginalTreeAttachment(): Unit = { val code = diff --git a/test/junit/scala/tools/testkit/AssertUtilTest.scala b/test/junit/scala/tools/testkit/AssertUtilTest.scala index d618ecb9ef16..9afa8d7e5618 100644 --- a/test/junit/scala/tools/testkit/AssertUtilTest.scala +++ b/test/junit/scala/tools/testkit/AssertUtilTest.scala @@ -122,4 +122,25 @@ class AssertUtilTest { assertEquals("00000000 f0 90 90 80 |𐐀.|", hexdump("\ud801\udc00").next()) } */ + + @Test def `assertSameElements reports fail of IterableOnce`: Unit = { + assertFails(_ == "expected: but was:") { + assertSameElements(List(1, 2, 3), Iterator(1, 2, 3, 4)) + } + assertFails(_ == "expected: but was:") { + assertSameElements(List(1, 2, 3), Set(1, 2, 3, 4)) + } + assertFails(_ == "expected: but was:") { + assertSameElements(Vector(1, 2, 3), Iterator(1, 2, 3, 4)) + } + assertFails(_ == "force use of canonical method expected: but was:") { + assertSameElements(Vector(1, 2, 3), Iterator(1, 2, 3, 4).to(Iterable), message = "force use of canonical method") + } + } + @Test def `assertSameElements supports Array directly`: Unit = { + assertSameElements(Array(42), Array(42)) + assertFails(_ == "expected: but was:") { + assertSameElements(Array(17), Array(42)) + } + } } diff --git a/test/junit/scala/util/TryTest.scala b/test/junit/scala/util/TryTest.scala index c505abd77596..4981a789f63e 100644 --- a/test/junit/scala/util/TryTest.scala +++ b/test/junit/scala/util/TryTest.scala @@ -64,6 +64,7 @@ class TryTest { assertEquals(Failure(e), Failure[Int](e).map(_ => throw e2)) } + @deprecated("ThreadDeath is deprecated on JDK 20", "") @Test def `map when there is a fatal exception`(): Unit = { val e3 = new ThreadDeath assertThrows[ThreadDeath] { @@ -82,6 +83,7 @@ class TryTest { val e2 = new Exception assertEquals(Failure(e), Failure[Int](e).flatMap[Int](_ => throw e2)) } + @deprecated("ThreadDeath is deprecated on JDK 20", "") @Test def `flatMap when there is a fatal exception`(): Unit = { val e3 = new ThreadDeath assertThrows[ThreadDeath] { diff --git a/test/junit/scala/util/UsingTest.scala b/test/junit/scala/util/UsingTest.scala index f68830934b57..eb7350159cdc 100644 --- a/test/junit/scala/util/UsingTest.scala +++ b/test/junit/scala/util/UsingTest.scala @@ -7,6 +7,7 @@ import scala.annotation.unused import scala.reflect.ClassTag import scala.util.control.ControlThrowable +@deprecated("ThreadDeath is deprecated on JDK 20", "") class UsingTest { import UsingTest._ @@ -744,6 +745,7 @@ class UsingTest { } } +@deprecated("ThreadDeath is deprecated on JDK 20", "") object UsingTest { final class ClosingVMError(message: String) extends VirtualMachineError(message) final class UsingVMError(message: String) extends VirtualMachineError(message) diff --git a/test/macro-annot/run/scopes/scopes_2.scala b/test/macro-annot/run/scopes/scopes_2.scala index 451931c14ee8..783c729aef1f 100644 --- a/test/macro-annot/run/scopes/scopes_2.scala +++ b/test/macro-annot/run/scopes/scopes_2.scala @@ -2,7 +2,7 @@ object Test extends App { def assertEquals(a: Any, b: Any): Unit = { assert(a == b, s"$a != $b") } - implicit val x = 42 + implicit val x: Int = 42 @explorer object C // @Test def toplevel: Unit = diff --git a/test/scalacheck/scala/collection/immutable/NumericRangeProperties.scala b/test/scalacheck/scala/collection/immutable/NumericRangeProperties.scala new file mode 100644 index 000000000000..ce758ef7546f --- /dev/null +++ b/test/scalacheck/scala/collection/immutable/NumericRangeProperties.scala @@ -0,0 +1,56 @@ +package scala +package collection.immutable + +import org.scalacheck._ + +import scala.util.Try + +class NumericRangeProperties extends Properties("immutable.NumericRange") { + import Prop._ + import NumericRangeProperties._ + + property("same length when take and drop with a specific amount (Byte)") = forAll { (r: NumericRange[Byte], amount: Int) => + Try(r.length).isSuccess ==> { + r.take(amount).length + r.drop(amount).length == r.length + } + } + + property("same length when take and drop with a specific amount (Short)") = forAll { (r: NumericRange[Short], amount: Int) => + Try(r.length).isSuccess ==> { + r.take(amount).length + r.drop(amount).length == r.length + } + } + + property("same length when take and drop with a specific amount (Int)") = forAll { (r: NumericRange[Int], amount: Int) => + Try(r.length).isSuccess ==> { + r.take(amount).length + r.drop(amount).length == r.length + } + } + + // This is commented intentionally, because of a bug in NumericRange.count, + // which makes the length unstable for property based testing: + // e.g., NumericRange(1L, -9223372036854775808L, -1L).length == 1 +// property("same length when take and drop with a specific amount (Long)") = forAll { (r: NumericRange[Long], amount: Int) => +// Try(r.length).isSuccess ==> { +// r.take(amount).length + r.drop(amount).length == r.length +// } +// } + + property("same length when take and drop with a specific amount (BigInt)") = forAll { (r: NumericRange[BigInt], amount: Int) => + Try(r.length).isSuccess ==> { + r.take(amount).length + r.drop(amount).length == r.length + } + } +} + +object NumericRangeProperties { + private implicit def arbitraryNumericRange[T](implicit num: Integral[T], tGen: Arbitrary[T]): Arbitrary[NumericRange[T]] = + Arbitrary( + for { + start <- tGen.arbitrary + end <- tGen.arbitrary + step <- tGen.arbitrary filterNot (num.equiv(_, num.zero)) + incl <- Gen.oneOf(true, false) + } yield if (incl) NumericRange.inclusive(start, end, step) else NumericRange(start, end, step) + ) +} \ No newline at end of file diff --git a/test/scalacheck/scala/collection/mutable/MutableTreeMap.scala b/test/scalacheck/scala/collection/mutable/MutableTreeMap.scala index 5b06a35725be..adae13349733 100644 --- a/test/scalacheck/scala/collection/mutable/MutableTreeMap.scala +++ b/test/scalacheck/scala/collection/mutable/MutableTreeMap.scala @@ -31,8 +31,8 @@ trait Generators { } yield mutable.TreeMap(keys zip values: _*) } - implicit def arbRedBlackTree[A: Arbitrary: Ordering, B: Arbitrary] = Arbitrary(genRedBlackTree[A, B]) - implicit def arbTreeMap[A: Arbitrary: Ordering, B: Arbitrary] = Arbitrary(genTreeMap[A, B]) + implicit def arbRedBlackTree[A: Arbitrary: Ordering, B: Arbitrary]: Arbitrary[RB.Tree[A,B]] = Arbitrary(genRedBlackTree[A, B]) + implicit def arbTreeMap[A: Arbitrary: Ordering, B: Arbitrary]: Arbitrary[mutable.TreeMap[A,B]] = Arbitrary(genTreeMap[A, B]) } object RedBlackTreeProperties extends Properties("mutable.RedBlackTree") with Generators { @@ -218,7 +218,7 @@ object MutableTreeMapProjectionProperties extends Properties("mutable.TreeMapPro type K = String type V = Int - implicit val ord = implicitly[Ordering[K]] + private val ord = implicitly[Ordering[K]] def in(key: K, from: Option[K], until: Option[K]) = from.fold(true)(_ <= key) && until.fold(true)(_ > key) diff --git a/test/scalacheck/scala/collection/mutable/MutableTreeSet.scala b/test/scalacheck/scala/collection/mutable/MutableTreeSet.scala index 39a44ae453e7..68ee90462935 100644 --- a/test/scalacheck/scala/collection/mutable/MutableTreeSet.scala +++ b/test/scalacheck/scala/collection/mutable/MutableTreeSet.scala @@ -117,7 +117,7 @@ object MutableTreeSetProperties extends Properties("mutable.TreeSet") { object MutableTreeSetProjectionProperties extends Properties("mutable.TreeSetProjection") { type K = String - implicit val ord = implicitly[Ordering[K]] + private val ord = implicitly[Ordering[K]] def in(key: K, from: Option[K], until: Option[K]) = from.fold(true)(_ <= key) && until.fold(true)(_ > key) diff --git a/test/scalacheck/scala/math/BigIntProperties.scala b/test/scalacheck/scala/math/BigIntProperties.scala index d036719b368f..a5107c4bf425 100644 --- a/test/scalacheck/scala/math/BigIntProperties.scala +++ b/test/scalacheck/scala/math/BigIntProperties.scala @@ -5,6 +5,7 @@ import Arbitrary.arbitrary import org.scalacheck.Prop._ import java.math.BigInteger import java.lang.Character +import scala.annotation.nowarn import scala.util.Random object BigIntProperties extends Properties("BigInt") { @@ -95,7 +96,7 @@ object BigIntProperties extends Properties("BigInt") { property("isValidLong") = forAll { (l: Long) => BigInt(l).isValidLong } property("isValidLong") = !BigInt("9223372036854775808").isValidLong property("isValidLong") = !BigInt("-9223372036854775809").isValidLong - property("isWhole") = forAll { (bi: BigInt) => bi.isWhole } + property("isWhole") = forAll { (bi: BigInt) => bi.isWhole: @nowarn } property("underlying") = forAll(bigInteger) { bi => BigInt(bi).underlying ?= bi } property("equals") = forAll(bigInteger, bigInteger) { (x, y) => (x == y) ?= (BigInt(x) equals BigInt(y)) } property("compare") = forAll(bigInteger, bigInteger) { (x, y) => x.compareTo(y) ?= BigInt(x).compare(y) } diff --git a/test/scalacheck/scala/reflect/quasiquotes/ArbitraryTreesAndNames.scala b/test/scalacheck/scala/reflect/quasiquotes/ArbitraryTreesAndNames.scala index 19032a2d0fb7..bc6e6089c231 100644 --- a/test/scalacheck/scala/reflect/quasiquotes/ArbitraryTreesAndNames.scala +++ b/test/scalacheck/scala/reflect/quasiquotes/ArbitraryTreesAndNames.scala @@ -267,8 +267,8 @@ trait ArbitraryTreesAndNames { def genTreeIsTypeWrapped(size: Int) = for(tit <- genTreeIsType(size)) yield TreeIsType(tit) - implicit val liftTreeIsTerm = Liftable[TreeIsTerm] { _.tree } - implicit val liftTreeIsType = Liftable[TreeIsType] { _.tree } + implicit val liftTreeIsTerm: Liftable[TreeIsTerm] = Liftable[TreeIsTerm] { _.tree } + implicit val liftTreeIsType: Liftable[TreeIsType] = Liftable[TreeIsType] { _.tree } implicit def treeIsTerm2tree(tit: TreeIsTerm): Tree = tit.tree implicit def treeIsType2tree(tit: TreeIsType): Tree = tit.tree diff --git a/test/scalacheck/scala/tools/nsc/scaladoc/HtmlFactoryTest.scala b/test/scalacheck/scala/tools/nsc/scaladoc/HtmlFactoryTest.scala index 2673c7ea4d70..67c4d5c3c99b 100644 --- a/test/scalacheck/scala/tools/nsc/scaladoc/HtmlFactoryTest.scala +++ b/test/scalacheck/scala/tools/nsc/scaladoc/HtmlFactoryTest.scala @@ -538,7 +538,7 @@ object HtmlFactoryTest extends Properties("HtmlFactory") { //println(files) // class - { + locally { val html = files.get("com/example/p1/Clazz.html") .map(page => { lazy val s = toHtml(page); () => s }) property("class") = html.map(_()).isDefined diff --git a/test/scalacheck/treemap.scala b/test/scalacheck/treemap.scala index 83fb586b5192..1df3424c2064 100644 --- a/test/scalacheck/treemap.scala +++ b/test/scalacheck/treemap.scala @@ -12,7 +12,7 @@ object TreeMapTest extends Properties("TreeMap") { keys <- listOf(arbitrary[A]) values <- listOfN(keys.size, arbitrary[B]) } yield TreeMap(keys zip values: _*) - implicit def arbTreeMap[A : Arbitrary : Ordering, B : Arbitrary] = Arbitrary(genTreeMap[A, B]) + implicit def arbTreeMap[A : Arbitrary : Ordering, B : Arbitrary]: Arbitrary[TreeMap[A, B]] = Arbitrary(genTreeMap[A, B]) property("foreach/iterator consistency") = forAll { (subject: TreeMap[Int, String]) => val it = subject.iterator diff --git a/test/scaladoc/run/diagrams-base.check b/test/scaladoc/run/diagrams-base.check index 619c56180bb9..ec9c6d6a74f2 100644 --- a/test/scaladoc/run/diagrams-base.check +++ b/test/scaladoc/run/diagrams-base.check @@ -1 +1,10 @@ +newSource:10: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.diagrams.T) + object E { implicit def eToT(e: E) = new T } + ^ +newSource:18: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.diagrams.E) + object X { implicit def xToE(x: X) = new E} + ^ +newSource:21: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.diagrams.E) + object Z { implicit def zToE(z: Z) = new E} + ^ Done. diff --git a/test/scaladoc/run/diagrams-filtering.check b/test/scaladoc/run/diagrams-filtering.check index 619c56180bb9..6f40b2c32793 100644 --- a/test/scaladoc/run/diagrams-filtering.check +++ b/test/scaladoc/run/diagrams-filtering.check @@ -1 +1,7 @@ +newSource:30: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.diagrams.T) + implicit def eToT(e: E) = new T + ^ +newSource:31: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.diagrams.A) + implicit def eToA(e: E) = new A { } + ^ Done. diff --git a/test/scaladoc/run/doc-source-url-java.scala b/test/scaladoc/run/doc-source-url-java.scala index 714d79e73d73..a131e9c00360 100644 --- a/test/scaladoc/run/doc-source-url-java.scala +++ b/test/scaladoc/run/doc-source-url-java.scala @@ -10,7 +10,7 @@ * additional information regarding copyright ownership. */ -import java.net.URL +import java.net.URI import scala.tools.nsc.ScalaDocReporter import scala.tools.nsc.doc.Universe @@ -26,14 +26,14 @@ object Test extends ScaladocModelTest { override def model: Option[Universe] = newDocFactory.makeUniverse(Left(List(resourceFile))) - def scaladocSettings = "-doc-source-url file:€{FILE_PATH}||€{FILE_EXT}||€{FILE_PATH_EXT}||€{FILE_LINE}" + def scaladocSettings = "-doc-source-url file:€{FILE_PATH}@@€{FILE_EXT}@@€{FILE_PATH_EXT}@@€{FILE_LINE}" def testModel(rootPackage: Package) = { import access._ val clazz = rootPackage._class("WithSource") - val expect = s"file:test/scaladoc/resources/doc-source-url||.java||test/scaladoc/resources/doc-source-url.java||13" - assert(clazz.sourceUrl.contains(new URL(expect)), s"got ${clazz.sourceUrl}") + val expect = s"file:test/scaladoc/resources/doc-source-url@@.java@@test/scaladoc/resources/doc-source-url.java@@13" + assert(clazz.sourceUrl.contains(new URI(expect).toURL), s"got ${clazz.sourceUrl}") } } diff --git a/test/scaladoc/run/doc-source-url.scala b/test/scaladoc/run/doc-source-url.scala index ce1abc3b0b36..0811fd25ddfe 100644 --- a/test/scaladoc/run/doc-source-url.scala +++ b/test/scaladoc/run/doc-source-url.scala @@ -10,7 +10,7 @@ * additional information regarding copyright ownership. */ -import java.net.URL +import java.net.URI import scala.tools.nsc.ScalaDocReporter import scala.tools.nsc.doc.Universe @@ -26,14 +26,14 @@ object Test extends ScaladocModelTest { override def model: Option[Universe] = newDocFactory.makeUniverse(Left(List(resourceFile))) - def scaladocSettings = "-doc-source-url file:€{FILE_PATH}||€{FILE_EXT}||€{FILE_PATH_EXT}||€{FILE_LINE}" + def scaladocSettings = "-doc-source-url file:€{FILE_PATH}@@€{FILE_EXT}@@€{FILE_PATH_EXT}@@€{FILE_LINE}" def testModel(rootPackage: Package) = { import access._ val clazz = rootPackage._class("WithSource") - val expect = s"file:test/scaladoc/resources/doc-source-url||.scala||test/scaladoc/resources/doc-source-url.scala||13" - assert(clazz.sourceUrl.contains(new URL(expect)), s"got ${clazz.sourceUrl}") + val expect = s"file:test/scaladoc/resources/doc-source-url@@.scala@@test/scaladoc/resources/doc-source-url.scala@@13" + assert(clazz.sourceUrl.contains(new URI(expect).toURL), s"got ${clazz.sourceUrl}") } } diff --git a/test/scaladoc/run/implicits-ambiguating.check b/test/scaladoc/run/implicits-ambiguating.check index 619c56180bb9..f716066eb126 100644 --- a/test/scaladoc/run/implicits-ambiguating.check +++ b/test/scaladoc/run/implicits-ambiguating.check @@ -1 +1,7 @@ +newSource:70: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.ambiguating.X[T]) + implicit def AtoX[T](a: A[T]) = new X[T] + ^ +newSource:71: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.ambiguating.Z[T]) + implicit def AtoZ[T](a: A[T]) = new Z[T] + ^ Done. diff --git a/test/scaladoc/run/implicits-base.check b/test/scaladoc/run/implicits-base.check index 619c56180bb9..e5f9afca6ebf 100644 --- a/test/scaladoc/run/implicits-base.check +++ b/test/scaladoc/run/implicits-base.check @@ -1 +1,19 @@ +newSource:36: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.base.EnrichedA[V]) + implicit def enrichA0[V](a: A[V]) = new EnrichedA(a) + ^ +newSource:37: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.base.NumericA[ZBUR]) + implicit def enrichA1[ZBUR: Numeric](a: A[ZBUR]) = new NumericA[ZBUR](a) + ^ +newSource:38: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.base.IntA) + implicit def enrichA2(a: A[Int]) = new IntA(a) + ^ +newSource:39: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.base.GtColonDoubleA) + implicit def enrichA3(a: A[T] forSome { type T <: Double }) = new GtColonDoubleA(a) + ^ +newSource:42: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.base.MyNumericA[Z]) + implicit def enrichA6[Z: MyNumeric](a: A[Z]) = new MyNumericA[Z](a) + ^ +newSource:44: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.base.ManifestA[H] with scala.test.scaladoc.implicits.base.MyTraversableOps[H]) + implicit def enrichA7[H <: Double : Manifest](a: A[H]) = new ManifestA[H](a) with MyTraversableOps[H] { def convToTraversableOps(x: H): H = sys.error("no") } + ^ Done. diff --git a/test/scaladoc/run/implicits-chaining.check b/test/scaladoc/run/implicits-chaining.check index 619c56180bb9..9cbfe46b4ac1 100644 --- a/test/scaladoc/run/implicits-chaining.check +++ b/test/scaladoc/run/implicits-chaining.check @@ -1 +1,16 @@ +newSource:22: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.Implicit1[T]) + implicit def implicit1[T <: Intermediate[_, _]](implicit b: Implicit2[T]) = new Implicit1[T](b) + ^ +newSource:24: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.Implicit2[T]) + implicit def implicit2alt1[T <: Intermediate[_ <: String, _]](implicit c: Implicit3[T]) = new Implicit2[T](c) + ^ +newSource:25: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.Implicit2[T]) + implicit def implicit2alt2[T <: Intermediate[_ <: Double, _]](implicit c: Implicit3[T]) = new Implicit2[T](c) + ^ +newSource:27: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.Implicit3[T]) + implicit def implicit3alt1[T <: Intermediate[_, _ <: Int]] = new Implicit3[T]() + ^ +newSource:28: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.Implicit3[T]) + implicit def implicit3alt2[T <: Intermediate[_ <: Double, _ <: AnyRef],X] = new Implicit3[T]() + ^ Done. diff --git a/test/scaladoc/run/implicits-known-type-classes.check b/test/scaladoc/run/implicits-known-type-classes.check index 619c56180bb9..225b60e6cfe3 100644 --- a/test/scaladoc/run/implicits-known-type-classes.check +++ b/test/scaladoc/run/implicits-known-type-classes.check @@ -1 +1,49 @@ +newSource:13: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[Numeric[T]]) + implicit def convertNumeric [T: Numeric] (a: A[T]) = new B(implicitly[Numeric[T]]) + ^ +newSource:14: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[Integral[T]]) + implicit def convertIntegral [T: Integral] (a: A[T]) = new B(implicitly[Integral[T]]) + ^ +newSource:15: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[Fractional[T]]) + implicit def convertFractional [T: Fractional] (a: A[T]) = new B(implicitly[Fractional[T]]) + ^ +newSource:16: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[Manifest[T]]) + implicit def convertManifest [T: Manifest] (a: A[T]) = new B(implicitly[Manifest[T]]) + ^ +newSource:17: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[reflect.ClassManifest[T]]) + implicit def convertClassManifest [T: ClassManifest] (a: A[T]) = new B(implicitly[ClassManifest[T]]) + ^ +newSource:18: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[OptManifest[T]]) + implicit def convertOptManifest [T: OptManifest] (a: A[T]) = new B(implicitly[OptManifest[T]]) + ^ +newSource:19: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[scala.reflect.ClassTag[T]]) + implicit def convertClassTag [T: ClassTag] (a: A[T]) = new B(implicitly[ClassTag[T]]) + ^ +newSource:20: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[reflect.runtime.universe.TypeTag[T]]) + implicit def convertTypeTag [T: TypeTag] (a: A[T]) = new B(implicitly[TypeTag[T]]) + ^ +newSource:29: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[scala.test.scaladoc.implicits.typeclasses.A.K[T]]) + implicit def convertK [T: K] (a: A[T]) = new B(implicitly[K[T]]) + ^ +newSource:30: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[scala.test.scaladoc.implicits.typeclasses.A.L[T]]) + implicit def convertL [T: L] (a: A[T]) = new B(implicitly[L[T]]) + ^ +newSource:31: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[scala.test.scaladoc.implicits.typeclasses.A.M[T]]) + implicit def convertM [T: M] (a: A[T]) = new B(implicitly[M[T]]) + ^ +newSource:32: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[scala.test.scaladoc.implicits.typeclasses.A.N[T]]) + implicit def convertN [T: N] (a: A[T]) = new B(implicitly[N[T]]) + ^ +newSource:33: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[scala.test.scaladoc.implicits.typeclasses.A.O[T]]) + implicit def convertO [T: O] (a: A[T]) = new B(implicitly[O[T]]) + ^ +newSource:34: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[scala.test.scaladoc.implicits.typeclasses.A.P[T]]) + implicit def convertP [T: P] (a: A[T]) = new B(implicitly[P[T]]) + ^ +newSource:35: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[scala.test.scaladoc.implicits.typeclasses.A.Q[T]]) + implicit def convertQ [T: Q] (a: A[T]) = new B(implicitly[Q[T]]) + ^ +newSource:36: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[scala.test.scaladoc.implicits.typeclasses.A.R[T]]) + implicit def convertR [T: R] (a: A[T]) = new B(implicitly[R[T]]) + ^ Done. diff --git a/test/scaladoc/run/implicits-shadowing.check b/test/scaladoc/run/implicits-shadowing.check index 619c56180bb9..59ba86b25cef 100644 --- a/test/scaladoc/run/implicits-shadowing.check +++ b/test/scaladoc/run/implicits-shadowing.check @@ -1 +1,4 @@ +newSource:63: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.shadowing.Z[T]) + implicit def AtoZ[T](a: A[T]) = new Z[T] + ^ Done. diff --git a/test/scaladoc/run/implicits-var-exp.check b/test/scaladoc/run/implicits-var-exp.check index 619c56180bb9..9cc519cab9e0 100644 --- a/test/scaladoc/run/implicits-var-exp.check +++ b/test/scaladoc/run/implicits-var-exp.check @@ -1 +1,7 @@ +newSource:8: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.variable.expansion.C) + implicit def aToC(a: A) = new C + ^ +newSource:9: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.variable.expansion.E with scala.test.scaladoc.variable.expansion.F) + implicit def aToE(a: A) = new E with F + ^ Done. diff --git a/test/tasty/neg-full-circle/src-3-app/TestExperimentalDefsPre.check b/test/tasty/neg-full-circle/src-3-app/TestExperimentalDefsPre.check index a645c25a1366..bdd98c5ac849 100644 --- a/test/tasty/neg-full-circle/src-3-app/TestExperimentalDefsPre.check +++ b/test/tasty/neg-full-circle/src-3-app/TestExperimentalDefsPre.check @@ -1,3 +1,7 @@ +-- Error: TestExperimentalDefsPre_fail.scala:1:18 +1 |import downstream.ExperimentalDefsPre.* + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |object ExperimentalDefsPre is marked @experimental and therefore may only be used in an experimental scope. -- Error: TestExperimentalDefsPre_fail.scala:4:10 4 | def test = new SubExperimentalNotExperimental | ^ @@ -10,8 +14,4 @@ 6 | class SubSubExperimental extends SubExperimentalNotExperimental | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |object ExperimentalDefsPre is marked @experimental and therefore may only be used in an experimental scope. --- Error: TestExperimentalDefsPre_fail.scala:6:8 -6 | class SubSubExperimental extends SubExperimentalNotExperimental - | ^ - |extension of experimental class SubExperimentalNotExperimental must have @experimental annotation 4 errors found diff --git a/test/tasty/neg/src-2/TestApplySigPoly.check b/test/tasty/neg/src-2/TestApplySigPoly.check new file mode 100644 index 000000000000..ccfb756803d9 --- /dev/null +++ b/test/tasty/neg/src-2/TestApplySigPoly.check @@ -0,0 +1,4 @@ +TestApplySigPoly_fail.scala:6: error: Unsupported Scala 3 signature polymorphic application in an annotation of method annotatedMethod; note that complex trees are not yet supported for Annotations; found in method annotatedMethod in object tastytest.ApplySigPoly. + def test = TestApplySigPolyPre.forceAnnots[ApplySigPoly.type, ApplySigPoly.boxAnnot] + ^ +1 error diff --git a/test/tasty/neg/src-2/TestApplySigPoly_fail.scala b/test/tasty/neg/src-2/TestApplySigPoly_fail.scala new file mode 100644 index 000000000000..d86094e4d4b1 --- /dev/null +++ b/test/tasty/neg/src-2/TestApplySigPoly_fail.scala @@ -0,0 +1,8 @@ +package tastytest + +object TestApplySigPoly { + + // force an annotation tree with an APPLYsigpoly node + def test = TestApplySigPolyPre.forceAnnots[ApplySigPoly.type, ApplySigPoly.boxAnnot] + +} diff --git a/test/tasty/neg/src-2/TestApplySigPoly_pre.scala b/test/tasty/neg/src-2/TestApplySigPoly_pre.scala new file mode 100644 index 000000000000..dc7d49bca384 --- /dev/null +++ b/test/tasty/neg/src-2/TestApplySigPoly_pre.scala @@ -0,0 +1,25 @@ +package tastytest + +import scala.language.experimental.macros + +import scala.reflect.macros.blackbox.Context + +object TestApplySigPolyPre { + + /** forces annotations of type `A` on methods from class `T` */ + def forceAnnots[T, A]: Unit = macro Macros.forceAnnotsImpl[T, A] + + object Macros { + def forceAnnotsImpl[T, A](c: Context)(implicit T: c.WeakTypeTag[T], A: c.WeakTypeTag[A]): c.Expr[Unit] = { + import c.universe._ + for { + method <- weakTypeOf[T].members.filter(_.isMethod) + annot <- method.annotations.find(_.tree.tpe =:= weakTypeOf[A]) + } { + annot.tree + } + c.Expr[Unit](q"()") + } + } + +} diff --git a/test/tasty/neg/src-2/TestCtxFns_fail.scala b/test/tasty/neg/src-2/TestCtxFns_fail.scala index 150ec4a209bc..c1db5e1d1585 100644 --- a/test/tasty/neg/src-2/TestCtxFns_fail.scala +++ b/test/tasty/neg/src-2/TestCtxFns_fail.scala @@ -9,7 +9,7 @@ object TestCtxFns { def puts[T](t: T): Unit = logs += t.toString } - implicit val ctx = new TestContext + implicit val ctx: TestContext = new TestContext def test1: Unit = { puts(23)(ctx) diff --git a/test/tasty/neg/src-2/TestErasedTypes.check b/test/tasty/neg/src-2/TestErasedTypes.check index 36e8612ded99..9db7d1c7e747 100644 --- a/test/tasty/neg/src-2/TestErasedTypes.check +++ b/test/tasty/neg/src-2/TestErasedTypes.check @@ -1,9 +1,3 @@ -TestErasedTypes_fail.scala:6: error: Unsupported Scala 3 erased modifier in refinement of type F; found in class tastytest.ErasedTypes.Bar. - def test1 = new Bar[Foo] - ^ -TestErasedTypes_fail.scala:7: error: Unsupported Scala 3 erased modifier in refinement of type F; found in class tastytest.ErasedTypes.Baz. - def test2 = new Baz[Foo] - ^ TestErasedTypes_fail.scala:9: error: Unsupported Scala 3 erased value x; found in method foo1 in trait tastytest.ErasedTypes.Foo. def test3(f: Foo) = f.foo1("foo") ^ @@ -13,4 +7,4 @@ TestErasedTypes_fail.scala:10: error: Unsupported Scala 3 erased value x; found TestErasedTypes_fail.scala:12: error: Unsupported Scala 3 erased method theString; found in object tastytest.ErasedTypes.ErasedCompileTimeOps. def test5 = ErasedCompileTimeOps.theString ^ -5 errors +3 errors diff --git a/test/tasty/neg/src-2/TestInvisibleDefs.check b/test/tasty/neg/src-2/TestInvisibleDefs.check index 9ce3bf4804cd..c1c95601ec33 100644 --- a/test/tasty/neg/src-2/TestInvisibleDefs.check +++ b/test/tasty/neg/src-2/TestInvisibleDefs.check @@ -8,6 +8,7 @@ TestInvisibleDefs_fail.scala:11: error: value getStatus is not a member of tasty mybean.getStatus() // error ^ TestInvisibleDefs_fail.scala:12: error: value setStatus is not a member of tastytest.InvisibleDefs.MyBean +did you mean status? mybean.setStatus("closed") // error ^ 4 errors diff --git a/test/tasty/neg/src-2/TestSaferExceptions.check b/test/tasty/neg/src-2/TestSaferExceptions.check index ad1ae4edd7d8..6090209bcb5d 100644 --- a/test/tasty/neg/src-2/TestSaferExceptions.check +++ b/test/tasty/neg/src-2/TestSaferExceptions.check @@ -1,4 +1,4 @@ -TestSaferExceptions_fail.scala:5: error: Unsupported Scala 3 erased context function type in bounds of type mayThrow: erased tastytest.SaferExceptions.CanThrowCapability[E] ?=> A; found in object tastytest.SaferExceptions. +TestSaferExceptions_fail.scala:5: error: Unsupported Scala 3 erased value x$1; found in method apply in tastytest.SaferExceptions.. def test = SaferExceptions.safeDiv(1, 0) // error ^ 1 error diff --git a/test/tasty/neg/src-3/ApplySigPoly.scala b/test/tasty/neg/src-3/ApplySigPoly.scala new file mode 100644 index 000000000000..046da9ff69a2 --- /dev/null +++ b/test/tasty/neg/src-3/ApplySigPoly.scala @@ -0,0 +1,20 @@ +package tastytest + +object ApplySigPoly { + + class Foo { + def foo(x: Int): Int = x + } + + private val lookup = java.lang.invoke.MethodHandles.lookup() + private val mt = java.lang.invoke.MethodType.methodType(classOf[Int], classOf[Int]); + + val mh = lookup.findVirtual(classOf[Foo], "foo", mt) + + val self = new Foo() + + class boxAnnot(val value: Int) extends scala.annotation.Annotation + + @boxAnnot(mh.invokeExact(self, 23): Int) + def annotatedMethod: Int = 23 +} diff --git a/test/tasty/run/src-2/tastytest/TestApplySigPoly.scala b/test/tasty/run/src-2/tastytest/TestApplySigPoly.scala new file mode 100644 index 000000000000..b77464610b05 --- /dev/null +++ b/test/tasty/run/src-2/tastytest/TestApplySigPoly.scala @@ -0,0 +1,14 @@ +package tastytest + +object TestApplySigPoly extends Suite("TestApplySigPoly") { + + test("construct class with APPLYsigpoly in parent") { + val subbox = new ApplySigPoly.SubBox() + assert(subbox.value == 23) + } + + test("call method with APPLYsigpoly in annotation") { + assert(ApplySigPoly.annotatedMethod == 23) + } + +} diff --git a/test/tasty/run/src-2/tastytest/TestLogarithms.scala b/test/tasty/run/src-2/tastytest/TestLogarithms.scala index 69992afe9f90..76bf9352ad15 100644 --- a/test/tasty/run/src-2/tastytest/TestLogarithms.scala +++ b/test/tasty/run/src-2/tastytest/TestLogarithms.scala @@ -4,10 +4,10 @@ import Logarithms._ object TestLogarithms extends Suite("TestLogarithms") { - val Some(l1) = Logarithm.of(2) - val Some(l2) = Logarithm.of(3) + val Some(l1) = Logarithm.of(10) + val Some(l2) = Logarithm.of(100) - test(assert((l1 + l2).toDouble == 4.999999999999999)) - test(assert((l1 * l2).toDouble == 6.0)) + test(assert((l1 + l2).toDouble == 109.99999999999997)) + test(assert((l1 * l2).toDouble == 1000.0)) } diff --git a/test/tasty/run/src-3/tastytest/ApplySigPoly.scala b/test/tasty/run/src-3/tastytest/ApplySigPoly.scala new file mode 100644 index 000000000000..4cdb4a4855d2 --- /dev/null +++ b/test/tasty/run/src-3/tastytest/ApplySigPoly.scala @@ -0,0 +1,25 @@ +package tastytest + +object ApplySigPoly { + + class Foo { + def foo(x: Int): Int = x + } + + private val lookup = java.lang.invoke.MethodHandles.lookup() + private val mt = java.lang.invoke.MethodType.methodType(classOf[Int], classOf[Int]); + + val mh = lookup.findVirtual(classOf[Foo], "foo", mt) + + class IntBox(val value: Int) + + val self = new Foo() + + // There should appear a APPLYsigpoly node in the parent of SubBox + class SubBox extends IntBox(mh.invokeExact(self, 23): Int) + + class boxAnnot(val value: Int) extends scala.annotation.Annotation + + @boxAnnot(mh.invokeExact(self, 23): Int) + def annotatedMethod: Int = 23 +} diff --git a/test/tasty/run/src-3/tastytest/Logarithms.scala b/test/tasty/run/src-3/tastytest/Logarithms.scala index f1e22f568785..b33d99f63137 100644 --- a/test/tasty/run/src-3/tastytest/Logarithms.scala +++ b/test/tasty/run/src-3/tastytest/Logarithms.scala @@ -1,5 +1,7 @@ package tastytest +import java.lang.StrictMath + object Logarithms { opaque type Logarithm = Double @@ -8,16 +10,16 @@ object Logarithms { // These are the two ways to lift to the Logarithm type - private[Logarithms] def apply(d: Double): Logarithm = math.log(d) + private[Logarithms] def apply(d: Double): Logarithm = StrictMath.log10(d) def of(d: Double): Option[Logarithm] = - if (d > 0.0) Some(math.log(d)) else None + if (d > 0.0) Some(StrictMath.log10(d)) else None } // implicit define cross compatible public APIs for opaque types final implicit class LogarithmOps(val logarithm: Logarithm) extends AnyVal { - def toDouble: Double = math.exp(logarithm) - def + (other: Logarithm): Logarithm = Logarithm(math.exp(logarithm) + math.exp(other)) + def toDouble: Double = StrictMath.pow(10.0, logarithm) + def + (other: Logarithm): Logarithm = Logarithm(StrictMath.pow(10.0, logarithm) + StrictMath.pow(10.0, other)) def * (other: Logarithm): Logarithm = logarithm + other } diff --git a/test/tasty/test/scala/tools/tastytest/TastyTestJUnit.scala b/test/tasty/test/scala/tools/tastytest/TastyTestJUnit.scala index 0ce6bfbffe48..0eb353a54cd7 100644 --- a/test/tasty/test/scala/tools/tastytest/TastyTestJUnit.scala +++ b/test/tasty/test/scala/tools/tastytest/TastyTestJUnit.scala @@ -2,6 +2,7 @@ package scala.tools.tastytest import org.junit.{Test => test, BeforeClass => setup, AfterClass => teardown} import org.junit.Assert._ +import org.junit.Assume._ import scala.util.{Try, Failure, Properties} diff --git a/versions.properties b/versions.properties index c049b91d2833..4c5a41f63ff8 100644 --- a/versions.properties +++ b/versions.properties @@ -1,13 +1,13 @@ # Scala version used for bootstrapping (see README.md) -starr.version=2.13.9 +starr.version=2.13.11-M2 # These are the versions of the modules that go with this release. # Artifact dependencies: # - scala-compiler: jline (% "optional") # Other usages: # - scala-asm: jar content included in scala-compiler -scala-asm.version=9.3.0-scala-1 +scala-asm.version=9.5.0-scala-1 # jna.version must be updated together with jline-terminal-jna -jline.version=3.21.0 -jna.version=5.9.0 +jline.version=3.22.0 +jna.version=5.13.0