diff --git a/.github/PULL_REQUEST_TEMPLATE/fix-issue.md b/.github/PULL_REQUEST_TEMPLATE/fix-issue.md index f7cf22eb59c7..005be5daef4f 100644 --- a/.github/PULL_REQUEST_TEMPLATE/fix-issue.md +++ b/.github/PULL_REQUEST_TEMPLATE/fix-issue.md @@ -8,7 +8,7 @@ assignees: '' ## Fix #XYZ diff --git a/.github/PULL_REQUEST_TEMPLATE/other-pr.md b/.github/PULL_REQUEST_TEMPLATE/other-pr.md index fad49836df92..a9948b717932 100644 --- a/.github/PULL_REQUEST_TEMPLATE/other-pr.md +++ b/.github/PULL_REQUEST_TEMPLATE/other-pr.md @@ -8,7 +8,7 @@ assignees: '' ## Description diff --git a/.github/workflows/build-sdk.yml b/.github/workflows/build-sdk.yml index b2af623d731a..cd111df1a083 100644 --- a/.github/workflows/build-sdk.yml +++ b/.github/workflows/build-sdk.yml @@ -61,6 +61,7 @@ jobs: distribution: temurin java-version: ${{ inputs.java-version }} cache : sbt + - uses: sbt/setup-sbt@v1 - name: Build and pack the SDK (universal) run : ./project/scripts/sbt dist/Universal/stage - name: Build and pack the SDK (linux x86-64) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a2006e16c7e8..cc1eb5d40d97 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -801,19 +801,13 @@ jobs: distDir="$3" # Build binaries - ./project/scripts/sbt "${sbtProject}/Universal/stage" + ./project/scripts/sbt "all ${sbtProject}/Universal/packageBin ${sbtProject}/Universal/packageZipTarball" - outputPath="${distDir}/target/universal/stage" artifactName="scala3-${{ env.RELEASE_TAG }}${distroSuffix}" - zipArchive="${artifactName}.zip" - tarGzArchive="${artifactName}.tar.gz" - - cwd=$(pwd) - (cd $outputPath && zip -r ${zipArchive} . && mv ${zipArchive} "${cwd}/") - tar -czf ${tarGzArchive} -C "$outputPath" . # Caluclate SHA for each of archive files - for file in "${zipArchive}" "${tarGzArchive}"; do + for file in "${artifactName}.zip" "${artifactName}.tar.gz"; do + mv ${distDir}/target/universal/$file $file sha256sum "${file}" > "${file}.sha256" done } diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml index f370cb2b541c..d052dc5eb6fc 100644 --- a/.github/workflows/cla.yml +++ b/.github/workflows/cla.yml @@ -3,20 +3,11 @@ on: pull_request: branches-ignore: - 'language-reference-stable' - push: - branches: - - 'language-reference-stable' - merge_group: -permissions: - contents: write - pull-requests: write - jobs: check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - run: ./project/scripts/check-cla.sh - if: github.event_name == 'pull_request' - env: - AUTHOR: ${{ github.event.pull_request.user.login }} + - name: Verify CLA + uses: scala/cla-checker@v1 + with: + author: ${{ github.event.pull_request.user.login }} diff --git a/.github/workflows/dependency-graph.yml b/.github/workflows/dependency-graph.yml index 35af4fa0526d..6a3f8174b2d7 100644 --- a/.github/workflows/dependency-graph.yml +++ b/.github/workflows/dependency-graph.yml @@ -9,6 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - uses: sbt/setup-sbt@v1 - uses: scalacenter/sbt-dependency-submission@v3 env: DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }} diff --git a/.github/workflows/language-reference.yaml b/.github/workflows/language-reference.yaml index 7f87b4a453ef..d79f4d029a77 100644 --- a/.github/workflows/language-reference.yaml +++ b/.github/workflows/language-reference.yaml @@ -36,6 +36,7 @@ jobs: distribution: 'temurin' java-version: 17 cache: 'sbt' + - uses: sbt/setup-sbt@v1 - name: Generate reference documentation and test links run: | diff --git a/.github/workflows/launchers.yml b/.github/workflows/launchers.yml index ce3aac235224..4ee07e4bfcc9 100644 --- a/.github/workflows/launchers.yml +++ b/.github/workflows/launchers.yml @@ -20,6 +20,7 @@ jobs: java-version: '17' distribution: 'temurin' cache: 'sbt' + - uses: sbt/setup-sbt@v1 - name: Build and test launcher command run: ./project/scripts/native-integration/bashTests env: @@ -37,9 +38,7 @@ jobs: java-version: '17' distribution: 'temurin' cache: 'sbt' - # https://github.com/actions/runner-images/issues/9369 - - name: Install sbt - run: brew install sbt + - uses: sbt/setup-sbt@v1 - name: Build and test launcher command run: ./project/scripts/native-integration/bashTests env: @@ -58,9 +57,7 @@ jobs: java-version: '17' distribution: 'temurin' cache: 'sbt' - # https://github.com/actions/runner-images/issues/9369 - - name: Install sbt - run: brew install sbt + - uses: sbt/setup-sbt@v1 - name: Build and test launcher command run: ./project/scripts/native-integration/bashTests env: @@ -79,9 +76,7 @@ jobs: java-version: '17' distribution: 'temurin' cache: 'sbt' - # https://github.com/actions/runner-images/issues/9369 - - name: Install sbt - run: brew install sbt + - uses: sbt/setup-sbt@v1 - name: Build and test launcher command run: ./project/scripts/native-integration/bashTests env: @@ -100,6 +95,7 @@ jobs: java-version: '17' distribution: 'temurin' cache: 'sbt' + - uses: sbt/setup-sbt@v1 - name: Build the launcher command run: sbt "dist-win-x86_64/Universal/stage" - name: Run the launcher command tests diff --git a/.github/workflows/publish-chocolatey.yml b/.github/workflows/publish-chocolatey.yml index 3b31728a50ba..88a8a7913188 100644 --- a/.github/workflows/publish-chocolatey.yml +++ b/.github/workflows/publish-chocolatey.yml @@ -35,5 +35,5 @@ jobs: with: name: scala.nupkg - name: Publish the package to Chocolatey - run: choco push scala.nupkg --source https://push.chocolatey.org/ --api-key ${{ secrets.API-KEY }} + run: choco push scala.${{inputs.version}}.nupkg --source https://push.chocolatey.org/ --api-key ${{ secrets.API-KEY }} \ No newline at end of file diff --git a/.github/workflows/scaladoc.yaml b/.github/workflows/scaladoc.yaml index 4f6f5bbfe2fb..d2e3071e765b 100644 --- a/.github/workflows/scaladoc.yaml +++ b/.github/workflows/scaladoc.yaml @@ -37,6 +37,7 @@ jobs: java-version: 17 cache: 'sbt' + - uses: sbt/setup-sbt@v1 - name: Compile and test scala3doc-js run: ./project/scripts/sbt scaladoc-js-main/test diff --git a/.github/workflows/test-cc.yml b/.github/workflows/test-cc.yml index 68113163ff93..69996fb6d74e 100644 --- a/.github/workflows/test-cc.yml +++ b/.github/workflows/test-cc.yml @@ -26,5 +26,6 @@ jobs: steps: - name: Git Checkout uses: actions/checkout@v4 + - uses: sbt/setup-sbt@v1 - name: Test with Scala 2 library with CC TASTy run: ./project/scripts/sbt ";set ThisBuild/Build.scala2Library := Build.Scala2LibraryCCTasty ;scala3-bootstrapped/test" diff --git a/.vscode-template/settings.json b/.vscode-template/settings.json index 257da27b118f..8cf2d29e3bae 100644 --- a/.vscode-template/settings.json +++ b/.vscode-template/settings.json @@ -9,7 +9,6 @@ "**/*.class": true, "**/*.tasty": true, "**/target/": true, - "community-build/community-projects": true, - "tests/pos-with-compiler-cc/dotc/**/*.scala": true + "community-build/community-projects": true } } diff --git a/MAINTENANCE.md b/MAINTENANCE.md index 05b90cc90b86..47339e17eee5 100644 --- a/MAINTENANCE.md +++ b/MAINTENANCE.md @@ -69,7 +69,7 @@ The following is the list of all the principal areas of the compiler and the int - Parser: @odersky, @hamzaremmal, @KacperFKorban - Typer: @odersky, @smarter, (@dwijnand), @noti0nal, @EugeneFlesselle, @KacperFKorban, @bracevac - Erasure: @smarter, @odersky -- Enums: +- Enums: - Derivation & Mirrors: (@dwijnand), @EugeneFlesselle - Export: @odersky - Pattern Matching: @dwijnand, @sjrd, @noti0na1 @@ -77,7 +77,7 @@ The following is the list of all the principal areas of the compiler and the int - Metaprogramming (Quotes, Reflect, Staging): @jchyb, @hamzaremmal - Match types: @sjrd, @dwijnand, @Linyxus, @EugeneFlesselle - GADT: @dwijnand, @Linyxus -- Initialization checker: +- Initialization checker: - Transforms: @sjrd, @odersky, @smarter - Tailrec: @sjrd, @mbovel - JS backend: @sjrd @@ -87,7 +87,7 @@ The following is the list of all the principal areas of the compiler and the int - Safe nulls (experimental): @noti0na1 - Capture checker (experimental): @odersky, @Linyxus, @bracevac, @noti0na1 - Modularity (experimental): @KacperFKorban -- Named Tuples (experimental): @odersky +- Named Tuples (experimental): @odersky, @aherlihy ### Tooling - REPL: @dwijnand @@ -95,9 +95,9 @@ The following is the list of all the principal areas of the compiler and the int - IDE: @tgodzik, (@kasiaMarek) - Scaladoc: @Florian3k - SemanticDB: @natsukagami, (@tanishiking) -- Coverage: @KacperFKorban +- Coverage: @KacperFKorban - Linting (especially unused warnings) / Reporting UX: @KacperFKorban -- Presentation Compiler: @rochala, @tgodzik, @kasiaMarek, @natsukagami +- Presentation Compiler: @rochala, @tgodzik, @kasiaMarek, @natsukagami - Debug Adapter: @adpi2, (@tgodzik) - Scastie: @rochala diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala index 4f4caf36d92a..e632def24700 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala @@ -515,7 +515,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { */ object locals { - private val slots = mutable.AnyRefMap.empty[Symbol, Local] // (local-or-param-sym -> Local(BType, name, idx, isSynth)) + private val slots = mutable.HashMap.empty[Symbol, Local] // (local-or-param-sym -> Local(BType, name, idx, isSynth)) private var nxtIdx = -1 // next available index for local-var diff --git a/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala b/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala index cb7ed3d54788..ae423b6b80dd 100644 --- a/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala +++ b/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala @@ -185,6 +185,7 @@ object BackendUtils { 20 -> asm.Opcodes.V20, 21 -> asm.Opcodes.V21, 22 -> asm.Opcodes.V22, - 23 -> asm.Opcodes.V23 + 23 -> asm.Opcodes.V23, + 24 -> asm.Opcodes.V24 ) } diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 56c153498f87..67e1885b511f 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -694,15 +694,15 @@ object desugar { val originalTparams = constr1.leadingTypeParams val originalVparamss = asTermOnly(constr1.trailingParamss) lazy val derivedEnumParams = enumClass.typeParams.map(derivedTypeParamWithVariance) - val impliedTparams = - if (isEnumCase) { + val enumTParams = + if isEnumCase then val tparamReferenced = typeParamIsReferenced( - enumClass.typeParams, originalTparams, originalVparamss, parents) - if (originalTparams.isEmpty && (parents.isEmpty || tparamReferenced)) + enumClass.typeParams, originalTparams, originalVparamss, parents) + if originalTparams.isEmpty && (parents.isEmpty || tparamReferenced) then derivedEnumParams.map(tdef => tdef.withFlags(tdef.mods.flags | PrivateLocal)) - else originalTparams - } - else originalTparams + else Nil + else Nil + val impliedTparams = enumTParams ++ originalTparams if mods.is(Trait) then for vparams <- originalVparamss; vparam <- vparams do @@ -735,6 +735,11 @@ object desugar { derived.withAnnotations(Nil) val constr = cpy.DefDef(constr1)(paramss = joinParams(constrTparams, constrVparamss)) + if enumTParams.nonEmpty then + defaultGetters = defaultGetters.map: + case ddef: DefDef => + val tParams = enumTParams.map(tparam => toMethParam(tparam, KeepAnnotations.All)) + cpy.DefDef(ddef)(paramss = joinParams(tParams, ddef.trailingParamss)) val (normalizedBody, enumCases, enumCompanionRef) = { // Add constructor type parameters and evidence implicit parameters @@ -1081,12 +1086,13 @@ object desugar { if mods.isAllOf(Given | Inline | Transparent) then report.error("inline given instances cannot be trasparent", cdef) var classMods = if mods.is(Given) then mods &~ (Inline | Transparent) | Synthetic else mods - if vparamAccessors.exists(_.mods.is(Tracked)) then + val newBody = tparamAccessors ::: vparamAccessors ::: normalizedBody ::: caseClassMeths + if newBody.collect { case d: ValOrDefDef => d }.exists(_.mods.is(Tracked)) then classMods |= Dependent cpy.TypeDef(cdef: TypeDef)( name = className, rhs = cpy.Template(impl)(constr, parents1, clsDerived, self1, - tparamAccessors ::: vparamAccessors ::: normalizedBody ::: caseClassMeths) + newBody) ).withMods(classMods) } @@ -1556,6 +1562,12 @@ object desugar { rhsOK(rhs) } + val legalTracked: Context ?=> MemberDefTest = { + case valdef @ ValDef(_, _, _) => + val sym = valdef.symbol + !ctx.owner.exists || ctx.owner.isClass || ctx.owner.is(Case) || ctx.owner.isConstructor || valdef.mods.is(Param) || valdef.mods.is(ParamAccessor) + } + def checkOpaqueAlias(tree: MemberDef)(using Context): MemberDef = def check(rhs: Tree): MemberDef = rhs match case bounds: TypeBoundsTree if bounds.alias.isEmpty => @@ -1581,6 +1593,7 @@ object desugar { } else tested tested = checkOpaqueAlias(tested) tested = checkApplicable(Opaque, legalOpaque) + tested = checkApplicable(Tracked, legalTracked) tested case _ => tree diff --git a/compiler/src/dotty/tools/dotc/classpath/DirectoryClassPath.scala b/compiler/src/dotty/tools/dotc/classpath/DirectoryClassPath.scala index aed5be45cb0d..2d659b532d7b 100644 --- a/compiler/src/dotty/tools/dotc/classpath/DirectoryClassPath.scala +++ b/compiler/src/dotty/tools/dotc/classpath/DirectoryClassPath.scala @@ -229,7 +229,7 @@ final class CtSymClassPath(ctSym: java.nio.file.Path, release: Int) extends Clas // e.g. "java.lang" -> Seq(/876/java/lang, /87/java/lang, /8/java/lang)) private val packageIndex: scala.collection.Map[String, scala.collection.Seq[Path]] = { - val index = collection.mutable.AnyRefMap[String, collection.mutable.ListBuffer[Path]]() + val index = collection.mutable.HashMap[String, collection.mutable.ListBuffer[Path]]() val isJava12OrHigher = scala.util.Properties.isJavaAtLeast("12") rootsForRelease.foreach(root => Files.walk(root).iterator().asScala.filter(Files.isDirectory(_)).foreach { p => val moduleNamePathElementCount = if (isJava12OrHigher) 1 else 0 diff --git a/compiler/src/dotty/tools/dotc/config/PathResolver.scala b/compiler/src/dotty/tools/dotc/config/PathResolver.scala index 29e6e35855c8..f60727e6bba2 100644 --- a/compiler/src/dotty/tools/dotc/config/PathResolver.scala +++ b/compiler/src/dotty/tools/dotc/config/PathResolver.scala @@ -36,9 +36,16 @@ object PathResolver { /** Values found solely by inspecting environment or property variables. */ object Environment { - private def searchForBootClasspath = ( - systemProperties find (_._1 endsWith ".boot.class.path") map (_._2) getOrElse "" - ) + private def searchForBootClasspath = { + import scala.jdk.CollectionConverters.* + val props = System.getProperties + // This formulation should be immune to ConcurrentModificationExceptions when system properties + // we're unlucky enough to witness a partially published result of System.setProperty or direct + // mutation of the System property map. stringPropertyNames internally uses the Enumeration interface, + // rather than Iterator, and this disables the fail-fast ConcurrentModificationException. + val propNames = props.stringPropertyNames() + propNames.asScala collectFirst { case k if k endsWith ".boot.class.path" => props.getProperty(k) } getOrElse "" + } /** Environment variables which java pays attention to so it * seems we do as well. @@ -46,7 +53,8 @@ object PathResolver { def classPathEnv: String = envOrElse("CLASSPATH", "") def sourcePathEnv: String = envOrElse("SOURCEPATH", "") - def javaBootClassPath: String = propOrElse("sun.boot.class.path", searchForBootClasspath) + //using propOrNone/getOrElse instead of propOrElse so that searchForBootClasspath is lazy evaluated + def javaBootClassPath: String = propOrNone("sun.boot.class.path") getOrElse searchForBootClasspath def javaExtDirs: String = propOrEmpty("java.ext.dirs") def scalaHome: String = propOrEmpty("scala.home") diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 6ef33d24f8be..6a8a88a429e5 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -353,8 +353,7 @@ private sealed trait XSettings: val XreadComments: Setting[Boolean] = BooleanSetting(AdvancedSetting, "Xread-docs", "Read documentation from tasty.") /** Area-specific debug output */ - val XnoDecodeStacktraces: Setting[Boolean] = BooleanSetting(AdvancedSetting, "Xno-decode-stacktraces", "Show raw StackOverflow stacktraces, instead of decoding them into triggering operations.") - val XnoEnrichErrorMessages: Setting[Boolean] = BooleanSetting(AdvancedSetting, "Xno-enrich-error-messages", "Show raw error messages, instead of enriching them with contextual information.") + val XnoEnrichErrorMessages: Setting[Boolean] = BooleanSetting(AdvancedSetting, "Xno-enrich-error-messages", "Show raw error messages, instead of enriching them with contextual information.", aliases = List("Xno-decode-stacktraces")) val XdebugMacros: Setting[Boolean] = BooleanSetting(AdvancedSetting, "Xdebug-macros", "Show debug info when quote pattern match fails") /** Pipeline compilation options */ @@ -487,7 +486,7 @@ private sealed trait YSettings: @deprecated(message = "Lifted to -X, Scheduled for removal.", since = "3.5.0") val YreadComments: Setting[Boolean] = BooleanSetting(ForkSetting, "Yread-docs", "Read documentation from tasty.", deprecation = Deprecation.renamed("-Xread-docs")) @deprecated(message = "Lifted to -X, Scheduled for removal.", since = "3.5.0") - val YnoDecodeStacktraces: Setting[Boolean] = BooleanSetting(ForkSetting, "Yno-decode-stacktraces", "Show raw StackOverflow stacktraces, instead of decoding them into triggering operations.", deprecation = Deprecation.renamed("-Xno-decode-stacktraces")) + val YnoDecodeStacktraces: Setting[Boolean] = BooleanSetting(ForkSetting, "Yno-decode-stacktraces", "Show raw StackOverflow stacktraces, instead of decoding them into triggering operations.", deprecation = Deprecation.renamed("-Xno-enrich-error-messages")) @deprecated(message = "Lifted to -X, Scheduled for removal.", since = "3.5.0") val YnoEnrichErrorMessages: Setting[Boolean] = BooleanSetting(ForkSetting, "Yno-enrich-error-messages", "Show raw error messages, instead of enriching them with contextual information.", deprecation = Deprecation.renamed("-Xno-enrich-error-messages")) @deprecated(message = "Lifted to -X, Scheduled for removal.", since = "3.5.0") diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index b915373da021..0775b3caaf0c 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -480,7 +480,7 @@ object Flags { */ val AfterLoadFlags: FlagSet = commonFlags( FromStartFlags, AccessFlags, Final, AccessorOrSealed, - Abstract, LazyOrTrait, SelfName, JavaDefined, JavaAnnotation, Transparent, Tracked) + Abstract, LazyOrTrait, SelfName, JavaDefined, JavaAnnotation, Transparent) /** A value that's unstable unless complemented with a Stable flag */ val UnstableValueFlags: FlagSet = Mutable | Method diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index e14e5cf0a728..be651842d9b0 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -2539,7 +2539,9 @@ object SymDenotations { ) if compiledNow.exists then compiledNow else - val assocFiles = multi.aggregate(d => Set(d.symbol.associatedFile.nn), _ union _) + val assocFiles = multi + .filterWithPredicate(_.symbol.maybeOwner.isPackageObject) + .aggregate(d => Set(d.symbol.associatedFile.nn), _ union _) if assocFiles.size == 1 then multi // they are all overloaded variants from the same file else diff --git a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala index 1c9696da67d1..4761beae8bd0 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala @@ -117,7 +117,7 @@ extends TypeError: em"""Recursion limit exceeded. |Maybe there is an illegal cyclic reference? |If that's not the case, you could also try to increase the stacksize using the -Xss JVM option. - |For the unprocessed stack trace, compile with -Xno-decode-stacktraces. + |For the unprocessed stack trace, compile with -Xno-enrich-error-messages. |A recurring operation is (inner to outer): |${opsString(mostCommon).stripMargin}""" @@ -137,7 +137,7 @@ object handleRecursive: e def apply(op: String, details: => String, exc: Throwable, weight: Int = 1)(using Context): Nothing = - if ctx.settings.XnoDecodeStacktraces.value then + if ctx.settings.XnoEnrichErrorMessages.value then throw exc else exc match case _: RecursionOverflow => diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 7fd6444746ce..7b80c7c80a21 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -392,7 +392,7 @@ class TreePickler(pickler: TastyPickler, attributes: Attributes) { } catch case ex: Throwable => - if !ctx.settings.XnoDecodeStacktraces.value + if !ctx.settings.XnoEnrichErrorMessages.value && handleRecursive.underlyingStackOverflowOrNull(ex) != null then throw StackSizeExceeded(mdef) else @@ -941,7 +941,7 @@ class TreePickler(pickler: TastyPickler, attributes: Attributes) { em"""Recursion limit exceeded while pickling ${ex.mdef} |in ${ex.mdef.symbol.showLocated}. |You could try to increase the stacksize using the -Xss JVM option. - |For the unprocessed stack trace, compile with -Xno-decode-stacktraces.""", + |For the unprocessed stack trace, compile with -Xno-enrich-error-messages.""", ex.mdef.srcPos) def missing = forwardSymRefs.keysIterator diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index de99ce0105ea..d9ae4ddb6006 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1432,7 +1432,7 @@ class TreeUnpickler(reader: TastyReader, extendOnly(namedArgs) else // needs reordering, and possibly fill in holes for default arguments - val argsByName = mutable.AnyRefMap.from(namedArgs.map(arg => arg.name -> arg)) + val argsByName = mutable.HashMap.from(namedArgs.map(arg => arg.name -> arg)) val reconstructedArgs = formalNames.lazyZip(methType.paramInfos).map { (name, tpe) => argsByName.remove(name).getOrElse(makeDefault(name, tpe)) } diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index 611fda9c1d41..3b91312740d1 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -161,7 +161,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas private val entries = new Array[AnyRef](index.length) /** A map from symbols to their associated `decls` scopes */ - private val symScopes = mutable.AnyRefMap[Symbol, Scope]() + private val symScopes = mutable.HashMap[Symbol, Scope]() /** A mapping from method types to the parameters used in constructing them */ private val paramsOfMethodType = new java.util.IdentityHashMap[MethodType, List[Symbol]] diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 89f88faebc17..7933cbbea12f 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3519,7 +3519,7 @@ object Parsers { * UsingClsTermParamClause::= ‘(’ ‘using’ [‘erased’] (ClsParams | ContextTypes) ‘)’ * ClsParams ::= ClsParam {‘,’ ClsParam} * ClsParam ::= {Annotation} - * [{Modifier | ‘tracked’} (‘val’ | ‘var’)] Param + * [{Modifier} (‘val’ | ‘var’)] Param * TypelessClause ::= DefTermParamClause * | UsingParamClause * @@ -3557,8 +3557,6 @@ object Parsers { if isErasedKw then mods = addModifier(mods) if paramOwner.isClass then - if isIdent(nme.tracked) && in.featureEnabled(Feature.modularity) && !in.lookahead.isColon then - mods = addModifier(mods) mods = addFlag(modifiers(start = mods), ParamAccessor) mods = if in.token == VAL then @@ -3706,7 +3704,15 @@ object Parsers { in.languageImportContext = in.languageImportContext.importContext(imp, NoSymbol) for case ImportSelector(id @ Ident(imported), EmptyTree, _) <- selectors do if Feature.handleGlobalLanguageImport(prefix, imported) && !outermost then - syntaxError(em"this language import is only allowed at the toplevel", id.span) + val desc = + if ctx.mode.is(Mode.Interactive) then + "not allowed in the REPL" + else "only allowed at the toplevel" + val hint = + if ctx.mode.is(Mode.Interactive) then + f"\nTo use this language feature, include the flag `-language:$prefix.$imported` when starting the REPL" + else "" + syntaxError(em"this language import is $desc$hint", id.span) if allSourceVersionNames.contains(imported) && prefix.isEmpty then if !outermost then syntaxError(em"source version import is only allowed at the toplevel", id.span) diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index 2dc0a1a8d805..2007b633a7c5 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -212,6 +212,7 @@ object Scanners { def featureEnabled(name: TermName) = Feature.enabled(name)(using languageImportContext) def erasedEnabled = featureEnabled(Feature.erasedDefinitions) + def trackedEnabled = featureEnabled(Feature.modularity) private var postfixOpsEnabledCache = false private var postfixOpsEnabledCtx: Context = NoContext @@ -1195,7 +1196,7 @@ object Scanners { def isSoftModifier: Boolean = token == IDENTIFIER - && (softModifierNames.contains(name) || name == nme.erased && erasedEnabled) + && (softModifierNames.contains(name) || name == nme.erased && erasedEnabled || name == nme.tracked && trackedEnabled) def isSoftModifierInModifierPosition: Boolean = isSoftModifier && inModifierPosition() diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index 2c3774b59a9a..cc78203e873f 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -218,6 +218,8 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe case QuotedTypeMissingID // errorNumber: 202 case DeprecatedAssignmentSyntaxID // errorNumber: 203 case DeprecatedInfixNamedArgumentSyntaxID // errorNumber: 204 + case GivenSearchPriorityID // errorNumber: 205 + case EnumMayNotBeValueClassesID // errorNumber: 206 def errorNumber = ordinal - 1 diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index b396aa62f599..28a2b5757a93 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -3361,3 +3361,47 @@ class DeprecatedInfixNamedArgumentSyntax()(using Context) extends SyntaxMsg(Depr + Message.rewriteNotice("This", version = SourceVersion.`3.6-migration`) def explain(using Context) = "" + +class GivenSearchPriorityWarning( + pt: Type, + cmp: Int, + prev: Int, + winner: TermRef, + loser: TermRef, + isLastOldVersion: Boolean +)(using Context) extends Message(GivenSearchPriorityID): + def kind = MessageKind.PotentialIssue + def choice(nth: String, c: Int) = + if c == 0 then "none - it's ambiguous" + else s"the $nth alternative" + val (change, whichChoice) = + if isLastOldVersion + then ("will change in the future release", "Current choice ") + else ("has changed", "Previous choice") + def warningMessage: String = + i"""Given search preference for $pt between alternatives + | ${loser} + |and + | ${winner} + |$change. + |$whichChoice : ${choice("first", prev)} + |Choice from Scala 3.7 : ${choice("second", cmp)}""" + def migrationHints: String = + i"""Suppress this warning by choosing -source 3.5, -source 3.7, or + |by using @annotation.nowarn("id=205")""" + def ambiguousNote: String = + i""" + | + |Note: $warningMessage""" + def msg(using Context) = + i"""$warningMessage + | + |$migrationHints""" + + def explain(using Context) = "" + +final class EnumMayNotBeValueClasses(sym: Symbol)(using Context) extends SyntaxMsg(EnumMayNotBeValueClassesID): + def msg(using Context): String = i"$sym may not be a value class" + + def explain(using Context) = "" +end EnumMayNotBeValueClasses diff --git a/compiler/src/dotty/tools/dotc/transform/ArrayConstructors.scala b/compiler/src/dotty/tools/dotc/transform/ArrayConstructors.scala index e94fa612e6cf..84cb64533532 100644 --- a/compiler/src/dotty/tools/dotc/transform/ArrayConstructors.scala +++ b/compiler/src/dotty/tools/dotc/transform/ArrayConstructors.scala @@ -27,7 +27,10 @@ class ArrayConstructors extends MiniPhase { override def transformApply(tree: tpd.Apply)(using Context): tpd.Tree = { def expand(elemType: Type, dims: List[Tree]) = - tpd.newArray(elemType, tree.tpe, tree.span, JavaSeqLiteral(dims, TypeTree(defn.IntClass.typeRef))) + val elemTypeNonVoid = + if elemType.isValueSubType(defn.UnitType) then defn.BoxedUnitClass.typeRef + else elemType + tpd.newArray(elemTypeNonVoid, tree.tpe, tree.span, JavaSeqLiteral(dims, TypeTree(defn.IntClass.typeRef))) if (tree.fun.symbol eq defn.ArrayConstructor) { val TypeApply(tycon, targ :: Nil) = tree.fun: @unchecked diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 0feee53ca50f..898517806e50 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -2,7 +2,7 @@ package dotty.tools package dotc package transform -import dotty.tools.dotc.ast.{Trees, tpd, untpd, desugar} +import dotty.tools.dotc.ast.{Trees, tpd, untpd, desugar, TreeTypeMap} import scala.collection.mutable import core.* import dotty.tools.dotc.typer.Checking @@ -16,7 +16,7 @@ import Symbols.*, NameOps.* import ContextFunctionResults.annotateContextResults import config.Printers.typr import config.Feature -import util.SrcPos +import util.{SrcPos, Stats} import reporting.* import NameKinds.WildcardParamName import cc.* @@ -154,7 +154,21 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => case _ => case _ => - private def transformAnnot(annot: Tree)(using Context): Tree = { + /** Returns a copy of the given tree with all symbols fresh. + * + * Used to guarantee that no symbols are shared between trees in different + * annotations. + */ + private def copySymbols(tree: Tree)(using Context) = + Stats.trackTime("Annotations copySymbols"): + val ttm = + new TreeTypeMap: + override def withMappedSyms(syms: List[Symbol]) = + withMappedSyms(syms, mapSymbols(syms, this, true)) + ttm(tree) + + /** Transforms the given annotation tree. */ + private def transformAnnotTree(annot: Tree)(using Context): Tree = { val saved = inJavaAnnot inJavaAnnot = annot.symbol.is(JavaDefined) if (inJavaAnnot) checkValidJavaAnnotation(annot) @@ -163,7 +177,19 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => } private def transformAnnot(annot: Annotation)(using Context): Annotation = - annot.derivedAnnotation(transformAnnot(annot.tree)) + val tree1 = + annot match + case _: BodyAnnotation => annot.tree + case _ => copySymbols(annot.tree) + annot.derivedAnnotation(transformAnnotTree(tree1)) + + /** Transforms all annotations in the given type. */ + private def transformAnnotsIn(using Context) = + new TypeMap: + def apply(tp: Type) = tp match + case tp @ AnnotatedType(parent, annot) => + tp.derivedAnnotatedType(mapOver(parent), transformAnnot(annot)) + case _ => mapOver(tp) private def processMemberDef(tree: Tree)(using Context): tree.type = { val sym = tree.symbol @@ -501,7 +527,7 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => Checking.checkRealizable(tree.tpt.tpe, tree.srcPos, "SAM type") super.transform(tree) case tree @ Annotated(annotated, annot) => - cpy.Annotated(tree)(transform(annotated), transformAnnot(annot)) + cpy.Annotated(tree)(transform(annotated), transformAnnotTree(annot)) case tree: AppliedTypeTree => if (tree.tpt.symbol == defn.andType) Checking.checkNonCyclicInherited(tree.tpe, tree.args.tpes, EmptyScope, tree.srcPos) @@ -524,11 +550,7 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => super.transform(tree) case tree: TypeTree => val tpe = if tree.isInferred then CleanupRetains()(tree.tpe) else tree.tpe - tree.withType: - tpe match - case AnnotatedType(parent, annot) => - AnnotatedType(parent, transformAnnot(annot)) // TODO: Also map annotations embedded in type? - case _ => tpe + tree.withType(transformAnnotsIn(tpe)) case Typed(Ident(nme.WILDCARD), _) => withMode(Mode.Pattern)(super.transform(tree)) // The added mode signals that bounds in a pattern need not diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 96c38bcc80af..73719cc287a4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -963,6 +963,8 @@ trait Applications extends Compatibility { case (arg: NamedArg, _) => arg case (arg, name) => NamedArg(name, arg) } + else if isAnnotConstr(methRef.symbol) then + typedArgs else if !sameSeq(args, orderedArgs) && !typedArgs.forall(isSafeArg) then // need to lift arguments to maintain evaluation order in the // presence of argument reorderings. diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 1cd531046753..e870ffd0fc90 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -734,11 +734,16 @@ object Checking { case _ => report.error(ValueClassesMayNotContainInitalization(clazz), stat.srcPos) } - if (clazz.isDerivedValueClass) { + // We don't check synthesised enum anonymous classes that are generated from + // enum extending a value class type (AnyVal or an alias of it) + // The error message 'EnumMayNotBeValueClassesID' will take care of generating the error message (See #22236) + if (clazz.isDerivedValueClass && !clazz.isEnumAnonymClass) { if (clazz.is(Trait)) report.error(CannotExtendAnyVal(clazz), clazz.srcPos) if clazz.is(Module) then report.error(CannotExtendAnyVal(clazz), clazz.srcPos) + if (clazz.is(Enum)) + report.error(EnumMayNotBeValueClasses(clazz), clazz.srcPos) if (clazz.is(Abstract)) report.error(ValueClassesMayNotBeAbstract(clazz), clazz.srcPos) if (!clazz.isStatic) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 228206d8fb1e..193cc443b4ae 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -549,10 +549,10 @@ object Implicits: /** An ambiguous implicits failure */ class AmbiguousImplicits(val alt1: SearchSuccess, val alt2: SearchSuccess, val expectedType: Type, val argument: Tree, val nested: Boolean = false) extends SearchFailureType: - private[Implicits] var priorityChangeWarnings: List[Message] = Nil + private[Implicits] var priorityChangeWarnings: List[GivenSearchPriorityWarning] = Nil def priorityChangeWarningNote(using Context): String = - priorityChangeWarnings.map(msg => s"\n\nNote: $msg").mkString + priorityChangeWarnings.map(_.ambiguousNote).mkString def msg(using Context): Message = var str1 = err.refStr(alt1.ref) @@ -1312,7 +1312,7 @@ trait Implicits: // A map that associates a priority change warning (between -source 3.6 and 3.7) // with the candidate refs mentioned in the warning. We report the associated // message if one of the critical candidates is part of the result of the implicit search. - val priorityChangeWarnings = mutable.ListBuffer[(/*critical:*/ List[TermRef], Message)]() + val priorityChangeWarnings = mutable.ListBuffer[(/*critical:*/ List[TermRef], GivenSearchPriorityWarning)]() val sv = Feature.sourceVersion val isLastOldVersion = sv.stable == SourceVersion.`3.6` @@ -1353,21 +1353,7 @@ trait Implicits: cmp match case 1 => (alt2, alt1) case -1 => (alt1, alt2) - def choice(nth: String, c: Int) = - if c == 0 then "none - it's ambiguous" - else s"the $nth alternative" - val (change, whichChoice) = - if isLastOldVersion - then ("will change", "Current choice ") - else ("has changed", "Previous choice") - val msg = - em"""Given search preference for $pt between alternatives - | ${loser.ref} - |and - | ${winner.ref} - |$change. - |$whichChoice : ${choice("first", prev)} - |New choice from Scala 3.7: ${choice("second", cmp)}""" + val msg = GivenSearchPriorityWarning(pt, cmp, prev, winner.ref, loser.ref, isLastOldVersion) val critical = alt1.ref :: alt2.ref :: Nil priorityChangeWarnings += ((critical, msg)) if isLastOldVersion then prev else cmp diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 87dec93f8040..e8b22325d1e9 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -66,9 +66,9 @@ class Namer { typer: Typer => /** A partial map from unexpanded member and pattern defs and to their expansions. * Populated during enterSyms, emptied during typer. */ - //lazy val expandedTree = new mutable.AnyRefMap[DefTree, Tree] + //lazy val expandedTree = new mutable.HashMap[DefTree, Tree] /*{ - override def default(tree: DefTree) = tree // can't have defaults on AnyRefMaps :-( + override def default(tree: DefTree) = tree // can't have defaults on HashMaps :-( }*/ /** A map from expanded MemberDef, PatDef or Import trees to their symbols. @@ -76,12 +76,12 @@ class Namer { typer: Typer => * with the same symbol is created (this can be when the symbol is completed * or at the latest when the tree is typechecked. */ - //lazy val symOfTree = new mutable.AnyRefMap[Tree, Symbol] + //lazy val symOfTree = new mutable.HashMap[Tree, Symbol] /** A map from expanded trees to their typed versions. * Populated when trees are typechecked during completion (using method typedAhead). */ - // lazy val typedTree = new mutable.AnyRefMap[Tree, tpd.Tree] + // lazy val typedTree = new mutable.HashMap[Tree, tpd.Tree] /** A map from method symbols to nested typers. * Populated when methods are completed. Emptied when they are typechecked. @@ -89,7 +89,7 @@ class Namer { typer: Typer => * one, so that trees that are shared between different DefDefs can be independently * used as indices. It also contains a scope that contains nested parameters. */ - lazy val nestedTyper: mutable.AnyRefMap[Symbol, Typer] = new mutable.AnyRefMap + lazy val nestedTyper: mutable.HashMap[Symbol, Typer] = new mutable.HashMap /** We are entering symbols coming from a SourceLoader */ private var lateCompile = false @@ -878,7 +878,7 @@ class Namer { typer: Typer => case original: untpd.MemberDef => lazy val annotCtx = annotContext(original, sym) original.setMods: - original.mods.withAnnotations : + original.mods.withAnnotations: original.mods.annotations.mapConserve: annotTree => val cls = typedAheadAnnotationClass(annotTree)(using annotCtx) if (cls eq sym) @@ -1626,7 +1626,8 @@ class Namer { typer: Typer => } else { val pclazz = pt.typeSymbol - if pclazz.is(Final) then + // The second condition avoids generating a useless message (See #22236 for more details) + if pclazz.is(Final) && !(pclazz.is(Enum) && pclazz.isDerivedValueClass) then report.error(ExtendFinalClass(cls, pclazz), cls.srcPos) else if pclazz.isEffectivelySealed && pclazz.associatedFile != cls.associatedFile then if pclazz.is(Sealed) && !pclazz.is(JavaDefined) then @@ -2016,6 +2017,11 @@ class Namer { typer: Typer => paramFn: Type => Type, fallbackProto: Type )(using Context): Type = + /** Is this member tracked? This is true if it is marked as `tracked` or if + * it overrides a `tracked` member. To account for the later, `isTracked` + * is overriden to `true` as a side-effect of computing `inherited`. + */ + var isTracked: Boolean = sym.is(Tracked) /** A type for this definition that might be inherited from elsewhere: * If this is a setter parameter, the corresponding getter type. @@ -2051,8 +2057,10 @@ class Namer { typer: Typer => if paramss.isEmpty then info.widenExpr else NoType - val iRawInfo = - cls.info.nonPrivateDecl(sym.name).matchingDenotation(site, schema, sym.targetName).info + val iDenot = cls.info.nonPrivateDecl(sym.name).matchingDenotation(site, schema, sym.targetName) + val iSym = iDenot.symbol + if iSym.is(Tracked) then isTracked = true + val iRawInfo = iDenot.info val iResType = instantiatedResType(iRawInfo, paramss).asSeenFrom(site, cls) if (iResType.exists) typr.println(i"using inherited type for ${mdef.name}; raw: $iRawInfo, inherited: $iResType") @@ -2146,6 +2154,7 @@ class Namer { typer: Typer => if defaultTp.exists then TypeOps.SimplifyKeepUnchecked() else null) match case ctp: ConstantType if sym.isInlineVal => ctp + case tp if isTracked => tp case tp => TypeComparer.widenInferred(tp, pt, Widen.Unions) // Replace aliases to Unit by Unit itself. If we leave the alias in @@ -2156,7 +2165,7 @@ class Namer { typer: Typer => def lhsType = fullyDefinedType(cookedRhsType, "right-hand side", mdef.srcPos) //if (sym.name.toString == "y") println(i"rhs = $rhsType, cooked = $cookedRhsType") if (inherited.exists) - if sym.isInlineVal then lhsType else inherited + if sym.isInlineVal || isTracked then lhsType else inherited else { if (sym.is(Implicit)) mdef match { diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 346b9a7afabc..c941ffe74e18 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -268,6 +268,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // special cases: definitions beat imports, and named imports beat // wildcard imports, provided both are in contexts with same scope found + else if newPrec == WildImport && ctx.outersIterator.exists: ctx => + ctx.isImportContext && namedImportRef(ctx.importInfo.uncheckedNN).exists + then + // Don't let two ambiguous wildcard imports rule over + // a winning named import. See pos/i18529. + found else if !scala2pkg && !previous.isError && !found.isError then fail(AmbiguousReference(name, newPrec, prevPrec, prevCtx, @@ -2427,7 +2433,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer else if ctx.reporter.errorsReported then UnspecifiedErrorType else errorType(em"cannot infer type; expected type $pt is not fully defined", tree.srcPos)) - def typedTypeTree(tree: untpd.TypeTree, pt: Type)(using Context): Tree = + def typedTypeTree(tree: untpd.TypeTree, pt: Type)(using Context): Tree = { tree match case tree: untpd.DerivedTypeTree => tree.ensureCompletions @@ -2443,6 +2449,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer } case _ => completeTypeTree(InferredTypeTree(), pt, tree) + } def typedInLambdaTypeTree(tree: untpd.InLambdaTypeTree, pt: Type)(using Context): Tree = val tp = @@ -2854,7 +2861,6 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val nnInfo = rhs1.notNullInfo vdef1.withNotNullInfo(if sym.is(Lazy) then nnInfo.retractedInfo else nnInfo) } - private def retractDefDef(sym: Symbol)(using Context): Tree = // it's a discarded method (synthetic case class method or synthetic java record constructor or overridden member), drop it val canBeInvalidated: Boolean = @@ -3666,7 +3672,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer } /** Typecheck and adapt tree, returning a typed tree. Parameters as for `typedUnadapted` */ - def typed(tree: untpd.Tree, pt: Type, locked: TypeVars)(using Context): Tree = + def typed(tree: untpd.Tree, pt: Type, locked: TypeVars)(using Context): Tree = { trace(i"typing $tree, pt = $pt", typr, show = true) { record(s"typed $getClass") record("typed total") @@ -3678,6 +3684,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer tree.withType(WildcardType) else adapt(typedUnadapted(tree, pt, locked), pt, locked) } + } def typed(tree: untpd.Tree, pt: Type = WildcardType)(using Context): Tree = typed(tree, pt, ctx.typerState.ownedVars) @@ -3793,7 +3800,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer def typedExpr(tree: untpd.Tree, pt: Type = WildcardType)(using Context): Tree = withoutMode(Mode.PatternOrTypeBits)(typed(tree, pt)) - def typedType(tree: untpd.Tree, pt: Type = WildcardType, mapPatternBounds: Boolean = false)(using Context): Tree = + def typedType(tree: untpd.Tree, pt: Type = WildcardType, mapPatternBounds: Boolean = false)(using Context): Tree = { val tree1 = withMode(Mode.Type) { typed(tree, pt) } if mapPatternBounds && ctx.mode.is(Mode.Pattern) && !ctx.isAfterTyper then tree1 match @@ -3809,6 +3816,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case tree1 => tree1 else tree1 + } def typedPattern(tree: untpd.Tree, selType: Type = WildcardType)(using Context): Tree = withMode(Mode.Pattern)(typed(tree, selType)) diff --git a/compiler/src/dotty/tools/dotc/util/FreshNameCreator.scala b/compiler/src/dotty/tools/dotc/util/FreshNameCreator.scala index f3375028c95f..f95b4bf85a96 100644 --- a/compiler/src/dotty/tools/dotc/util/FreshNameCreator.scala +++ b/compiler/src/dotty/tools/dotc/util/FreshNameCreator.scala @@ -14,7 +14,7 @@ abstract class FreshNameCreator { object FreshNameCreator { class Default extends FreshNameCreator { protected var counter: Int = 0 - protected val counters: mutable.Map[String, Int] = mutable.AnyRefMap() withDefaultValue 0 + protected val counters: mutable.Map[String, Int] = mutable.HashMap() withDefaultValue 0 /** * Create a fresh name with the given prefix. It is guaranteed diff --git a/compiler/src/dotty/tools/repl/ParseResult.scala b/compiler/src/dotty/tools/repl/ParseResult.scala index 24a624173050..6c9f95d4dca2 100644 --- a/compiler/src/dotty/tools/repl/ParseResult.scala +++ b/compiler/src/dotty/tools/repl/ParseResult.scala @@ -93,6 +93,10 @@ object Reset { val command: String = ":reset" } +/** Toggle automatic printing of results */ +case object Silent extends Command: + val command: String = ":silent" + /** `:quit` exits the repl */ case object Quit extends Command { val command: String = ":quit" @@ -113,6 +117,7 @@ case object Help extends Command { |:imports show import history |:reset [options] reset the repl to its initial state, forgetting all session entries |:settings update compiler options, if possible + |:silent disable/enable automatic printing of results """.stripMargin } @@ -137,6 +142,7 @@ object ParseResult { TypeOf.command -> (arg => TypeOf(arg)), DocOf.command -> (arg => DocOf(arg)), Settings.command -> (arg => Settings(arg)), + Silent.command -> (_ => Silent), ) def apply(source: SourceFile)(using state: State): ParseResult = { diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index 486005658d79..3a2b8ce00bbe 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -60,12 +60,14 @@ import scala.util.Using * @param valIndex the index of next value binding for free expressions * @param imports a map from object index to the list of user defined imports * @param invalidObjectIndexes the set of object indexes that failed to initialize + * @param quiet whether we print evaluation results * @param context the latest compiler context */ case class State(objectIndex: Int, valIndex: Int, imports: Map[Int, List[tpd.Import]], invalidObjectIndexes: Set[Int], + quiet: Boolean, context: Context): def validObjectIndexes = (1 to objectIndex).filterNot(invalidObjectIndexes.contains(_)) @@ -114,7 +116,7 @@ class ReplDriver(settings: Array[String], } /** the initial, empty state of the REPL session */ - final def initialState: State = State(0, 0, Map.empty, Set.empty, rootCtx) + final def initialState: State = State(0, 0, Map.empty, Set.empty, false, rootCtx) /** Reset state of repl to the initial state * @@ -217,11 +219,6 @@ class ReplDriver(settings: Array[String], interpret(ParseResult.complete(input)) } - final def runQuietly(input: String)(using State): State = runBody { - val parsed = ParseResult(input) - interpret(parsed, quiet = true) - } - protected def runBody(body: => State): State = rendering.classLoader()(using rootCtx).asContext(withRedirectedOutput(body)) // TODO: i5069 @@ -290,10 +287,10 @@ class ReplDriver(settings: Array[String], .getOrElse(Nil) end completions - protected def interpret(res: ParseResult, quiet: Boolean = false)(using state: State): State = { + protected def interpret(res: ParseResult)(using state: State): State = { res match { case parsed: Parsed if parsed.trees.nonEmpty => - compile(parsed, state, quiet) + compile(parsed, state) case SyntaxErrors(_, errs, _) => displayErrors(errs) @@ -311,7 +308,7 @@ class ReplDriver(settings: Array[String], } /** Compile `parsed` trees and evolve `state` in accordance */ - private def compile(parsed: Parsed, istate: State, quiet: Boolean = false): State = { + private def compile(parsed: Parsed, istate: State): State = { def extractNewestWrapper(tree: untpd.Tree): Name = tree match { case PackageDef(_, (obj: untpd.ModuleDef) :: Nil) => obj.name.moduleClassName case _ => nme.NO_NAME @@ -362,11 +359,9 @@ class ReplDriver(settings: Array[String], given Ordering[Diagnostic] = Ordering[(Int, Int, Int)].on(d => (d.pos.line, -d.level, d.pos.column)) - if (!quiet) { - (definitions ++ warnings) - .sorted - .foreach(printDiagnostic) - } + (if istate.quiet then warnings else definitions ++ warnings) + .sorted + .foreach(printDiagnostic) updatedState } @@ -542,6 +537,8 @@ class ReplDriver(settings: Array[String], rootCtx = setupRootCtx(tokenize(arg).toArray, rootCtx) state.copy(context = rootCtx) + case Silent => state.copy(quiet = !state.quiet) + case Quit => // end of the world! state diff --git a/compiler/test-resources/repl/silent b/compiler/test-resources/repl/silent new file mode 100644 index 000000000000..9e851e8adb01 --- /dev/null +++ b/compiler/test-resources/repl/silent @@ -0,0 +1,25 @@ +scala>:silent +scala> 1+1 +scala> case class A(x: Int) +scala> A("string") +-- [E007] Type Mismatch Error: ------------------------------------------------- +1 | A("string") + | ^^^^^^^^ + | Found: ("string" : String) + | Required: Int + | + | longer explanation available when compiling with `-explain` +1 error found +scala> Option[Int](2) match { case Some(x) => x } +1 warning found +-- [E029] Pattern Match Exhaustivity Warning: ---------------------------------- +1 | Option[Int](2) match { case Some(x) => x } + | ^^^^^^^^^^^^^^ + | match may not be exhaustive. + | + | It would fail on pattern case: None + | + | longer explanation available when compiling with `-explain` +scala>:silent +scala> 1 + 2 +val res2: Int = 3 diff --git a/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala b/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala index a412848eaa98..07834684d33b 100644 --- a/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala +++ b/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala @@ -105,7 +105,7 @@ class ScalaSettingsTests: createTestCase(settings.YdropComments , settings.XdropComments), createTestCase(settings.YcookComments , settings.XcookComments), createTestCase(settings.YreadComments , settings.XreadComments), - createTestCase(settings.YnoDecodeStacktraces , settings.XnoDecodeStacktraces), + createTestCase(settings.YnoDecodeStacktraces , settings.XnoEnrichErrorMessages), createTestCase(settings.YnoEnrichErrorMessages, settings.XnoEnrichErrorMessages), createTestCase(settings.YdebugMacros , settings.XdebugMacros), // createTestCase(settings.YjavaTasty , settings.XjavaTasty), @@ -134,7 +134,7 @@ class ScalaSettingsTests: createTestCase(settings.YdropComments , settings.XdropComments), createTestCase(settings.YcookComments , settings.XcookComments), createTestCase(settings.YreadComments , settings.XreadComments), - createTestCase(settings.YnoDecodeStacktraces , settings.XnoDecodeStacktraces), + createTestCase(settings.YnoDecodeStacktraces , settings.XnoEnrichErrorMessages), createTestCase(settings.YnoEnrichErrorMessages, settings.XnoEnrichErrorMessages), createTestCase(settings.YdebugMacros , settings.XdebugMacros), // createTestCase(settings.YjavaTasty , settings.XjavaTasty), @@ -175,7 +175,7 @@ class ScalaSettingsTests: createTestCase(settings.YdropComments , settings.XdropComments), createTestCase(settings.YcookComments , settings.XcookComments), createTestCase(settings.YreadComments , settings.XreadComments), - createTestCase(settings.YnoDecodeStacktraces , settings.XnoDecodeStacktraces), + createTestCase(settings.YnoDecodeStacktraces , settings.XnoEnrichErrorMessages), createTestCase(settings.YnoEnrichErrorMessages, settings.XnoEnrichErrorMessages), createTestCase(settings.YdebugMacros , settings.XdebugMacros), // createTestCase(settings.YjavaTasty , settings.XjavaTasty), diff --git a/compiler/test/dotty/tools/repl/ReplCompilerTests.scala b/compiler/test/dotty/tools/repl/ReplCompilerTests.scala index 221eb8acb9de..d32b28647c32 100644 --- a/compiler/test/dotty/tools/repl/ReplCompilerTests.scala +++ b/compiler/test/dotty/tools/repl/ReplCompilerTests.scala @@ -493,6 +493,24 @@ class ReplCompilerTests extends ReplTest: assertTrue(all.head.startsWith("-- [E103] Syntax Error")) assertTrue(all.exists(_.trim().startsWith("| Illegal start of statement: this modifier is not allowed here"))) + @Test def `i16250a`: Unit = initially: + val hints = List( + "this language import is not allowed in the REPL", + "To use this language feature, include the flag `-language:experimental.captureChecking` when starting the REPL" + ) + run("import language.experimental.captureChecking") + val all = lines() + assertTrue(hints.forall(hint => all.exists(_.contains(hint)))) + + @Test def `i16250b`: Unit = initially: + val hints = List( + "this language import is not allowed in the REPL", + "To use this language feature, include the flag `-language:experimental.pureFunctions` when starting the REPL" + ) + run("import language.experimental.pureFunctions") + val all = lines() + assertTrue(hints.forall(hint => all.exists(_.contains(hint)))) + object ReplCompilerTests: private val pattern = Pattern.compile("\\r[\\n]?|\\n"); diff --git a/compiler/test/dotty/tools/repl/TabcompleteTests.scala b/compiler/test/dotty/tools/repl/TabcompleteTests.scala index 95419824d9d1..01b9d1109994 100644 --- a/compiler/test/dotty/tools/repl/TabcompleteTests.scala +++ b/compiler/test/dotty/tools/repl/TabcompleteTests.scala @@ -217,6 +217,7 @@ class TabcompleteTests extends ReplTest { ":quit", ":reset", ":settings", + ":silent", ":type" ), tabComplete(":") diff --git a/dist/libexec/cli-common-platform b/dist/libexec/cli-common-platform index a5906e882bb4..e56f5221dbf2 100644 --- a/dist/libexec/cli-common-platform +++ b/dist/libexec/cli-common-platform @@ -1,3 +1,3 @@ #!/usr/bin/env bash -SCALA_CLI_CMD_BASH=("\"$JAVACMD\"" "-jar \"$PROG_HOME/bin/scala-cli.jar\"") +SCALA_CLI_CMD_BASH=("\"$JAVACMD\"" "-jar \"$PROG_HOME/libexec/scala-cli.jar\"") diff --git a/dist/libexec/cli-common-platform.bat b/dist/libexec/cli-common-platform.bat index 99103266c1d9..45b09f3460e6 100644 --- a/dist/libexec/cli-common-platform.bat +++ b/dist/libexec/cli-common-platform.bat @@ -2,4 +2,4 @@ @rem we need to escape % in the java command path, for some reason this doesnt work in common.bat set "_JAVACMD=!_JAVACMD:%%=%%%%!" -set SCALA_CLI_CMD_WIN="%_JAVACMD%" "-jar" "%_PROG_HOME%\bin\scala-cli.jar" \ No newline at end of file +set SCALA_CLI_CMD_WIN="%_JAVACMD%" "-jar" "%_PROG_HOME%\libexec\scala-cli.jar" diff --git a/dist/libexec/common-shared b/dist/libexec/common-shared index 8c85993a5283..fa1e62c09241 100644 --- a/dist/libexec/common-shared +++ b/dist/libexec/common-shared @@ -28,7 +28,7 @@ function onExit() { # to reenable echo if we are interrupted before completing. trap onExit INT TERM EXIT -unset cygwin mingw msys darwin conemu +unset cygwin mingw msys darwin # COLUMNS is used together with command line option '-pageWidth'. if command -v tput >/dev/null 2>&1; then @@ -57,8 +57,6 @@ esac unset CYGPATHCMD if [[ ${cygwin-} || ${mingw-} || ${msys-} ]]; then - # ConEmu terminal is incompatible with jna-5.*.jar - [[ (${CONEMUANSI-} || ${ConEmuANSI-}) ]] && conemu=true # cygpath is used by various windows shells: cygwin, git-sdk, gitbash, msys, etc. CYGPATHCMD=`which cygpath 2>/dev/null` case "$TERM" in diff --git a/docs/_docs/contributing/getting-started.md b/docs/_docs/contributing/getting-started.md index b6e3e4fac00a..c660edb2400e 100644 --- a/docs/_docs/contributing/getting-started.md +++ b/docs/_docs/contributing/getting-started.md @@ -146,6 +146,6 @@ The main development discussion channels are: [java11]: https://www.oracle.com/java/technologies/javase-jdk11-downloads.html [adopt]: https://adoptopenjdk.net/ [compat]: https://docs.scala-lang.org/overviews/jdk-compatibility/overview.html -[scala-cla]: https://www.lightbend.com/contribute/cla/scala +[scala-cla]: https://contribute.akka.io/cla/scala [dotty-issue]: https://github.com/scala/scala3/issues [dotty-discussion]: https://github.com/scala/scala3/discussions diff --git a/docs/_docs/internals/syntax.md b/docs/_docs/internals/syntax.md index d0074bb503c2..665b4f5144ba 100644 --- a/docs/_docs/internals/syntax.md +++ b/docs/_docs/internals/syntax.md @@ -141,7 +141,7 @@ type val var while with yield ### Soft keywords ``` -as derives end erased extension infix inline opaque open throws transparent using | * + - +as derives end erased extension infix inline opaque open throws tracked transparent using | * + - ``` See the [separate section on soft keywords](../reference/soft-modifier.md) for additional @@ -381,7 +381,7 @@ ClsParamClause ::= [nl] ‘(’ ClsParams ‘)’ | [nl] ‘(’ ‘using’ (ClsParams | FunArgTypes) ‘)’ ClsParams ::= ClsParam {‘,’ ClsParam} ClsParam ::= {Annotation} ValDef(mods, id, tpe, expr) -- point of mods on val/var - [{Modifier | ‘tracked’} (‘val’ | ‘var’)] Param + [{Modifier} (‘val’ | ‘var’)] Param DefParamClauses ::= DefParamClause { DefParamClause } -- and two DefTypeParamClause cannot be adjacent DefParamClause ::= DefTypeParamClause @@ -418,6 +418,7 @@ LocalModifier ::= ‘abstract’ | ‘transparent’ | ‘infix’ | ‘erased’ + | ‘tracked’ AccessModifier ::= (‘private’ | ‘protected’) [AccessQualifier] AccessQualifier ::= ‘[’ id ‘]’ diff --git a/docs/_docs/reference/experimental/modularity.md b/docs/_docs/reference/experimental/modularity.md index a989b71770af..66d4c0c23ede 100644 --- a/docs/_docs/reference/experimental/modularity.md +++ b/docs/_docs/reference/experimental/modularity.md @@ -108,14 +108,6 @@ This works as it should now. Without the addition of `tracked` to the parameter of `SetFunctor` typechecking would immediately lose track of the element type `T` after an `add`, and would therefore fail. -**Syntax Change** - -``` -ClsParam ::= {Annotation} [{Modifier | ‘tracked’} (‘val’ | ‘var’)] Param -``` - -The (soft) `tracked` modifier is only allowed for `val` parameters of classes. - **Discussion** Since `tracked` is so useful, why not assume it by default? First, `tracked` makes sense only for `val` parameters. If a class parameter is not also a field declared using `val` then there's nothing to refine in the constructor result type. One could think of at least making all `val` parameters tracked by default, but that would be a backwards incompatible change. For instance, the following code would break: @@ -134,6 +126,38 @@ only if the class refers to a type member of `x`. But it turns out that this scheme is unimplementable since it would quickly lead to cyclic references when typechecking recursive class graphs. So an explicit `tracked` looks like the best available option. +## Tracked members + +The `tracked` modifier can also be used for `val` members of classes and traits +to force the type of the member (or it's overriding member) to be as exact as +possible. More precisely, it will will assign the `tracked` member the infered +type of the rhs. For instance, consider the following definition: + +```scala +trait F: + tracked val a: Int + tracked val b: Int + +class N extends F: + val a = 22 // a.type =:= 22 + val b: Int = 22 // b.type =:= Int + tracked val c = 22 // c.type =:= 22 +``` + +Here, the `tracked` modifier ensures that the type of `a` in `N` is `22` and not +`Int`. But the type of `b` is `N` is `Int` since it's explicitly declared as +`Int`. `tracked` members can also be immediately initialized, as in the case of +`c`. + +## Tracked syntax change + +``` +LocalModifier ::= ‘tracked’ +``` + +The (soft) `tracked` modifier is allowed as a local modifier. + + ## Allow Class Parents to be Refined Types Since `tracked` parameters create refinements in constructor types, diff --git a/docs/_docs/reference/metaprogramming/staging.md b/docs/_docs/reference/metaprogramming/staging.md index 1c154e09f50e..001ae622eabc 100644 --- a/docs/_docs/reference/metaprogramming/staging.md +++ b/docs/_docs/reference/metaprogramming/staging.md @@ -98,9 +98,9 @@ scala -with-compiler -classpath out Test ## Example Now take exactly the same example as in [Macros](./macros.md). Assume that we -do not want to pass an array statically but generate code at run-time and pass +do not want to pass a base double value statically but generate code at run-time and pass the value, also at run-time. Note, how we make a future-stage function of type -`Expr[Array[Int] => Int]` in line 6 below. Using `staging.run { ... }` we can evaluate an +`Expr[Double => Double]` in line 6 below. Using `staging.run { ... }` we can evaluate an expression at runtime. Within the scope of `staging.run` we can also invoke `show` on an expression to get a source-like representation of the expression. @@ -110,12 +110,12 @@ import scala.quoted.* // make available the necessary compiler for runtime code generation given staging.Compiler = staging.Compiler.make(getClass.getClassLoader) -val f: Array[Int] => Int = staging.run { - val stagedSum: Expr[Array[Int] => Int] = - '{ (arr: Array[Int]) => ${sum('arr)}} - println(stagedSum.show) // Prints "(arr: Array[Int]) => { var sum = 0; ... }" - stagedSum +val power3: Double => Double = staging.run { + val stagedPower3: Expr[Double => Double] = + '{ (x: Double) => ${ unrolledPowerCode('x, 3) } } + println(stagedPower3.show) // Prints "((x: scala.Double) => x.*(x.*(x)))" + stagedPower3 } -f.apply(Array(1, 2, 3)) // Returns 6 +power3.apply(2.0) // Returns 8.0 ``` diff --git a/library/src/scala/runtime/coverage/Invoker.scala b/library/src/scala/runtime/coverage/Invoker.scala index c35c6c2ec7df..b3216ec37c67 100644 --- a/library/src/scala/runtime/coverage/Invoker.scala +++ b/library/src/scala/runtime/coverage/Invoker.scala @@ -3,7 +3,7 @@ package scala.runtime.coverage import scala.annotation.internal.sharable import scala.annotation.nowarn import scala.collection.concurrent.TrieMap -import scala.collection.mutable.{BitSet, AnyRefMap} +import scala.collection.mutable.{BitSet, HashMap} import java.io.{File, FileWriter} import java.nio.file.Files @@ -12,7 +12,7 @@ object Invoker { private val runtimeUUID = java.util.UUID.randomUUID() private val MeasurementsPrefix = "scoverage.measurements." - private val threadFiles = new ThreadLocal[AnyRefMap[String, FileWriter]] + private val threadFiles = new ThreadLocal[HashMap[String, FileWriter]] private val dataDirToSet = TrieMap.empty[String, BitSet] /** We record that the given id has been invoked by appending its id to the coverage data file. @@ -38,7 +38,7 @@ object Invoker { if added then var writers = threadFiles.get() if writers == null then - writers = AnyRefMap.empty + writers = HashMap.empty threadFiles.set(writers) val writer = writers.getOrElseUpdate( dataDir, diff --git a/presentation-compiler/src/main/dotty/tools/pc/InferExpectedType.scala b/presentation-compiler/src/main/dotty/tools/pc/InferExpectedType.scala index 260a28392093..3d65f69621e1 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/InferExpectedType.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/InferExpectedType.scala @@ -7,6 +7,7 @@ import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.Flags import dotty.tools.dotc.core.StdNames import dotty.tools.dotc.core.Symbols +import dotty.tools.dotc.core.Symbols.defn import dotty.tools.dotc.core.Types.* import dotty.tools.dotc.core.Types.Type import dotty.tools.dotc.interactive.Interactive @@ -61,7 +62,7 @@ class InferExpectedType( object InterCompletionType: def inferType(path: List[Tree])(using Context): Option[Type] = path match - case (lit: Literal) :: Select(Literal(_), _) :: Apply(Select(Literal(_), _), List(Literal(Constant(null)))) :: rest => inferType(rest, lit.span) + case (lit: Literal) :: Select(Literal(_), _) :: Apply(Select(Literal(_), _), List(s: Select)) :: rest if s.symbol == defn.Predef_undefined => inferType(rest, lit.span) case ident :: rest => inferType(rest, ident.span) case _ => None diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/SingletonCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/SingletonCompletions.scala index 6e59c9afca3a..53c4e01980bc 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/SingletonCompletions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/SingletonCompletions.scala @@ -7,18 +7,13 @@ import dotty.tools.pc.completions.CompletionValue.SingletonValue import dotty.tools.dotc.ast.tpd.* import dotty.tools.dotc.core.Constants.Constant import dotty.tools.dotc.core.Contexts.Context -import dotty.tools.dotc.core.Flags -import dotty.tools.dotc.core.StdNames import dotty.tools.dotc.core.Symbols import dotty.tools.dotc.core.Types.AndType import dotty.tools.dotc.core.Types.AppliedType import dotty.tools.dotc.core.Types.ConstantType import dotty.tools.dotc.core.Types.OrType -import dotty.tools.dotc.core.Types.TermRef import dotty.tools.dotc.core.Types.Type import dotty.tools.dotc.core.Types.TypeRef -import dotty.tools.dotc.util.Spans.Span -import dotty.tools.dotc.core.Symbols.defn object SingletonCompletions: def contribute( @@ -55,79 +50,3 @@ object SingletonCompletions: collectSingletons(tpe1).intersect(collectSingletons(tpe2)) case _ => Nil -object InterCompletionType: - def inferType(path: List[Tree])(using Context): Option[Type] = - path match - case (lit: Literal) :: Select(Literal(_), _) :: Apply(Select(Literal(_), _), List(s: Select)) :: rest if s.symbol == defn.Predef_undefined => - inferType(rest, lit.span) - case ident :: rest => inferType(rest, ident.span) - case _ => None - - def inferType(path: List[Tree], span: Span)(using Context): Option[Type] = - path match - case Apply(head, List(p : Select)) :: rest if p.name == StdNames.nme.??? && p.qualifier.symbol.name == StdNames.nme.Predef && p.span.isSynthetic => - inferType(rest, span) - case Block(_, expr) :: rest if expr.span.contains(span) => - inferType(rest, span) - case If(cond, _, _) :: rest if !cond.span.contains(span) => - inferType(rest, span) - case Typed(expr, tpt) :: _ if expr.span.contains(span) && !tpt.tpe.isErroneous => Some(tpt.tpe) - case Block(_, expr) :: rest if expr.span.contains(span) => - inferType(rest, span) - case Bind(_, body) :: rest if body.span.contains(span) => inferType(rest, span) - case Alternative(_) :: rest => inferType(rest, span) - case Try(block, _, _) :: rest if block.span.contains(span) => inferType(rest, span) - case CaseDef(_, _, body) :: Try(_, cases, _) :: rest if body.span.contains(span) && cases.exists(_.span.contains(span)) => inferType(rest, span) - case If(cond, _, _) :: rest if !cond.span.contains(span) => inferType(rest, span) - case CaseDef(_, _, body) :: Match(_, cases) :: rest if body.span.contains(span) && cases.exists(_.span.contains(span)) => - inferType(rest, span) - case NamedArg(_, arg) :: rest if arg.span.contains(span) => inferType(rest, span) - // x match - // case @@ - case CaseDef(pat, _, _) :: Match(sel, cases) :: rest if pat.span.contains(span) && cases.exists(_.span.contains(span)) && !sel.tpe.isErroneous => - sel.tpe match - case tpe: TermRef => Some(tpe.symbol.info).filterNot(_.isErroneous) - case tpe => Some(tpe) - // List(@@) - case SeqLiteral(_, tpe) :: _ if !tpe.tpe.isErroneous => - Some(tpe.tpe) - // val _: T = @@ - // def _: T = @@ - case (defn: ValOrDefDef) :: rest if !defn.tpt.tpe.isErroneous => Some(defn.tpt.tpe) - // f(@@) - case (app: Apply) :: rest => - val param = - for { - ind <- app.args.zipWithIndex.collectFirst { - case (arg, id) if arg.span.contains(span) => id - } - params <- app.symbol.paramSymss.find(!_.exists(_.isTypeParam)) - param <- params.get(ind) - } yield param.info - param match - // def f[T](a: T): T = ??? - // f[Int](@@) - // val _: Int = f(@@) - case Some(t : TypeRef) if t.symbol.is(Flags.TypeParam) => - for { - (typeParams, args) <- - app match - case Apply(TypeApply(fun, args), _) => - val typeParams = fun.symbol.paramSymss.headOption.filter(_.forall(_.isTypeParam)) - typeParams.map((_, args.map(_.tpe))) - // val f: (j: "a") => Int - // f(@@) - case Apply(Select(v, StdNames.nme.apply), _) => - v.symbol.info match - case AppliedType(des, args) => - Some((des.typeSymbol.typeParams, args)) - case _ => None - case _ => None - ind = typeParams.indexOf(t.symbol) - tpe <- args.get(ind) - if !tpe.isErroneous - } yield tpe - case Some(tpe) => Some(tpe) - case _ => None - case _ => None - diff --git a/project/Build.scala b/project/Build.scala index f36171aabbcd..806412931696 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -93,11 +93,12 @@ object Build { /** Version of the Scala compiler used to build the artifacts. * Reference version should track the latest version pushed to Maven: - * - In main branch it should be the last RC version (using experimental TASTy required for non-bootstrapped tests) + * - In main branch it should be the last RC version * - In release branch it should be the last stable release - * 3.6.0-RC1 was released as 3.6.0 - it's having and experimental TASTy version + * + * Warning: Change of this variable needs to be consulted with `expectedTastyVersion` */ - val referenceVersion = "3.6.0" + val referenceVersion = "3.6.3-RC1" /** Version of the Scala compiler targeted in the current release cycle * Contains a version without RC/SNAPSHOT/NIGHTLY specific suffixes @@ -105,6 +106,8 @@ object Build { * * Should only be referred from `dottyVersion` or settings/tasks requiring simplified version string, * eg. `compatMode` or Windows native distribution version. + * + * Warning: Change of this variable might require updating `expectedTastyVersion` */ val developedVersion = "3.6.4" @@ -117,6 +120,25 @@ object Build { */ val baseVersion = s"$developedVersion-RC1" + /** The version of TASTY that should be emitted, checked in runtime test + * For defails on how TASTY version should be set see related discussions: + * - https://github.com/scala/scala3/issues/13447#issuecomment-912447107 + * - https://github.com/scala/scala3/issues/14306#issuecomment-1069333516 + * - https://github.com/scala/scala3/pull/19321 + * + * Simplified rules, given 3.$minor.$patch = $developedVersion + * - Major version is always 28 + * - TASTY minor version: + * - in main (NIGHTLY): {if $patch == 0 then $minor else ${minor + 1}} + * - in release branch is always equal to $minor + * - TASTY experimental version: + * - in main (NIGHTLY) is always experimental + * - in release candidate branch is experimental if {patch == 0} + * - in stable release is always non-experimetnal + */ + val expectedTastyVersion = "28.7-experimental-1" + checkReleasedTastyVersion() + /** Final version of Scala compiler, controlled by environment variables. */ val dottyVersion = { if (isRelease) baseVersion @@ -149,9 +171,9 @@ object Build { * For a developedVersion `3.M.P` the mimaPreviousDottyVersion should be set to: * - `3.M.0` if `P > 0` * - `3.(M-1).0` if `P = 0` - * 3.6.1 is an exception from this rule - 3.6.0 was a broken release + * 3.6.2 is an exception from this rule - 3.6.0 was a broken release, 3.6.1 was hotfix (unstable) release */ - val mimaPreviousDottyVersion = "3.6.1" + val mimaPreviousDottyVersion = "3.6.2" /** LTS version against which we check binary compatibility. * @@ -739,11 +761,11 @@ object Build { // get libraries onboard libraryDependencies ++= Seq( - "org.scala-lang.modules" % "scala-asm" % "9.7.0-scala-2", // used by the backend + "org.scala-lang.modules" % "scala-asm" % "9.7.1-scala-1", // used by the backend Dependencies.compilerInterface, - "org.jline" % "jline-reader" % "3.27.0", // used by the REPL - "org.jline" % "jline-terminal" % "3.27.0", - "org.jline" % "jline-terminal-jna" % "3.27.0", // needed for Windows + "org.jline" % "jline-reader" % "3.27.1", // used by the REPL + "org.jline" % "jline-terminal" % "3.27.1", + "org.jline" % "jline-terminal-jni" % "3.27.1", // needed for Windows ("io.get-coursier" %% "coursier" % "2.0.16" % Test).cross(CrossVersion.for3Use2_13), ), @@ -1425,7 +1447,7 @@ object Build { lazy val `scala3-presentation-compiler` = project.in(file("presentation-compiler")) .withCommonSettings(Bootstrapped) - .dependsOn(`scala3-compiler-bootstrapped`, `scala3-library-bootstrapped`, `scala3-presentation-compiler-testcases`) + .dependsOn(`scala3-compiler-bootstrapped`, `scala3-library-bootstrapped`, `scala3-presentation-compiler-testcases` % "test->test") .settings(presentationCompilerSettings) .settings(scala3PresentationCompilerBuildInfo) .settings( @@ -2235,7 +2257,14 @@ object Build { // ======== Universal / stage := (Universal / stage).dependsOn(republish).value, Universal / packageBin := (Universal / packageBin).dependsOn(republish).value, - Universal / packageZipTarball := (Universal / packageZipTarball).dependsOn(republish).value, + Universal / packageZipTarball := (Universal / packageZipTarball).dependsOn(republish) + .map { archiveFile => + // Rename .tgz to .tar.gz for consistency with previous versions + val renamedFile = archiveFile.getParentFile() / archiveFile.getName.replaceAll("\\.tgz$", ".tar.gz") + IO.move(archiveFile, renamedFile) + renamedFile + } + .value, // ======== Universal / mappings ++= directory(dist.base / "bin"), Universal / mappings ++= directory(republishRepo.value / "maven2"), @@ -2424,6 +2453,9 @@ object Build { settings(disableDocSetting). settings( versionScheme := Some("semver-spec"), + Test / envVars ++= Map( + "EXPECTED_TASTY_VERSION" -> expectedTastyVersion, + ), if (mode == Bootstrapped) Def.settings( commonMiMaSettings, mimaForwardIssueFilters := MiMaFilters.TastyCore.ForwardsBreakingChanges, @@ -2473,6 +2505,34 @@ object Build { case Bootstrapped => commonBootstrappedSettings }) } + + /* Tests TASTy version invariants during NIGHLY, RC or Stable releases */ + def checkReleasedTastyVersion(): Unit = { + lazy val (scalaMinor, scalaPatch, scalaIsRC) = baseVersion.split("\\.|-").take(4) match { + case Array("3", minor, patch) => (minor.toInt, patch.toInt, false) + case Array("3", minor, patch, _) => (minor.toInt, patch.toInt, true) + case other => sys.error(s"Invalid Scala base version string: $baseVersion") + } + lazy val (tastyMinor, tastyIsExperimental) = expectedTastyVersion.split("\\.|-").take(4) match { + case Array("28", minor) => (minor.toInt, false) + case Array("28", minor, "experimental", _) => (minor.toInt, true) + case other => sys.error(s"Invalid TASTy version string: $expectedTastyVersion") + } + + if(isNightly) { + assert(tastyIsExperimental, "TASTY needs to be experimental in nightly builds") + val expectedTastyMinor = if(scalaPatch == 0) scalaMinor else scalaMinor + 1 + assert(tastyMinor == expectedTastyMinor, "Invalid TASTy minor version") + } + + if(isRelease) { + assert(scalaMinor == tastyMinor, "Minor versions of TASTY vesion and Scala version should match in release builds") + if (scalaIsRC && scalaPatch == 0) + assert(tastyIsExperimental, "TASTy should be experimental when releasing a new minor version RC") + else + assert(!tastyIsExperimental, "Stable version cannot use experimental TASTY") + } + } } object ScaladocConfigs { diff --git a/project/scripts/check-cla.sh b/project/scripts/check-cla.sh deleted file mode 100755 index dbb148d3c652..000000000000 --- a/project/scripts/check-cla.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash -set -eux - -echo "Pull request submitted by $AUTHOR"; -if [[ "$AUTHOR" == "github-actions[bot]" || "$AUTHOR" == "dependabot[bot]" ]] ; then - echo "CLA check for $AUTHOR successful"; -else - signed=$(curl -L -s "https://contribute.akka.io/contribute/cla/scala/check/$AUTHOR" | jq -r ".signed"); - if [ "$signed" = "true" ] ; then - echo "CLA check for $AUTHOR successful"; - else - echo "CLA check for $AUTHOR failed"; - echo "Please sign the Scala CLA to contribute to the Scala compiler."; - echo "Go to https://contribute.akka.io/contribute/cla/scala and then"; - echo "comment on the pull request to ask for a new check."; - echo ""; - echo "Check if CLA is signed: https://contribute.akka.io/contribute/cla/scala/check/$AUTHOR"; - exit 1; - fi; -fi; diff --git a/tasty/src/dotty/tools/tasty/TastyFormat.scala b/tasty/src/dotty/tools/tasty/TastyFormat.scala index 8f5f9d57a8a5..8ff590fefec5 100644 --- a/tasty/src/dotty/tools/tasty/TastyFormat.scala +++ b/tasty/src/dotty/tools/tasty/TastyFormat.scala @@ -324,7 +324,7 @@ object TastyFormat { * compatibility, but remains backwards compatible, with all * preceding `MinorVersion`. */ - final val MinorVersion: Int = 6 + final val MinorVersion: Int = 7 /** Natural Number. The `ExperimentalVersion` allows for * experimentation with changes to TASTy without committing diff --git a/tasty/test/dotty/tools/tasty/BuildTastyVersionTest.scala b/tasty/test/dotty/tools/tasty/BuildTastyVersionTest.scala new file mode 100644 index 000000000000..d2e62e1f9eb0 --- /dev/null +++ b/tasty/test/dotty/tools/tasty/BuildTastyVersionTest.scala @@ -0,0 +1,26 @@ +package dotty.tools.tasty + +import org.junit.Assert._ +import org.junit.Test + +import TastyBuffer._ + +// Tests ensuring TASTY version emitted by compiler is matching expected TASTY version +class BuildTastyVersionTest { + + val CurrentTastyVersion = TastyVersion(TastyFormat.MajorVersion, TastyFormat.MinorVersion, TastyFormat.ExperimentalVersion) + + // Needs to be defined in build Test/envVars + val ExpectedTastyVersionEnvVar = "EXPECTED_TASTY_VERSION" + + @Test def testBuildTastyVersion(): Unit = { + val expectedVersion = sys.env.get(ExpectedTastyVersionEnvVar) + .getOrElse(fail(s"Env variable $ExpectedTastyVersionEnvVar not defined")) + .match { + case s"$major.$minor-experimental-$experimental" => TastyVersion(major.toInt, minor.toInt, experimental.toInt) + case s"$major.$minor" if minor.forall(_.isDigit) => TastyVersion(major.toInt, minor.toInt, 0) + case other => fail(s"Invalid TASTY version string: $other") + } + assertEquals(CurrentTastyVersion, expectedVersion) + } +} diff --git a/tests/neg/abstract-tracked-1.scala b/tests/neg/abstract-tracked-1.scala new file mode 100644 index 000000000000..0aef9f938816 --- /dev/null +++ b/tests/neg/abstract-tracked-1.scala @@ -0,0 +1,12 @@ +import scala.language.experimental.modularity +import scala.language.future + +trait F: + tracked val a: Int + +class G: + val a: Int = 1 + +def Test = + val g = new G + summon[g.a.type <:< 1] // error diff --git a/tests/neg/abstract-tracked.check b/tests/neg/abstract-tracked.check new file mode 100644 index 000000000000..70a85e81df85 --- /dev/null +++ b/tests/neg/abstract-tracked.check @@ -0,0 +1,20 @@ +-- [E156] Syntax Error: tests/neg/abstract-tracked.scala:4:14 ---------------------------------------------------------- +4 |tracked trait F // error + |^^^^^^^^^^^^^^^ + |Modifier tracked is not allowed for this definition +-- [E156] Syntax Error: tests/neg/abstract-tracked.scala:9:15 ---------------------------------------------------------- +9 |tracked object O // error + |^^^^^^^^^^^^^^^^ + |Modifier tracked is not allowed for this definition +-- [E156] Syntax Error: tests/neg/abstract-tracked.scala:11:14 --------------------------------------------------------- +11 |tracked class C // error + |^^^^^^^^^^^^^^^ + |Modifier tracked is not allowed for this definition +-- [E156] Syntax Error: tests/neg/abstract-tracked.scala:7:14 ---------------------------------------------------------- +7 | tracked def f: F // error + | ^^^^^^^^^^^^^^^^ + | Modifier tracked is not allowed for this definition +-- [E156] Syntax Error: tests/neg/abstract-tracked.scala:14:14 --------------------------------------------------------- +14 | tracked val x = 1 // error + | ^^^^^^^^^^^^^^^^^ + | Modifier tracked is not allowed for this definition diff --git a/tests/neg/abstract-tracked.scala b/tests/neg/abstract-tracked.scala new file mode 100644 index 000000000000..ff4a7ea8174f --- /dev/null +++ b/tests/neg/abstract-tracked.scala @@ -0,0 +1,14 @@ +import scala.language.experimental.modularity +import scala.language.future + +tracked trait F // error + +trait G: + tracked def f: F // error + +tracked object O // error + +tracked class C // error + +def f = + tracked val x = 1 // error diff --git a/tests/neg/given-triangle.check b/tests/neg/given-triangle.check index f366c18e78f0..8a05ed4b3129 100644 --- a/tests/neg/given-triangle.check +++ b/tests/neg/given-triangle.check @@ -7,6 +7,6 @@ | (given_B : B) |and | (given_A : A) - |will change. - |Current choice : the first alternative - |New choice from Scala 3.7: the second alternative + |will change in the future release. + |Current choice : the first alternative + |Choice from Scala 3.7 : the second alternative diff --git a/tests/neg/i21944.check b/tests/neg/i21944.check new file mode 100644 index 000000000000..591447c6a510 --- /dev/null +++ b/tests/neg/i21944.check @@ -0,0 +1,4 @@ +-- [E206] Syntax Error: tests/neg/i21944.scala:1:5 --------------------------------------------------------------------- +1 |enum Orientation extends AnyVal: // error + | ^ + | class Orientation may not be a value class diff --git a/tests/neg/i21944.scala b/tests/neg/i21944.scala new file mode 100644 index 000000000000..bf335e56c671 --- /dev/null +++ b/tests/neg/i21944.scala @@ -0,0 +1,2 @@ +enum Orientation extends AnyVal: // error + case North, South, East, West diff --git a/tests/neg/i2887b.check b/tests/neg/i2887b.check index 5bd5f570fbf7..eb89f8582e5a 100644 --- a/tests/neg/i2887b.check +++ b/tests/neg/i2887b.check @@ -4,7 +4,7 @@ | Recursion limit exceeded. | Maybe there is an illegal cyclic reference? | If that's not the case, you could also try to increase the stacksize using the -Xss JVM option. - | For the unprocessed stack trace, compile with -Xno-decode-stacktraces. + | For the unprocessed stack trace, compile with -Xno-enrich-error-messages. | A recurring operation is (inner to outer): | | try to instantiate Z[Z] diff --git a/tests/neg/tracked.check b/tests/neg/tracked.check index 14a4d2a08300..3494c401a007 100644 --- a/tests/neg/tracked.check +++ b/tests/neg/tracked.check @@ -6,22 +6,6 @@ 7 | def foo(tracked a: Int) = // error | ^ | ':' expected, but identifier found --- Error: tests/neg/tracked.scala:8:12 --------------------------------------------------------------------------------- -8 | tracked val b: Int = 2 // error - | ^^^ - | end of statement expected but 'val' found --- Error: tests/neg/tracked.scala:11:10 -------------------------------------------------------------------------------- -11 | tracked object Foo // error // error - | ^^^^^^ - | end of statement expected but 'object' found --- Error: tests/neg/tracked.scala:14:10 -------------------------------------------------------------------------------- -14 | tracked class D // error // error - | ^^^^^ - | end of statement expected but 'class' found --- Error: tests/neg/tracked.scala:17:10 -------------------------------------------------------------------------------- -17 | tracked type T = Int // error // error - | ^^^^ - | end of statement expected but 'type' found -- Error: tests/neg/tracked.scala:20:25 -------------------------------------------------------------------------------- 20 | given g2: (tracked val x: Int) => C = C(x) // error | ^^^^^^^^^^^^^^^^^^ @@ -30,21 +14,19 @@ 4 |class C2(tracked var x: Int) // error | ^ | mutable variables may not be `tracked` --- [E006] Not Found Error: tests/neg/tracked.scala:11:2 ---------------------------------------------------------------- -11 | tracked object Foo // error // error - | ^^^^^^^ - | Not found: tracked - | - | longer explanation available when compiling with `-explain` --- [E006] Not Found Error: tests/neg/tracked.scala:14:2 ---------------------------------------------------------------- -14 | tracked class D // error // error - | ^^^^^^^ - | Not found: tracked - | - | longer explanation available when compiling with `-explain` --- [E006] Not Found Error: tests/neg/tracked.scala:17:2 ---------------------------------------------------------------- -17 | tracked type T = Int // error // error - | ^^^^^^^ - | Not found: tracked - | - | longer explanation available when compiling with `-explain` +-- [E156] Syntax Error: tests/neg/tracked.scala:8:16 ------------------------------------------------------------------- +8 | tracked val b: Int = 2 // error + | ^^^^^^^^^^^^^^^^^^^^^^ + | Modifier tracked is not allowed for this definition +-- [E156] Syntax Error: tests/neg/tracked.scala:11:17 ------------------------------------------------------------------ +11 | tracked object Foo // error + | ^^^^^^^^^^^^^^^^^^ + | Modifier tracked is not allowed for this definition +-- [E156] Syntax Error: tests/neg/tracked.scala:14:16 ------------------------------------------------------------------ +14 | tracked class D // error + | ^^^^^^^^^^^^^^^ + | Modifier tracked is not allowed for this definition +-- [E156] Syntax Error: tests/neg/tracked.scala:17:15 ------------------------------------------------------------------ +17 | tracked type T = Int // error + | ^^^^^^^^^^^^^^^^^^^^ + | Modifier tracked is not allowed for this definition diff --git a/tests/neg/tracked.scala b/tests/neg/tracked.scala index 9f874ca3c0da..3d6c1a14fc55 100644 --- a/tests/neg/tracked.scala +++ b/tests/neg/tracked.scala @@ -8,13 +8,13 @@ object A: tracked val b: Int = 2 // error object B: - tracked object Foo // error // error + tracked object Foo // error object C: - tracked class D // error // error + tracked class D // error object D: - tracked type T = Int // error // error + tracked type T = Int // error object E: given g2: (tracked val x: Int) => C = C(x) // error diff --git a/tests/pos/abstract-tracked-2.scala b/tests/pos/abstract-tracked-2.scala new file mode 100644 index 000000000000..01e4ee84c548 --- /dev/null +++ b/tests/pos/abstract-tracked-2.scala @@ -0,0 +1,11 @@ +import scala.language.experimental.modularity +import scala.language.future + +abstract class Vec: + tracked val size: Int + +@main def main = + val v = new Vec: + val size0: size.type = 10 + val size = 10 + val size1: size.type = 10 diff --git a/tests/pos/abstract-tracked.scala b/tests/pos/abstract-tracked.scala new file mode 100644 index 000000000000..21812db9c04d --- /dev/null +++ b/tests/pos/abstract-tracked.scala @@ -0,0 +1,55 @@ +import scala.language.experimental.modularity +import scala.language.future + +trait F: + tracked val a: Int + +trait G: + tracked val b: Int + +trait H: + tracked val c: Int = 3 + +trait I extends F + +trait J extends F: + val a: Int = 1 + +class K(tracked val d: Int) + +class L + +trait M: + val f: Int + +class N extends F: + val a = 10 + +object Test: + val f = new F: + val a = 1 + val g = new G: + val b: 2 = 2 + val h = new H: + override val c = 4 + val i = new I: + val a = 5 + val j = new J: + override val a = 6 + val k = new K(7) + val l = new L { + tracked val e = 8 + } + val m = new M: + tracked val f = 9 + val n = new N + + summon[f.a.type <:< 1] + summon[g.b.type <:< 2] + summon[h.c.type <:< 4] + summon[i.a.type <:< 5] + summon[j.a.type <:< 6] + summon[k.d.type <:< 7] + // summon[l.e.type <:< 8] // unrelated issue -- error: e is not a member of L + summon[m.f.type <:< 9] + summon[n.a.type <:< 10] diff --git a/tests/pos/annot-17939.scala b/tests/pos/annot-17939.scala new file mode 100644 index 000000000000..604143183af2 --- /dev/null +++ b/tests/pos/annot-17939.scala @@ -0,0 +1,8 @@ +import scala.annotation.Annotation +class myRefined[T](f: T => Boolean) extends Annotation + +class Box[T](val x: T) +class Box2(val x: Int) + +class A(a: String @myRefined((x: Int) => Box(3).x == 3)) // crash +class A2(a2: String @myRefined((x: Int) => Box2(3).x == 3)) // works diff --git a/tests/pos/annot-19846.scala b/tests/pos/annot-19846.scala new file mode 100644 index 000000000000..ff2f8f632eab --- /dev/null +++ b/tests/pos/annot-19846.scala @@ -0,0 +1,9 @@ +package dependentAnnotation + +class lambdaAnnot(g: () => Int) extends annotation.StaticAnnotation + +def f(x: Int): Int @lambdaAnnot(() => x + 1) = x + +@main def main = + val y: Int = 5 + val z = f(y) diff --git a/tests/pos/annot-19846b.scala b/tests/pos/annot-19846b.scala new file mode 100644 index 000000000000..09c24a5cf3cf --- /dev/null +++ b/tests/pos/annot-19846b.scala @@ -0,0 +1,8 @@ +class qualified[T](predicate: T => Boolean) extends annotation.StaticAnnotation + +class EqualPair(val x: Int, val y: Int @qualified[Int](it => it == x)) + +@main def main = + val p = EqualPair(42, 42) + val y = p.y + println(42) diff --git a/tests/pos/annot-body.scala b/tests/pos/annot-body.scala new file mode 100644 index 000000000000..d8f6f79ae674 --- /dev/null +++ b/tests/pos/annot-body.scala @@ -0,0 +1,15 @@ +// This test checks that symbols in `BodyAnnotation` are not copied in +// `transformAnnot` during `PostTyper`. + +package json + +trait Reads[A] { + def reads(a: Any): A +} + +object JsMacroImpl { + inline def reads[A]: Reads[A] = + new Reads[A] { self => + def reads(a: Any) = ??? + } +} diff --git a/tests/pos/annot-i20272a.scala b/tests/pos/annot-i20272a.scala new file mode 100644 index 000000000000..e04ee1efcd99 --- /dev/null +++ b/tests/pos/annot-i20272a.scala @@ -0,0 +1,20 @@ +import language.experimental.captureChecking + + trait Iterable[T] { self: Iterable[T]^ => + def map[U](f: T => U): Iterable[U]^{this, f} + } + + object Test { + def assertEquals[A, B](a: A, b: B): Boolean = ??? + + def foo[T](level: Int, lines: Iterable[T]) = + lines.map(x => x) + + def bar(messages: Iterable[String]) = + foo(1, messages) + + val it: Iterable[String] = ??? + val msgs = bar(it) + + assertEquals(msgs, msgs) + } diff --git a/tests/pos/i17394/_1.scala b/tests/pos/i17394/_1.scala new file mode 100644 index 000000000000..fdab17ec3f19 --- /dev/null +++ b/tests/pos/i17394/_1.scala @@ -0,0 +1,9 @@ +package example: + def xd: Int = ??? + +package bar: + trait A: + def foo: String = ??? + +package object example extends bar.A: + def foo(x: String): String = ??? diff --git a/tests/pos/i17394/_2.scala b/tests/pos/i17394/_2.scala new file mode 100644 index 000000000000..5ac6a31a66b2 --- /dev/null +++ b/tests/pos/i17394/_2.scala @@ -0,0 +1,4 @@ +import example.* + +@main def main = + val _ = foo diff --git a/tests/pos/i17394b/_1.scala b/tests/pos/i17394b/_1.scala new file mode 100644 index 000000000000..fdab17ec3f19 --- /dev/null +++ b/tests/pos/i17394b/_1.scala @@ -0,0 +1,9 @@ +package example: + def xd: Int = ??? + +package bar: + trait A: + def foo: String = ??? + +package object example extends bar.A: + def foo(x: String): String = ??? diff --git a/tests/pos/i17394b/_2.scala b/tests/pos/i17394b/_2.scala new file mode 100644 index 000000000000..8c86ca45e215 --- /dev/null +++ b/tests/pos/i17394b/_2.scala @@ -0,0 +1,5 @@ +//> using options -Wunused:imports + +import example.{given, *} + +@main def main = () diff --git a/tests/pos/i18529/JCode1.java b/tests/pos/i18529/JCode1.java new file mode 100644 index 000000000000..e1f12f852c00 --- /dev/null +++ b/tests/pos/i18529/JCode1.java @@ -0,0 +1,9 @@ +package bug.code; + +import bug.util.List; +import bug.util.*; +import java.util.*; + +public class JCode1 { + public void m1(List xs) { return; } +} diff --git a/tests/pos/i18529/JCode2.java b/tests/pos/i18529/JCode2.java new file mode 100644 index 000000000000..2a1ec812852c --- /dev/null +++ b/tests/pos/i18529/JCode2.java @@ -0,0 +1,9 @@ +package bug.code; + +import bug.util.*; +import bug.util.List; +import java.util.*; + +public class JCode2 { + public void m1(List xs) { return; } +} diff --git a/tests/pos/i18529/List.java b/tests/pos/i18529/List.java new file mode 100644 index 000000000000..caf3c0b8036b --- /dev/null +++ b/tests/pos/i18529/List.java @@ -0,0 +1,3 @@ +package bug.util; + +public final class List {} diff --git a/tests/pos/i18529/SCode1.scala b/tests/pos/i18529/SCode1.scala new file mode 100644 index 000000000000..b6796b1540c6 --- /dev/null +++ b/tests/pos/i18529/SCode1.scala @@ -0,0 +1,9 @@ +package bug.code + +import bug.util.List +import bug.util.* +import java.util.* + +class SCode1 { + def work(xs: List[Int]): Unit = {} +} diff --git a/tests/pos/i18529/SCode2.scala b/tests/pos/i18529/SCode2.scala new file mode 100644 index 000000000000..30fc7d0e6f91 --- /dev/null +++ b/tests/pos/i18529/SCode2.scala @@ -0,0 +1,9 @@ +package bug.code + +import bug.util.* +import bug.util.List +import java.util.* + +class SCode2 { + def work(xs: List[Int]): Unit = {} +} diff --git a/tests/pos/i18529/Test.scala b/tests/pos/i18529/Test.scala new file mode 100644 index 000000000000..be7795442a7a --- /dev/null +++ b/tests/pos/i18529/Test.scala @@ -0,0 +1 @@ +class Test diff --git a/tests/pos/i22137.scala b/tests/pos/i22137.scala new file mode 100644 index 000000000000..b52dd9171146 --- /dev/null +++ b/tests/pos/i22137.scala @@ -0,0 +1,5 @@ +enum Parser[+Value]: + case Success(value: Value, issues: Seq[Failure] = Seq.empty) extends Parser[Value] + case Failure(exception: Throwable) extends Parser[Nothing] + +val v = Parser.Success(1) \ No newline at end of file diff --git a/tests/printing/dependent-annot-default-args.check b/tests/printing/dependent-annot-default-args.check index 44c1fe31e2d1..f457d5d62edb 100644 --- a/tests/printing/dependent-annot-default-args.check +++ b/tests/printing/dependent-annot-default-args.check @@ -8,16 +8,39 @@ package { final module class annot() extends AnyRef() { this: annot.type => def $lessinit$greater$default$2: Any @uncheckedVariance = 42 } + class annot2(x: Any, y: Array[Any]) extends annotation.Annotation() { + private[this] val x: Any + private[this] val y: Array[Any] + } + final lazy module val annot2: annot2 = new annot2() + final module class annot2() extends AnyRef() { this: annot2.type => + def $lessinit$greater$default$1: Any @uncheckedVariance = -1 + def $lessinit$greater$default$2: Array[Any] @uncheckedVariance = + Array.apply[Any](["Hello" : Any]*)(scala.reflect.ClassTag.Any) + } final lazy module val dependent-annot-default-args$package: dependent-annot-default-args$package = new dependent-annot-default-args$package() final module class dependent-annot-default-args$package() extends Object() { this: dependent-annot-default-args$package.type => def f(x: Int): Int @annot(x) = x + def f2(x: Int): + Int @annot2( + y = Array.apply[Any](["Hello",x : Any]*)(scala.reflect.ClassTag.Any)) + = x def test: Unit = { val y: Int = ??? val z: Int @annot(y) = f(y) + val z2: + Int @annot2( + y = Array.apply[Any](["Hello",y : Any]*)(scala.reflect.ClassTag.Any) + ) + = f2(y) + @annot(44) val z3: Int = 45 + @annot2( + y = Array.apply[Any](["Hello",y : Any]*)(scala.reflect.ClassTag.Any)) + val z4: Int = 45 () } } diff --git a/tests/printing/dependent-annot-default-args.scala b/tests/printing/dependent-annot-default-args.scala index 7ddce711fedc..11fc9ef52cc9 100644 --- a/tests/printing/dependent-annot-default-args.scala +++ b/tests/printing/dependent-annot-default-args.scala @@ -1,5 +1,15 @@ class annot(x: Any, y: Any = 42) extends annotation.Annotation +class annot2(x: Any = -1, y: Array[Any] = Array("Hello")) extends annotation.Annotation + def f(x: Int): Int @annot(x) = x +def f2(x: Int): Int @annot2(y = Array("Hello", x)) = x + def test = val y: Int = ??? + val z = f(y) + val z2 = f2(y) + + @annot(44) val z3 = 45 + @annot2(y = Array("Hello", y)) val z4 = 45 + diff --git a/tests/sjs-junit/test/org/scalajs/testsuite/compiler/RegressionTestScala3.scala b/tests/sjs-junit/test/org/scalajs/testsuite/compiler/RegressionTestScala3.scala index de260f3481ed..02cb08789232 100644 --- a/tests/sjs-junit/test/org/scalajs/testsuite/compiler/RegressionTestScala3.scala +++ b/tests/sjs-junit/test/org/scalajs/testsuite/compiler/RegressionTestScala3.scala @@ -145,6 +145,17 @@ class RegressionTestScala3 { assertEquals(5, Issue14289.Container.b()) assertEquals(true, Issue14289.Container.c()) } + + @Test def createArrayOfUnitIssue22226(): Unit = { + val a = Array.ofDim[Unit](0) + assertSame(classOf[Array[Unit]], a.getClass()) + + val b = new Array[Unit](0) + assertSame(classOf[Array[Unit]], b.getClass()) + + val c = Array.ofDim[Unit](0, 0) + assertSame(classOf[Array[Array[Unit]]], c.getClass()) + } } object RegressionTestScala3 { diff --git a/tests/warn/i21036a.check b/tests/warn/i21036a.check index 63d611a6e246..6ce5b94d123f 100644 --- a/tests/warn/i21036a.check +++ b/tests/warn/i21036a.check @@ -1,10 +1,13 @@ --- Warning: tests/warn/i21036a.scala:7:17 ------------------------------------------------------------------------------ +-- [E205] Potential Issue Warning: tests/warn/i21036a.scala:7:17 ------------------------------------------------------- 7 |val y = summon[A] // warn | ^ | Given search preference for A between alternatives | (b : B) | and | (a : A) - | will change. - | Current choice : the first alternative - | New choice from Scala 3.7: the second alternative + | will change in the future release. + | Current choice : the first alternative + | Choice from Scala 3.7 : the second alternative + | + | Suppress this warning by choosing -source 3.5, -source 3.7, or + | by using @annotation.nowarn("id=205") diff --git a/tests/warn/i21036b.check b/tests/warn/i21036b.check index dfa19a0e9bb1..da0639438c86 100644 --- a/tests/warn/i21036b.check +++ b/tests/warn/i21036b.check @@ -1,4 +1,4 @@ --- Warning: tests/warn/i21036b.scala:7:17 ------------------------------------------------------------------------------ +-- [E205] Potential Issue Warning: tests/warn/i21036b.scala:7:17 ------------------------------------------------------- 7 |val y = summon[A] // warn | ^ | Given search preference for A between alternatives @@ -6,5 +6,8 @@ | and | (a : A) | has changed. - | Previous choice : the first alternative - | New choice from Scala 3.7: the second alternative + | Previous choice : the first alternative + | Choice from Scala 3.7 : the second alternative + | + | Suppress this warning by choosing -source 3.5, -source 3.7, or + | by using @annotation.nowarn("id=205") diff --git a/tests/warn/i21036c.scala b/tests/warn/i21036c.scala new file mode 100644 index 000000000000..4015cc8a84bb --- /dev/null +++ b/tests/warn/i21036c.scala @@ -0,0 +1,7 @@ +trait A +trait B extends A +given b: B = ??? +given a: A = ??? + +@annotation.nowarn("id=205") +val y = summon[A] // don't warn \ No newline at end of file