Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove support for -scala-output-version flag #15146

Merged
merged 6 commits into from
May 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion compiler/src/dotty/tools/dotc/config/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ trait CommonScalaSettings:
val silentWarnings: Setting[Boolean] = BooleanSetting("-nowarn", "Silence all warnings.", aliases = List("--no-warnings"))

val javaOutputVersion: Setting[String] = ChoiceSetting("-java-output-version", "version", "Compile code with classes specific to the given version of the Java platform available on the classpath and emit bytecode for this version. Corresponds to -release flag in javac.", ScalaSettings.supportedReleaseVersions, "", aliases = List("-release", "--release"))
val scalaOutputVersion: Setting[String] = ChoiceSetting("-scala-output-version", "version", "Emit TASTy files that can be consumed by specified version of the compiler. The compilation will fail if for any reason valid TASTy cannot be produced (e.g. the code contains references to some parts of the standard library API that are missing in the older stdlib or uses language features unexpressible in the older version of TASTy format)", ScalaSettings.supportedScalaReleaseVersions, "")

val deprecation: Setting[Boolean] = BooleanSetting("-deprecation", "Emit warning and location for usages of deprecated APIs.", aliases = List("--deprecation"))
val feature: Setting[Boolean] = BooleanSetting("-feature", "Emit warning and location for usages of features that should be imported explicitly.", aliases = List("--feature"))
Expand Down
22 changes: 2 additions & 20 deletions compiler/src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import io.{AbstractFile, NoAbstractFile, PlainFile, Path}
import scala.io.Codec
import collection.mutable
import printing._
import config.{JavaPlatform, SJSPlatform, Platform, ScalaSettings, ScalaRelease}
import config.{JavaPlatform, SJSPlatform, Platform, ScalaSettings}
import classfile.ReusableDataReader
import StdNames.nme

Expand All @@ -38,9 +38,6 @@ import xsbti.AnalysisCallback
import plugins._
import java.util.concurrent.atomic.AtomicInteger
import java.nio.file.InvalidPathException
import dotty.tools.tasty.TastyFormat
import dotty.tools.dotc.config.{ NoScalaVersion, SpecificScalaVersion, AnyScalaVersion, ScalaBuild }
import dotty.tools.dotc.core.tasty.TastyVersion

object Contexts {

Expand Down Expand Up @@ -478,22 +475,7 @@ object Contexts {

/** A new context that summarizes an import statement */
def importContext(imp: Import[?], sym: Symbol): FreshContext =
fresh.setImportInfo(ImportInfo(sym, imp.selectors, imp.expr))

def scalaRelease: ScalaRelease =
val releaseName = base.settings.scalaOutputVersion.value
if releaseName.nonEmpty then ScalaRelease.parse(releaseName).get else ScalaRelease.latest

def tastyVersion: TastyVersion =
import math.Ordered.orderingToOrdered
val latestRelease = ScalaRelease.latest
val specifiedRelease = scalaRelease
if specifiedRelease < latestRelease then
// This is needed to make -scala-output-version a no-op when set to the latest release for unstable versions of the compiler
// (which might have the tasty format version numbers set to higher values before they're decreased during a release)
TastyVersion.fromStableScalaRelease(specifiedRelease.majorVersion, specifiedRelease.minorVersion)
else
TastyVersion.compilerVersion
fresh.setImportInfo(ImportInfo(sym, imp.selectors, imp.expr))

/** Is the debug option set? */
def debug: Boolean = base.settings.Ydebug.value
Expand Down
1 change: 0 additions & 1 deletion compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -988,7 +988,6 @@ class Definitions {
@tu lazy val FunctionalInterfaceAnnot: ClassSymbol = requiredClass("java.lang.FunctionalInterface")
@tu lazy val TargetNameAnnot: ClassSymbol = requiredClass("scala.annotation.targetName")
@tu lazy val VarargsAnnot: ClassSymbol = requiredClass("scala.annotation.varargs")
@tu lazy val SinceAnnot: ClassSymbol = requiredClass("scala.annotation.since")

@tu lazy val JavaRepeatableAnnot: ClassSymbol = requiredClass("java.lang.annotation.Repeatable")

Expand Down
33 changes: 2 additions & 31 deletions compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import java.util.UUID
import scala.collection.immutable
import scala.collection.mutable.{ ListBuffer, ArrayBuffer }
import scala.annotation.switch
import tasty.TastyVersion
import typer.Checking.checkNonCyclic
import io.{AbstractFile, ZipArchive}
import scala.util.control.NonFatal
Expand Down Expand Up @@ -914,7 +913,7 @@ class ClassfileParser(
}

def unpickleTASTY(bytes: Array[Byte]): Some[Embedded] = {
val unpickler = new tasty.DottyUnpickler(bytes, ctx.tastyVersion)
val unpickler = new tasty.DottyUnpickler(bytes)
unpickler.enter(roots = Set(classRoot, moduleRoot, moduleRoot.sourceModule))(using ctx.withSource(util.NoSource))
Some(unpickler)
}
Expand Down Expand Up @@ -980,37 +979,9 @@ class ClassfileParser(
if (tastyBytes.nonEmpty) {
val reader = new TastyReader(bytes, 0, 16)
val expectedUUID = new UUID(reader.readUncompressedLong(), reader.readUncompressedLong())
val tastyHeader = new TastyHeaderUnpickler(tastyBytes).readFullHeader()
val fileTastyVersion = TastyVersion(tastyHeader.majorVersion, tastyHeader.minorVersion, tastyHeader.experimentalVersion)
val tastyUUID = tastyHeader.uuid
val tastyUUID = new TastyHeaderUnpickler(tastyBytes).readHeader()
if (expectedUUID != tastyUUID)
report.warning(s"$classfile is out of sync with its TASTy file. Loaded TASTy file. Try cleaning the project to fix this issue", NoSourcePosition)

val tastyFilePath = classfile.path.stripSuffix(".class") + ".tasty"

def reportWrongTasty(reason: String, highestAllowed: TastyVersion) =
report.error(s"""The class ${classRoot.symbol.showFullName} cannot be loaded from file ${tastyFilePath} because $reason:
|highest allowed: ${highestAllowed.show}
|found: ${fileTastyVersion.show}
""".stripMargin)

val isTastyReadable = fileTastyVersion.isCompatibleWith(TastyVersion.compilerVersion)
if !isTastyReadable then
reportWrongTasty("its TASTy format cannot be read by the compiler", TastyVersion.compilerVersion)
else
def isStdlibClass(cls: ClassDenotation): Boolean =
ctx.platform.classPath.findClassFile(cls.fullName.mangledString) match {
case Some(entry: ZipArchive#Entry) =>
entry.underlyingSource.map(_.name.startsWith("scala3-library_")).getOrElse(false)
case _ => false
}
// While emitting older TASTy the the newer standard library used by the compiler will still be on the class path so trying to read its TASTy files should not cause a crash.
// This is OK however because references to elements of stdlib API are validated according to the values of their `@since` annotations.
// This should guarantee that the code won't crash at runtime when used with the stdlib provided by an older compiler.
val isTastyCompatible = fileTastyVersion.isCompatibleWith(ctx.tastyVersion) || isStdlibClass(classRoot)
if !isTastyCompatible then
reportWrongTasty(s"its TASTy format is not compatible with the one of the targeted Scala release (${ctx.scalaRelease.show})", ctx.tastyVersion)

return unpickleTASTY(tastyBytes)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ object DottyUnpickler {
* @param bytes the bytearray containing the Tasty file from which we unpickle
* @param mode the tasty file contains package (TopLevel), an expression (Term) or a type (TypeTree)
*/
class DottyUnpickler(bytes: Array[Byte], maximalTastyVersion: TastyVersion, mode: UnpickleMode = UnpickleMode.TopLevel) extends ClassfileParser.Embedded with tpd.TreeProvider {
class DottyUnpickler(bytes: Array[Byte], mode: UnpickleMode = UnpickleMode.TopLevel) extends ClassfileParser.Embedded with tpd.TreeProvider {
import tpd._
import DottyUnpickler._

Expand Down
12 changes: 4 additions & 8 deletions compiler/src/dotty/tools/dotc/core/tasty/TastyPickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import dotty.tools.tasty.{TastyBuffer, TastyFormat, TastyHash}
import TastyFormat._
import TastyBuffer._

import Contexts._

import collection.mutable
import core.Symbols.ClassSymbol
import Decorators._
Expand All @@ -33,7 +31,7 @@ class TastyPickler(val rootCls: ClassSymbol) {
def newSection(name: String, buf: TastyBuffer): Unit =
sections += ((nameBuffer.nameIndex(name.toTermName), buf))

def assembleParts()(using Context): Array[Byte] = {
def assembleParts(): Array[Byte] = {
def lengthWithLength(buf: TastyBuffer) =
buf.length + natSize(buf.length)

Expand All @@ -43,8 +41,6 @@ class TastyPickler(val rootCls: ClassSymbol) {
val nameBufferHash = TastyHash.pjwHash64(nameBuffer.bytes)
val treeSectionHash +: otherSectionHashes = sections.map(x => TastyHash.pjwHash64(x._2.bytes))

val tastyVersion = ctx.tastyVersion

// Hash of name table and tree
val uuidLow: Long = nameBufferHash ^ treeSectionHash
// Hash of positions, comments and any additional section
Expand All @@ -53,9 +49,9 @@ class TastyPickler(val rootCls: ClassSymbol) {
val headerBuffer = {
val buf = new TastyBuffer(header.length + TastyPickler.versionStringBytes.length + 32)
for (ch <- header) buf.writeByte(ch.toByte)
buf.writeNat(tastyVersion.major)
buf.writeNat(tastyVersion.minor)
buf.writeNat(tastyVersion.experimental)
buf.writeNat(MajorVersion)
buf.writeNat(MinorVersion)
buf.writeNat(ExperimentalVersion)
buf.writeNat(TastyPickler.versionStringBytes.length)
buf.writeBytes(TastyPickler.versionStringBytes, TastyPickler.versionStringBytes.length)
buf.writeUncompressedLong(uuidLow)
Expand Down
23 changes: 0 additions & 23 deletions compiler/src/dotty/tools/dotc/core/tasty/TastyVersion.scala

This file was deleted.

2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ object PickledQuotes {
quotePickling.println(s"**** unpickling quote from TASTY\n${TastyPrinter.showContents(bytes, ctx.settings.color.value == "never")}")

val mode = if (isType) UnpickleMode.TypeTree else UnpickleMode.Term
val unpickler = new DottyUnpickler(bytes, ctx.tastyVersion, mode)
val unpickler = new DottyUnpickler(bytes, mode)
unpickler.enter(Set.empty)

val tree = unpickler.tree
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/Pickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ class Pickler extends Phase {
ctx.initialize()
val unpicklers =
for ((cls, pickler) <- picklers) yield {
val unpickler = new DottyUnpickler(pickler.assembleParts(), ctx.tastyVersion)
val unpickler = new DottyUnpickler(pickler.assembleParts())
unpickler.enter(roots = Set.empty)
cls -> unpickler
}
Expand Down
30 changes: 0 additions & 30 deletions compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ class CrossVersionChecks extends MiniPhase:
private def checkUndesiredProperties(sym: Symbol, pos: SrcPos)(using Context): Unit =
checkDeprecated(sym, pos)
checkExperimentalRef(sym, pos)
checkSinceAnnot(sym, pos)

val xMigrationValue = ctx.settings.Xmigration.value
if xMigrationValue != NoScalaVersion then
Expand Down Expand Up @@ -85,29 +84,6 @@ class CrossVersionChecks extends MiniPhase:
for annot <- sym.annotations if annot.symbol.isExperimental do
Feature.checkExperimentalDef(annot.symbol, annot.tree)

private def checkSinceAnnot(sym: Symbol, pos: SrcPos)(using Context): Unit =
for
annot <- sym.getAnnotation(defn.SinceAnnot)
releaseName <- annot.argumentConstantString(0)
do
ScalaRelease.parse(releaseName) match
case Some(release) if release > ctx.scalaRelease =>
report.error(
i"$sym was added in Scala release ${releaseName.show}, therefore it cannot be used in the code targeting Scala ${ctx.scalaRelease.show}",
pos)
case None =>
report.error(i"$sym has an unparsable release name: '${releaseName}'", annot.tree.srcPos)
case _ =>

private def checkSinceAnnotInSignature(sym: Symbol, pos: SrcPos)(using Context) =
new TypeTraverser:
def traverse(tp: Type) =
if tp.typeSymbol.hasAnnotation(defn.SinceAnnot) then
checkSinceAnnot(tp.typeSymbol, pos)
else
traverseChildren(tp)
.traverse(sym.info)

/** If @migration is present (indicating that the symbol has changed semantics between versions),
* emit a warning.
*/
Expand Down Expand Up @@ -152,15 +128,12 @@ class CrossVersionChecks extends MiniPhase:
checkDeprecatedOvers(tree)
checkExperimentalAnnots(tree.symbol)
checkExperimentalSignature(tree.symbol, tree)
checkSinceAnnot(tree.symbol, tree.srcPos)
checkSinceAnnotInSignature(tree.symbol, tree)
tree

override def transformDefDef(tree: DefDef)(using Context): DefDef =
checkDeprecatedOvers(tree)
checkExperimentalAnnots(tree.symbol)
checkExperimentalSignature(tree.symbol, tree)
checkSinceAnnotInSignature(tree.symbol, tree)
tree

override def transformTemplate(tree: Template)(using Context): Tree =
Expand Down Expand Up @@ -190,19 +163,16 @@ class CrossVersionChecks extends MiniPhase:
case TypeRef(_, sym: Symbol) =>
checkDeprecated(sym, tree.srcPos)
checkExperimentalRef(sym, tree.srcPos)
checkSinceAnnot(sym, tree.srcPos)
case TermRef(_, sym: Symbol) =>
checkDeprecated(sym, tree.srcPos)
checkExperimentalRef(sym, tree.srcPos)
checkSinceAnnot(sym, tree.srcPos)
case _ =>
}
tree
}

override def transformTypeDef(tree: TypeDef)(using Context): TypeDef = {
checkExperimentalAnnots(tree.symbol)
checkSinceAnnot(tree.symbol, tree.srcPos)
tree
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class CommentPicklingTest {
implicit val ctx: Context = setup(args, initCtx).map(_._2).getOrElse(initCtx)
ctx.initialize()
val trees = files.flatMap { f =>
val unpickler = new DottyUnpickler(f.toByteArray(), ctx.tastyVersion)
val unpickler = new DottyUnpickler(f.toByteArray())
unpickler.enter(roots = Set.empty)
unpickler.rootTrees(using ctx)
}
Expand Down
13 changes: 5 additions & 8 deletions compiler/test/dotty/tools/vulpix/ParallelTesting.scala
Original file line number Diff line number Diff line change
Expand Up @@ -177,25 +177,23 @@ trait ParallelTesting extends RunnerOrchestration { self =>
flags: TestFlags,
outDir: JFile
) extends TestSource {
case class Group(ordinal: Int, compiler: String, release: String)
case class Group(ordinal: Int, compiler: String)

lazy val compilationGroups: List[(Group, Array[JFile])] =
val Release = """r([\d\.]+)""".r
val Compiler = """c([\d\.]+)""".r
val Ordinal = """(\d+)""".r
def groupFor(file: JFile): Group =
val groupSuffix = file.getName.dropWhile(_ != '_').stripSuffix(".scala").stripSuffix(".java")
val groupSuffixParts = groupSuffix.split("_")
val ordinal = groupSuffixParts.collectFirst { case Ordinal(n) => n.toInt }.getOrElse(Int.MinValue)
val release = groupSuffixParts.collectFirst { case Release(r) => r }.getOrElse("")
val compiler = groupSuffixParts.collectFirst { case Compiler(c) => c }.getOrElse("")
Group(ordinal, compiler, release)
Group(ordinal, compiler)

dir.listFiles
.filter(isSourceFile)
.groupBy(groupFor)
.toList
.sortBy { (g, _) => (g.ordinal, g.compiler, g.release) }
.sortBy { (g, _) => (g.ordinal, g.compiler) }
.map { (g, f) => (g, f.sorted) }

def sourceFiles = compilationGroups.map(_._2).flatten.toArray
Expand All @@ -218,11 +216,10 @@ trait ParallelTesting extends RunnerOrchestration { self =>

case testSource @ SeparateCompilationSource(_, dir, flags, outDir) =>
testSource.compilationGroups.map { (group, files) =>
val flags1 = if group.release.isEmpty then flags else flags.and("-scala-output-version", group.release)
if group.compiler.isEmpty then
compile(files, flags1, suppressErrors, outDir)
compile(files, flags, suppressErrors, outDir)
else
compileWithOtherCompiler(group.compiler, files, flags1, outDir)
compileWithOtherCompiler(group.compiler, files, flags, outDir)
}
})

Expand Down
7 changes: 2 additions & 5 deletions docs/_docs/contributing/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,9 @@ $ sbt

When the sources of a test consist of multiple source files places in a single directory they are passed to the compiler in a single run and the compiler decides in which order to compile them. In some cases, however, to reproduce a specific test scenario it might be necessary to compile the source files in several steps in a specified order. To achieve that one can add a `_${step_index}` suffix to a file name (before the `.scala` or `.java` extension) indicating the order of compilation. E.g. if the test directory contains files named `Foo_1.scala`, `Bar_2.scala` and `Baz_2.scala` then `Foo_1.scala` will be compiled first and after that `Bar_2.scala` together with `Baz_2.scala`.
Kordyjan marked this conversation as resolved.
Show resolved Hide resolved

There are also other suffixes indicating how some particular files are compiled:
* `_c${compilerVersion}` - compile a file with a specific version of the compiler instead of the one developed on the current branch
(e.g. `Foo_c3.0.2.scala`)
* `_r${release}` - compile a file with a given value of `-scala-output-version` flag (e.g. `Foo_r3.0.scala`)
The other kind of suffix that can modify how particular files are compiled is `_c${compilerVersion}`. When specified, the file will be compiled with a specific version of the compiler instead of the one developed on the current branch.

Different suffixes can be mixed together (their order is not important although consistency is advised), e.g. `Foo_1_r3.0`, `Bar_2_c3.0.2`.
Different suffixes can be mixed together (their order is not important although consistency is advised), e.g. `Foo_1_c3.0.2`, `Bar_2_c3.1.0`.

### Bootstrapped-only tests

Expand Down
Loading