From d19d0546711641448e6ddddc826e95ab64174d76 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Thu, 2 Nov 2023 14:15:13 +0100 Subject: [PATCH 1/7] restore intercepting UnpickleExceptions --- .../dotty/tools/dotc/core/SymbolLoaders.scala | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala index 3969a09a69ee..ce72981752e0 100644 --- a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala +++ b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala @@ -24,7 +24,7 @@ import ast.desugar import parsing.JavaParsers.OutlineJavaParser import parsing.Parsers.OutlineParser -import dotty.tools.tasty.TastyHeaderUnpickler +import dotty.tools.tasty.{TastyHeaderUnpickler, UnpickleException} object SymbolLoaders { @@ -421,14 +421,25 @@ class TastyLoader(val tastyFile: AbstractFile) extends SymbolLoader { def description(using Context): String = "TASTy file " + tastyFile.toString override def doComplete(root: SymDenotation)(using Context): Unit = - val (classRoot, moduleRoot) = rootDenots(root.asClass) - val tastyBytes = tastyFile.toByteArray - val unpickler = new tasty.DottyUnpickler(tastyBytes) - unpickler.enter(roots = Set(classRoot, moduleRoot, moduleRoot.sourceModule))(using ctx.withSource(util.NoSource)) - if mayLoadTreesFromTasty then - classRoot.classSymbol.rootTreeOrProvider = unpickler - moduleRoot.classSymbol.rootTreeOrProvider = unpickler - checkTastyUUID(tastyFile, tastyBytes) + try + val (classRoot, moduleRoot) = rootDenots(root.asClass) + val tastyBytes = tastyFile.toByteArray + val unpickler = new tasty.DottyUnpickler(tastyBytes) + unpickler.enter(roots = Set(classRoot, moduleRoot, moduleRoot.sourceModule))(using ctx.withSource(util.NoSource)) + if mayLoadTreesFromTasty then + classRoot.classSymbol.rootTreeOrProvider = unpickler + moduleRoot.classSymbol.rootTreeOrProvider = unpickler + checkTastyUUID(tastyFile, tastyBytes) + catch case e: RuntimeException => + val message = e match + case e: UnpickleException => + i"""TASTy file ${tastyFile.canonicalPath} could not be read, failing with: + | ${Option(e.getMessage).getOrElse("")}""" + case _ => + i"""TASTy file ${tastyFile.canonicalPath} is broken, reading aborted with ${e.getClass} + | ${Option(e.getMessage).getOrElse("")}""" + if (ctx.debug) e.printStackTrace() + throw IOException(message) private def checkTastyUUID(tastyFile: AbstractFile, tastyBytes: Array[Byte])(using Context): Unit = From a6c5b17f29bda7e6c0cadd01f12e8b344c9ef0d1 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Thu, 2 Nov 2023 14:15:31 +0100 Subject: [PATCH 2/7] improve error messages for mismatched tasty version --- .../src/dotty/tools/backend/jvm/CodeGen.scala | 4 +- .../dotty/tools/dotc/core/SymbolLoaders.scala | 4 +- .../dotc/core/tasty/TastyUnpickler.scala | 4 +- .../tools/tasty/TastyHeaderUnpickler.scala | 222 +++++++++++++--- .../tasty/TastyHeaderUnpicklerTest.scala | 240 ++++++++++++++++-- .../backend/jvm/GenBCode.scala | 4 +- 6 files changed, 405 insertions(+), 73 deletions(-) diff --git a/compiler/src/dotty/tools/backend/jvm/CodeGen.scala b/compiler/src/dotty/tools/backend/jvm/CodeGen.scala index 9572777095e0..c70bb3fac60c 100644 --- a/compiler/src/dotty/tools/backend/jvm/CodeGen.scala +++ b/compiler/src/dotty/tools/backend/jvm/CodeGen.scala @@ -24,7 +24,7 @@ import StdNames.nme import java.io.DataOutputStream import java.nio.channels.ClosedByInterruptException -import dotty.tools.tasty.{ TastyBuffer, TastyHeaderUnpickler } +import dotty.tools.tasty.{ TastyBuffer, TastyHeaderUnpickler, UnpicklerConfig } import scala.tools.asm import scala.tools.asm.tree._ @@ -94,7 +94,7 @@ class CodeGen(val int: DottyBackendInterface, val primitives: DottyPrimitives)( for (binary <- unit.pickled.get(claszSymbol.asClass)) { generatedTasty += GeneratedTasty(store, binary) val tasty = - val uuid = new TastyHeaderUnpickler(binary()).readHeader() + val uuid = new TastyHeaderUnpickler(UnpicklerConfig.scala3Compiler, binary()).readHeader() val lo = uuid.getMostSignificantBits val hi = uuid.getLeastSignificantBits diff --git a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala index ce72981752e0..920b15973ffa 100644 --- a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala +++ b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala @@ -24,7 +24,7 @@ import ast.desugar import parsing.JavaParsers.OutlineJavaParser import parsing.Parsers.OutlineParser -import dotty.tools.tasty.{TastyHeaderUnpickler, UnpickleException} +import dotty.tools.tasty.{TastyHeaderUnpickler, UnpickleException, UnpicklerConfig} object SymbolLoaders { @@ -447,7 +447,7 @@ class TastyLoader(val tastyFile: AbstractFile) extends SymbolLoader { val className = tastyFile.name.stripSuffix(".tasty") tastyFile.resolveSibling(className + ".class") if classfile != null then - val tastyUUID = new TastyHeaderUnpickler(tastyBytes).readHeader() + val tastyUUID = new TastyHeaderUnpickler(UnpicklerConfig.scala3Compiler, tastyBytes).readHeader() new ClassfileTastyUUIDParser(classfile)(ctx).checkTastyUUID(tastyUUID) else // This will be the case in any of our tests that compile with `-Youtput-only-tasty` diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala index 70bdec7780e2..44802b4dbd46 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala @@ -4,7 +4,7 @@ package tasty import scala.language.unsafeNulls -import dotty.tools.tasty.{TastyFormat, TastyBuffer, TastyReader, TastyHeaderUnpickler} +import dotty.tools.tasty.{TastyFormat, TastyBuffer, TastyReader, TastyHeaderUnpickler, UnpicklerConfig} import TastyFormat.NameTags._, TastyFormat.nameTagToString import TastyBuffer.NameRef @@ -88,7 +88,7 @@ class TastyUnpickler(reader: TastyReader) { result } - new TastyHeaderUnpickler(reader).readHeader() + new TastyHeaderUnpickler(UnpicklerConfig.scala3Compiler, reader).readHeader() locally { until(readEnd()) { nameAtRef.add(readNameContents()) } diff --git a/tasty/src/dotty/tools/tasty/TastyHeaderUnpickler.scala b/tasty/src/dotty/tools/tasty/TastyHeaderUnpickler.scala index c0ed5dbd58fa..0a7f67d4da96 100644 --- a/tasty/src/dotty/tools/tasty/TastyHeaderUnpickler.scala +++ b/tasty/src/dotty/tools/tasty/TastyHeaderUnpickler.scala @@ -3,6 +3,7 @@ package dotty.tools.tasty import java.util.UUID import TastyFormat.{MajorVersion, MinorVersion, ExperimentalVersion, header} +import TastyHeaderUnpickler.TastyVersion /** * The Tasty Header consists of four fields: @@ -27,12 +28,99 @@ sealed abstract case class TastyHeader( toolingVersion: String ) -class TastyHeaderUnpickler(reader: TastyReader) { +trait UnpicklerConfig { + /** The TASTy major version that this reader supports */ + def majorVersion: Int + /** The TASTy minor version that this reader supports */ + def minorVersion: Int + /** The TASTy experimental version that this reader supports */ + def experimentalVersion: Int + /** The description of the upgraded tool that can read the given TASTy version */ + def upgradedReaderTool(version: TastyVersion): String + /** The description of the upgraded tool that can produce the given TASTy version */ + def upgradedProducerTool(version: TastyVersion): String + /** Additional information to help a user fix the outdated TASTy problem */ + def recompileAdditionalInfo: String + /** Additional information to help a user fix the more recent TASTy problem */ + def upgradeAdditionalInfo(fileVersion: TastyVersion): String +} + +object UnpicklerConfig { + + /** A config where its major, minor and experimental versions are fixed to those in TastyFormat */ + trait DefaultTastyVersion extends UnpicklerConfig { + override final def majorVersion: Int = MajorVersion + override final def minorVersion: Int = MinorVersion + override final def experimentalVersion: Int = ExperimentalVersion + } + + trait Scala3Compiler extends UnpicklerConfig { + private def asScala3Compiler(version: TastyVersion): String = + if (version.major == 28) { + // scala 3.x.y series + if (version.experimental > 0) + // scenario here is someone using 3.4.0 to read 3.4.1-RC1-NIGHTLY, in this case, we should show 3.4 nightly. + s"the same nightly or snapshot Scala 3.${version.minor - 1} compiler" + else s"a Scala 3.${version.minor}.0 compiler or newer" + } + else if (version.experimental > 0) "the same Scala compiler" // unknown major version, just say same + else "a more recent Scala compiler" // unknown major version, just say later + + /** The description of the upgraded scala compiler that can read the given TASTy version */ + final def upgradedReaderTool(version: TastyVersion): String = asScala3Compiler(version) + + /** The description of the upgraded scala compiler that can produce the given TASTy version */ + final def upgradedProducerTool(version: TastyVersion): String = asScala3Compiler(version) + + final def recompileAdditionalInfo: String = """ + | Usually this means that the library dependency containing this file should be updated.""".stripMargin + + final def upgradeAdditionalInfo(fileVersion: TastyVersion): String = + if (fileVersion.isExperimental && experimentalVersion == 0) { + """ + | Note that you are using a stable compiler, which can not read experimental TASTy.""".stripMargin + } + else "" + } + + trait Generic extends UnpicklerConfig { + final def upgradedProducerTool(version: TastyVersion): String = + "a later version" + + final def upgradedReaderTool(version: TastyVersion): String = + if (version.isExperimental) s"the version of this tool compatible with TASTy ${version.show}" + else s"a newer version of this tool compatible with TASTy ${version.show}" + + final def recompileAdditionalInfo: String = """ + | Usually this means that the classpath entry of this file should be updated.""".stripMargin + + final def upgradeAdditionalInfo(fileVersion: TastyVersion): String = + if (fileVersion.isExperimental && experimentalVersion == 0) { + """ + | Note that this tool does not support reading experimental TASTy.""".stripMargin + } + else "" + } + + /** A config for the TASTy reader of a scala 3 compiler */ + val scala3Compiler = new UnpicklerConfig with Scala3Compiler with DefaultTastyVersion {} + + /** A config for the TASTy reader of a generic tool */ + val generic = new UnpicklerConfig with Generic with DefaultTastyVersion {} +} + +class TastyHeaderUnpickler(config: UnpicklerConfig, reader: TastyReader) { import TastyHeaderUnpickler._ import reader._ + def this(config: UnpicklerConfig, bytes: Array[Byte]) = this(config, new TastyReader(bytes)) + def this(reader: TastyReader) = this(UnpicklerConfig.generic, reader) def this(bytes: Array[Byte]) = this(new TastyReader(bytes)) + private val toolMajor: Int = config.majorVersion + private val toolMinor: Int = config.minorVersion + private val toolExperimental: Int = config.experimentalVersion + /** reads and verifies the TASTy version, extracting the UUID */ def readHeader(): UUID = readFullHeader().uuid @@ -45,8 +133,11 @@ class TastyHeaderUnpickler(reader: TastyReader) { val fileMajor = readNat() if (fileMajor <= 27) { // old behavior before `tasty-core` 3.0.0-M4 val fileMinor = readNat() - val signature = signatureString(fileMajor, fileMinor, 0) - throw new UnpickleException(signature + backIncompatAddendum + toolingAddendum) + val fileVersion = TastyVersion(fileMajor, fileMinor, 0) + val toolVersion = TastyVersion(toolMajor, toolMinor, toolExperimental) + val signature = signatureString(fileVersion, toolVersion, what = "backward", tool = None) + val fix = recompileFix(toolVersion.minStable) + throw new UnpickleException(signature + fix) } else { val fileMinor = readNat() @@ -63,20 +154,38 @@ class TastyHeaderUnpickler(reader: TastyReader) { fileMajor = fileMajor, fileMinor = fileMinor, fileExperimental = fileExperimental, - compilerMajor = MajorVersion, - compilerMinor = MinorVersion, - compilerExperimental = ExperimentalVersion + compilerMajor = toolMajor, + compilerMinor = toolMinor, + compilerExperimental = toolExperimental ) check(validVersion, { - val signature = signatureString(fileMajor, fileMinor, fileExperimental) - val producedByAddendum = s"\nThe TASTy file was produced by $toolingVersion.$toolingAddendum" - val msg = ( - if (fileExperimental != 0) unstableAddendum - else if (fileMajor < MajorVersion) backIncompatAddendum - else forwardIncompatAddendum + // failure means that the TASTy file is can not be read, therefore it is either: + // - backwards incompatible major, in which case the library should be recompiled by the minimum stable minor + // version supported by this compiler + // - any experimental in an older minor, in which case the library should be recompiled by the stable + // compiler in the same minor. + // - older experimental in the same minor, in which case the compiler is also experimental, and the library + // should be recompiled by the current compiler + // - forward incompatible, in which case the compiler must be upgraded to the same version as the file. + val fileVersion = TastyVersion(fileMajor, fileMinor, fileExperimental) + val toolVersion = TastyVersion(toolMajor, toolMinor, toolExperimental) + + val compat = Compatibility.failReason(file = fileVersion, read = toolVersion) + + val what = if (compat < 0) "backward" else "forward" + val signature = signatureString(fileVersion, toolVersion, what, tool = Some(toolingVersion)) + val fix = ( + if (compat < 0) { + val newCompiler = + if (compat == Compatibility.BackwardIncompatibleMajor) toolVersion.minStable + else if (compat == Compatibility.BackwardIncompatibleExperimental) fileVersion.nextStable + else toolVersion // recompile the experimental library with the current experimental compiler + recompileFix(newCompiler) + } + else upgradeFix(fileVersion) ) - signature + msg + producedByAddendum + signature + fix }) val uuid = new UUID(readUncompressedLong(), readUncompressedLong()) @@ -89,40 +198,71 @@ class TastyHeaderUnpickler(reader: TastyReader) { private def check(cond: Boolean, msg: => String): Unit = { if (!cond) throw new UnpickleException(msg) } + + private def signatureString( + fileVersion: TastyVersion, toolVersion: TastyVersion, what: String, tool: Option[String]) = { + val optProducedBy = tool.fold("")(t => s" produced by $t") + s"""TASTy file$optProducedBy has a $what incompatible TASTy version ${fileVersion.show}, + | expected ${toolVersion.validRange}. + |""".stripMargin + } + + private def recompileFix(producerVersion: TastyVersion) = { + val addendum = config.recompileAdditionalInfo + val newTool = config.upgradedProducerTool(producerVersion) + s""" The source of this file should be recompiled by $newTool.$addendum""".stripMargin + } + + private def upgradeFix(fileVersion: TastyVersion) = { + val addendum = config.upgradeAdditionalInfo(fileVersion) + val newTool = config.upgradedReaderTool(fileVersion) + s""" To read this ${fileVersion.kind} file, use $newTool.$addendum""".stripMargin + } } object TastyHeaderUnpickler { - private def toolingAddendum = ( - if (ExperimentalVersion > 0) - "\nNote that your tooling is currently using an unstable TASTy version." - else - "" - ) - - private def signatureString(fileMajor: Int, fileMinor: Int, fileExperimental: Int) = { - def showMinorVersion(min: Int, exp: Int) = { - val expStr = if (exp == 0) "" else s" [unstable release: $exp]" - s"$min$expStr" - } - val minorVersion = showMinorVersion(MinorVersion, ExperimentalVersion) - val fileMinorVersion = showMinorVersion(fileMinor, fileExperimental) - s"""TASTy signature has wrong version. - | expected: {majorVersion: $MajorVersion, minorVersion: $minorVersion} - | found : {majorVersion: $fileMajor, minorVersion: $fileMinorVersion} - | - |""".stripMargin + private object Compatibility { + final val BackwardIncompatibleMajor = -3 + final val BackwardIncompatibleExperimental = -2 + final val ExperimentalRecompile = -1 + final val ExperimentalUpgrade = 1 + final val ForwardIncompatible = 2 + + /** Given that file can't be read, extract the reason */ + def failReason(file: TastyVersion, read: TastyVersion): Int = + if (file.major == read.major && file.minor == read.minor && file.isExperimental && read.isExperimental) { + if (file.experimental < read.experimental) ExperimentalRecompile // recompile library as compiler is too new + else ExperimentalUpgrade // they should upgrade compiler as library is too new + } + else if (file.major < read.major) + BackwardIncompatibleMajor // pre 3.0.0 + else if (file.isExperimental && file.major == read.major && file.minor <= read.minor) + // e.g. 3.4.0 reading 3.4.0-RC1-NIGHTLY, or 3.3.0 reading 3.0.2-RC1-NIGHTLY + BackwardIncompatibleExperimental + else ForwardIncompatible } - private def unstableAddendum = - """This TASTy file was produced by an unstable release. - |To read this TASTy file, your tooling must be at the same version.""".stripMargin + case class TastyVersion(major: Int, minor: Int, experimental: Int) { + def isExperimental: Boolean = experimental > 0 + + def nextStable: TastyVersion = copy(experimental = 0) - private def backIncompatAddendum = - """This TASTy file was produced by an earlier release that is not supported anymore. - |Please recompile this TASTy with a later version.""".stripMargin + def minStable: TastyVersion = copy(minor = 0, experimental = 0) - private def forwardIncompatAddendum = - """This TASTy file was produced by a more recent, forwards incompatible release. - |To read this TASTy file, please upgrade your tooling.""".stripMargin + def show: String = { + val suffix = if (isExperimental) s"-experimental-$experimental" else "" + s"$major.$minor$suffix" + } + + def kind: String = + if (isExperimental) "experimental TASTy" else "TASTy" + + def validRange: String = { + val min = TastyVersion(major, 0, 0) + val max = if (experimental == 0) this else TastyVersion(major, minor - 1, 0) + val extra = Option.when(experimental > 0)(this) + s"stable TASTy from ${min.show} to ${max.show}${extra.fold("")(e => s", or exactly ${e.show}")}" + } + } } diff --git a/tasty/test/dotty/tools/tasty/TastyHeaderUnpicklerTest.scala b/tasty/test/dotty/tools/tasty/TastyHeaderUnpicklerTest.scala index 9f54c4b3061b..785ae9de297d 100644 --- a/tasty/test/dotty/tools/tasty/TastyHeaderUnpicklerTest.scala +++ b/tasty/test/dotty/tools/tasty/TastyHeaderUnpicklerTest.scala @@ -5,49 +5,230 @@ import org.junit.{Test, Ignore} import TastyFormat._ import TastyBuffer._ +import TastyHeaderUnpickler.TastyVersion -@Ignore // comment if you want to experiment with error messages class TastyHeaderUnpicklerTest { import TastyHeaderUnpicklerTest._ - @Test def vanilla: Unit = { - runTest(MajorVersion, MinorVersion, ExperimentalVersion, "Scala 3.0.0-M4-bin-SNAPSHOT-git-12345") + @Test + def okThisCompilerReadsItself: Unit = { + val file = TastyVersion(MajorVersion, MinorVersion, ExperimentalVersion) + val read = TastyVersion(MajorVersion, MinorVersion, ExperimentalVersion) + runTest(file, read, "Scala (current)") } - @Test def failBumpExperimental: Unit = { - (runTest(MajorVersion, MinorVersion, ExperimentalVersion + 1, "Scala 3.0.0-M4-bin-SNAPSHOT-git-12345")) + @Test + def okExperimentalCompilerReadsItself: Unit = { + val file = TastyVersion(MajorVersion, MinorVersion, 1) + val read = TastyVersion(MajorVersion, MinorVersion, 1) + runTest(file, read, "Scala (current)") } - @Test def failBumpMinor: Unit = { - (runTest(MajorVersion, MinorVersion + 1, ExperimentalVersion, "Scala 3.1.0-RC1")) + @Test + def okStableCompilerReadsItself: Unit = { + val file = TastyVersion(MajorVersion, MinorVersion, 0) + val read = TastyVersion(MajorVersion, MinorVersion, 0) + runTest(file, read, "Scala (current)") } - @Test def failBumpMajor: Unit = { - (runTest(MajorVersion + 1, MinorVersion, ExperimentalVersion, "Scala 4.0.0-M1")) + @Test + def okReadOldStableMinorFromStable: Unit = { + val file = TastyVersion(28, 2, 0) + val read = TastyVersion(28, 3, 0) + runTest(file, read, "Scala 3.2.2") } - @Test def failBumpMajorFinal: Unit = { - (runTest(MajorVersion + 1, MinorVersion, 0, "Scala 4.0.0")) + @Test + def okReadOldStableMinorFromExperimental: Unit = { + val file = TastyVersion(28, 2, 0) + val read = TastyVersion(28, 3, 1) + runTest(file, read, "Scala 3.2.2") } - @Test def okSubtractExperimental: Unit = { - (runTest(MajorVersion, MinorVersion, ExperimentalVersion - 1, "Scala 3.0.0")) + @Test + def failReadExperimentalFromStableSameMinor: Unit = { + val file = TastyVersion(28, 4, 1) + val read = TastyVersion(28, 4, 0) + expectUnpickleError(runTest(file, read, "Scala 3.4.0-RC1-bin-SNAPSHOT")) { + """TASTy file produced by Scala 3.4.0-RC1-bin-SNAPSHOT has a backward incompatible TASTy version 28.4-experimental-1, + | expected stable TASTy from 28.0 to 28.4. + | The source of this file should be recompiled by a Scala 3.4.0 compiler or newer. + | Usually this means that the library dependency containing this file should be updated.""".stripMargin + } + } + + @Test + def failReadExperimentalFromOldMinor: Unit = { + val file = TastyVersion(28, 3, 1) + val read = TastyVersion(28, 4, 0) + expectUnpickleError(runTest(file, read, "Scala 3.2.1-RC1-bin-SNAPSHOT")) { + """TASTy file produced by Scala 3.2.1-RC1-bin-SNAPSHOT has a backward incompatible TASTy version 28.3-experimental-1, + | expected stable TASTy from 28.0 to 28.4. + | The source of this file should be recompiled by a Scala 3.3.0 compiler or newer. + | Usually this means that the library dependency containing this file should be updated.""".stripMargin + } + } + + @Test + def failReadOldMajor: Unit = { + val file = TastyVersion(27, 3, 0) + val read = TastyVersion(28, 3, 0) + expectUnpickleError(runTest(file, read, "Scala 3.0.0-M1")) { + """TASTy file has a backward incompatible TASTy version 27.3, + | expected stable TASTy from 28.0 to 28.3. + | The source of this file should be recompiled by a Scala 3.0.0 compiler or newer. + | Usually this means that the library dependency containing this file should be updated.""".stripMargin + } + } + + @Test + def failReadOldMajor_generic: Unit = { + // We check the generic version here because it will produce a different message. + val file = TastyVersion(27, 3, 0) + val read = TastyVersion(28, 3, 0) + expectUnpickleError(runTest(file, read, "Scala 3.0.0-M1", generic = true)) { + """TASTy file has a backward incompatible TASTy version 27.3, + | expected stable TASTy from 28.0 to 28.3. + | The source of this file should be recompiled by a later version. + | Usually this means that the classpath entry of this file should be updated.""".stripMargin + } + } + + @Test + def failReadOldExperimentalFromSameMinorWhileExperimental: Unit = { + val file = TastyVersion(28, 4, 1) + val read = TastyVersion(28, 4, 2) + expectUnpickleError(runTest(file, read, "Scala 3.3.3-RC1-NIGHTLY")) { + """TASTy file produced by Scala 3.3.3-RC1-NIGHTLY has a backward incompatible TASTy version 28.4-experimental-1, + | expected stable TASTy from 28.0 to 28.3, or exactly 28.4-experimental-2. + | The source of this file should be recompiled by the same nightly or snapshot Scala 3.3 compiler. + | Usually this means that the library dependency containing this file should be updated.""".stripMargin + } + } + + @Test + def failReadOldExperimentalFromSameMinorWhileExperimental_generic: Unit = { + // We check the generic version here because it will produce a different message. + val file = TastyVersion(28, 4, 1) + val read = TastyVersion(28, 4, 2) + expectUnpickleError(runTest(file, read, "Scala 3.3.3-RC1-NIGHTLY", generic = true)) { + """TASTy file produced by Scala 3.3.3-RC1-NIGHTLY has a backward incompatible TASTy version 28.4-experimental-1, + | expected stable TASTy from 28.0 to 28.3, or exactly 28.4-experimental-2. + | The source of this file should be recompiled by a later version. + | Usually this means that the classpath entry of this file should be updated.""".stripMargin + } + } + + @Test + def failReadNewerStableMinorFromStable: Unit = { + val file = TastyVersion(28, 3, 0) + val read = TastyVersion(28, 2, 0) + expectUnpickleError(runTest(file, read, "Scala 3.3.1")) { + """TASTy file produced by Scala 3.3.1 has a forward incompatible TASTy version 28.3, + | expected stable TASTy from 28.0 to 28.2. + | To read this TASTy file, use a Scala 3.3.0 compiler or newer.""".stripMargin + } + } + + @Test + def failReadNewerStableMinorFromStable_generic: Unit = { + // We check the generic version here because it will produce a different message. + val file = TastyVersion(28, 3, 0) + val read = TastyVersion(28, 2, 0) + expectUnpickleError(runTest(file, read, "Scala 3.3.1", generic = true)) { + """TASTy file produced by Scala 3.3.1 has a forward incompatible TASTy version 28.3, + | expected stable TASTy from 28.0 to 28.2. + | To read this TASTy file, use a newer version of this tool compatible with TASTy 28.3.""".stripMargin + } } - @Test def okSubtractMinor: Unit = { - (runTest(MajorVersion, MinorVersion - 1, ExperimentalVersion, "Scala 3.0.0-M4-bin-SNAPSHOT-git-12345")) + @Test + def failReadNewerExperimentalMinorFromStable: Unit = { + val file = TastyVersion(28, 3, 1) + val read = TastyVersion(28, 2, 0) + expectUnpickleError(runTest(file, read, "Scala 3.2.2-RC1-NIGHTLY")) { + """TASTy file produced by Scala 3.2.2-RC1-NIGHTLY has a forward incompatible TASTy version 28.3-experimental-1, + | expected stable TASTy from 28.0 to 28.2. + | To read this experimental TASTy file, use the same nightly or snapshot Scala 3.2 compiler. + | Note that you are using a stable compiler, which can not read experimental TASTy.""".stripMargin + } + } + + @Test + def failReadNewerStableMajor: Unit = { + val file = TastyVersion(29, 0, 0) + val read = TastyVersion(28, 3, 0) + expectUnpickleError(runTest(file, read, "Scala 4.0.0")) { + """TASTy file produced by Scala 4.0.0 has a forward incompatible TASTy version 29.0, + | expected stable TASTy from 28.0 to 28.3. + | To read this TASTy file, use a more recent Scala compiler.""".stripMargin + } + } + + @Test + def failReadNewerExperimentalMajor: Unit = { + val file = TastyVersion(29, 0, 1) + val read = TastyVersion(28, 3, 0) + expectUnpickleError(runTest(file, read, "Scala 4.0.0-M1")) { + """TASTy file produced by Scala 4.0.0-M1 has a forward incompatible TASTy version 29.0-experimental-1, + | expected stable TASTy from 28.0 to 28.3. + | To read this experimental TASTy file, use the same Scala compiler. + | Note that you are using a stable compiler, which can not read experimental TASTy.""".stripMargin + } + } + + @Test + def failReadNewerExperimentalMajor_generic: Unit = { + // We check the generic version here because it will produce a different message. + val file = TastyVersion(29, 0, 1) + val read = TastyVersion(28, 3, 0) + expectUnpickleError(runTest(file, read, "Scala 4.0.0-M1", generic = true)) { + """TASTy file produced by Scala 4.0.0-M1 has a forward incompatible TASTy version 29.0-experimental-1, + | expected stable TASTy from 28.0 to 28.3. + | To read this experimental TASTy file, use the version of this tool compatible with TASTy 29.0-experimental-1. + | Note that this tool does not support reading experimental TASTy.""".stripMargin + } + } + + @Test + def failReadStableFromExperimentalSameMinor: Unit = { + val file = TastyVersion(28, 4, 0) + val read = TastyVersion(28, 4, 1) // 3.4.0-RC1-NIGHTLY + expectUnpickleError(runTest(file, read, "Scala 3.4.2")) { + """TASTy file produced by Scala 3.4.2 has a forward incompatible TASTy version 28.4, + | expected stable TASTy from 28.0 to 28.3, or exactly 28.4-experimental-1. + | To read this TASTy file, use a Scala 3.4.0 compiler or newer.""".stripMargin + } + } + + @Test + def failReadNewerExperimentalFromExperimentalSameMinor: Unit = { + val file = TastyVersion(28, 4, 2) + val read = TastyVersion(28, 4, 1) + expectUnpickleError(runTest(file, read, "Scala 3.3.3-RC2-NIGHTLY")) { + """TASTy file produced by Scala 3.3.3-RC2-NIGHTLY has a forward incompatible TASTy version 28.4-experimental-2, + | expected stable TASTy from 28.0 to 28.3, or exactly 28.4-experimental-1. + | To read this experimental TASTy file, use the same nightly or snapshot Scala 3.3 compiler.""".stripMargin + } } - @Test def failSubtractMajor: Unit = { - (runTest(MajorVersion - 1, MinorVersion, ExperimentalVersion, "Scala 3.0.0-M4-bin-SNAPSHOT-git-12345")) + @Test + def failReadNewerExperimentalFromExperimentalSameMinor_generic: Unit = { + // We check the generic version here because it will produce a different message. + val file = TastyVersion(28, 4, 2) + val read = TastyVersion(28, 4, 1) + expectUnpickleError(runTest(file, read, "Scala 3.3.3-RC2-NIGHTLY", generic = true)) { + """TASTy file produced by Scala 3.3.3-RC2-NIGHTLY has a forward incompatible TASTy version 28.4-experimental-2, + | expected stable TASTy from 28.0 to 28.3, or exactly 28.4-experimental-1. + | To read this experimental TASTy file, use the version of this tool compatible with TASTy 28.4-experimental-2.""".stripMargin + } } } object TastyHeaderUnpicklerTest { - def fillHeader(maj: Int, min: Int, exp: Int, compiler: String): TastyBuffer = { val compilerBytes = compiler.getBytes(java.nio.charset.StandardCharsets.UTF_8) val buf = new TastyBuffer(header.length + 32 + compilerBytes.length) @@ -62,22 +243,33 @@ object TastyHeaderUnpicklerTest { buf } - def runTest(maj: Int, min: Int, exp: Int, compiler: String): Unit = { - val headerBuffer = fillHeader(maj, min, exp, compiler) - val bs = headerBuffer.bytes.clone + case class CustomScalaConfig(compilerVersion: TastyVersion) extends UnpicklerConfig.Scala3Compiler { + override def majorVersion: Int = compilerVersion.major + override def minorVersion: Int = compilerVersion.minor + override def experimentalVersion: Int = compilerVersion.experimental + } - val hr = new TastyHeaderUnpickler(bs) + case class CustomGenericConfig(compilerVersion: TastyVersion) extends UnpicklerConfig.Generic { + override def majorVersion: Int = compilerVersion.major + override def minorVersion: Int = compilerVersion.minor + override def experimentalVersion: Int = compilerVersion.experimental + } + def runTest(file: TastyVersion, read: TastyVersion, compiler: String, generic: Boolean = false): Unit = { + val headerBuffer = fillHeader(file.major, file.minor, file.experimental, compiler) + val bs = headerBuffer.bytes.clone + val config = if (generic) CustomGenericConfig(read) else CustomScalaConfig(read) + val hr = new TastyHeaderUnpickler(config, new TastyReader(bs)) hr.readFullHeader() } - def expectUnpickleError(op: => Unit) = { + def expectUnpickleError(op: => Unit)(message: String) = { try { op fail() } catch { - case err: UnpickleException => () + case err: UnpickleException => assert(err.getMessage.contains(message)) } } diff --git a/tests/pos-with-compiler-cc/backend/jvm/GenBCode.scala b/tests/pos-with-compiler-cc/backend/jvm/GenBCode.scala index 71d007370fe7..8ca2eab9ea8a 100644 --- a/tests/pos-with-compiler-cc/backend/jvm/GenBCode.scala +++ b/tests/pos-with-compiler-cc/backend/jvm/GenBCode.scala @@ -26,7 +26,7 @@ import Decorators.em import java.io.DataOutputStream import java.nio.channels.ClosedByInterruptException -import dotty.tools.tasty.{ TastyBuffer, TastyHeaderUnpickler } +import dotty.tools.tasty.{ TastyBuffer, TastyHeaderUnpickler, UnpicklerConfig } import scala.tools.asm import scala.tools.asm.Handle @@ -285,7 +285,7 @@ class GenBCodePipeline(val int: DottyBackendInterface, val primitives: DottyPrim throw ex finally outstream.close() - val uuid = new TastyHeaderUnpickler(binary()).readHeader() + val uuid = new TastyHeaderUnpickler(UnpicklerConfig.scala3Compiler, binary()).readHeader() val lo = uuid.getMostSignificantBits val hi = uuid.getLeastSignificantBits From 67351cb79e257439d21218d9cdb15fc4a13059f2 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Thu, 2 Nov 2023 16:41:35 +0100 Subject: [PATCH 3/7] add url to scala docs for tasty versioning --- .../tools/tasty/TastyHeaderUnpickler.scala | 8 ++- .../tasty/TastyHeaderUnpicklerTest.scala | 60 ++++++++++++++----- 2 files changed, 51 insertions(+), 17 deletions(-) diff --git a/tasty/src/dotty/tools/tasty/TastyHeaderUnpickler.scala b/tasty/src/dotty/tools/tasty/TastyHeaderUnpickler.scala index 0a7f67d4da96..ecfbda54d847 100644 --- a/tasty/src/dotty/tools/tasty/TastyHeaderUnpickler.scala +++ b/tasty/src/dotty/tools/tasty/TastyHeaderUnpickler.scala @@ -137,7 +137,7 @@ class TastyHeaderUnpickler(config: UnpicklerConfig, reader: TastyReader) { val toolVersion = TastyVersion(toolMajor, toolMinor, toolExperimental) val signature = signatureString(fileVersion, toolVersion, what = "backward", tool = None) val fix = recompileFix(toolVersion.minStable) - throw new UnpickleException(signature + fix) + throw new UnpickleException(signature + fix + tastyAddendum) } else { val fileMinor = readNat() @@ -185,7 +185,7 @@ class TastyHeaderUnpickler(config: UnpicklerConfig, reader: TastyReader) { } else upgradeFix(fileVersion) ) - signature + fix + signature + fix + tastyAddendum }) val uuid = new UUID(readUncompressedLong(), readUncompressedLong()) @@ -218,6 +218,10 @@ class TastyHeaderUnpickler(config: UnpicklerConfig, reader: TastyReader) { val newTool = config.upgradedReaderTool(fileVersion) s""" To read this ${fileVersion.kind} file, use $newTool.$addendum""".stripMargin } + + private def tastyAddendum: String = """ + | Please refer to the documentation for information on TASTy versioning: + | https://docs.scala-lang.org/scala3/reference/language-versions/binary-compatibility.html""".stripMargin } object TastyHeaderUnpickler { diff --git a/tasty/test/dotty/tools/tasty/TastyHeaderUnpicklerTest.scala b/tasty/test/dotty/tools/tasty/TastyHeaderUnpicklerTest.scala index 785ae9de297d..58805ce27aee 100644 --- a/tasty/test/dotty/tools/tasty/TastyHeaderUnpicklerTest.scala +++ b/tasty/test/dotty/tools/tasty/TastyHeaderUnpicklerTest.scala @@ -54,7 +54,9 @@ class TastyHeaderUnpicklerTest { """TASTy file produced by Scala 3.4.0-RC1-bin-SNAPSHOT has a backward incompatible TASTy version 28.4-experimental-1, | expected stable TASTy from 28.0 to 28.4. | The source of this file should be recompiled by a Scala 3.4.0 compiler or newer. - | Usually this means that the library dependency containing this file should be updated.""".stripMargin + | Usually this means that the library dependency containing this file should be updated. + | Please refer to the documentation for information on TASTy versioning: + | https://docs.scala-lang.org/scala3/reference/language-versions/binary-compatibility.html""".stripMargin } } @@ -66,7 +68,9 @@ class TastyHeaderUnpicklerTest { """TASTy file produced by Scala 3.2.1-RC1-bin-SNAPSHOT has a backward incompatible TASTy version 28.3-experimental-1, | expected stable TASTy from 28.0 to 28.4. | The source of this file should be recompiled by a Scala 3.3.0 compiler or newer. - | Usually this means that the library dependency containing this file should be updated.""".stripMargin + | Usually this means that the library dependency containing this file should be updated. + | Please refer to the documentation for information on TASTy versioning: + | https://docs.scala-lang.org/scala3/reference/language-versions/binary-compatibility.html""".stripMargin } } @@ -78,7 +82,9 @@ class TastyHeaderUnpicklerTest { """TASTy file has a backward incompatible TASTy version 27.3, | expected stable TASTy from 28.0 to 28.3. | The source of this file should be recompiled by a Scala 3.0.0 compiler or newer. - | Usually this means that the library dependency containing this file should be updated.""".stripMargin + | Usually this means that the library dependency containing this file should be updated. + | Please refer to the documentation for information on TASTy versioning: + | https://docs.scala-lang.org/scala3/reference/language-versions/binary-compatibility.html""".stripMargin } } @@ -91,7 +97,9 @@ class TastyHeaderUnpicklerTest { """TASTy file has a backward incompatible TASTy version 27.3, | expected stable TASTy from 28.0 to 28.3. | The source of this file should be recompiled by a later version. - | Usually this means that the classpath entry of this file should be updated.""".stripMargin + | Usually this means that the classpath entry of this file should be updated. + | Please refer to the documentation for information on TASTy versioning: + | https://docs.scala-lang.org/scala3/reference/language-versions/binary-compatibility.html""".stripMargin } } @@ -103,7 +111,9 @@ class TastyHeaderUnpicklerTest { """TASTy file produced by Scala 3.3.3-RC1-NIGHTLY has a backward incompatible TASTy version 28.4-experimental-1, | expected stable TASTy from 28.0 to 28.3, or exactly 28.4-experimental-2. | The source of this file should be recompiled by the same nightly or snapshot Scala 3.3 compiler. - | Usually this means that the library dependency containing this file should be updated.""".stripMargin + | Usually this means that the library dependency containing this file should be updated. + | Please refer to the documentation for information on TASTy versioning: + | https://docs.scala-lang.org/scala3/reference/language-versions/binary-compatibility.html""".stripMargin } } @@ -116,7 +126,9 @@ class TastyHeaderUnpicklerTest { """TASTy file produced by Scala 3.3.3-RC1-NIGHTLY has a backward incompatible TASTy version 28.4-experimental-1, | expected stable TASTy from 28.0 to 28.3, or exactly 28.4-experimental-2. | The source of this file should be recompiled by a later version. - | Usually this means that the classpath entry of this file should be updated.""".stripMargin + | Usually this means that the classpath entry of this file should be updated. + | Please refer to the documentation for information on TASTy versioning: + | https://docs.scala-lang.org/scala3/reference/language-versions/binary-compatibility.html""".stripMargin } } @@ -127,7 +139,9 @@ class TastyHeaderUnpicklerTest { expectUnpickleError(runTest(file, read, "Scala 3.3.1")) { """TASTy file produced by Scala 3.3.1 has a forward incompatible TASTy version 28.3, | expected stable TASTy from 28.0 to 28.2. - | To read this TASTy file, use a Scala 3.3.0 compiler or newer.""".stripMargin + | To read this TASTy file, use a Scala 3.3.0 compiler or newer. + | Please refer to the documentation for information on TASTy versioning: + | https://docs.scala-lang.org/scala3/reference/language-versions/binary-compatibility.html""".stripMargin } } @@ -139,7 +153,9 @@ class TastyHeaderUnpicklerTest { expectUnpickleError(runTest(file, read, "Scala 3.3.1", generic = true)) { """TASTy file produced by Scala 3.3.1 has a forward incompatible TASTy version 28.3, | expected stable TASTy from 28.0 to 28.2. - | To read this TASTy file, use a newer version of this tool compatible with TASTy 28.3.""".stripMargin + | To read this TASTy file, use a newer version of this tool compatible with TASTy 28.3. + | Please refer to the documentation for information on TASTy versioning: + | https://docs.scala-lang.org/scala3/reference/language-versions/binary-compatibility.html""".stripMargin } } @@ -151,7 +167,9 @@ class TastyHeaderUnpicklerTest { """TASTy file produced by Scala 3.2.2-RC1-NIGHTLY has a forward incompatible TASTy version 28.3-experimental-1, | expected stable TASTy from 28.0 to 28.2. | To read this experimental TASTy file, use the same nightly or snapshot Scala 3.2 compiler. - | Note that you are using a stable compiler, which can not read experimental TASTy.""".stripMargin + | Note that you are using a stable compiler, which can not read experimental TASTy. + | Please refer to the documentation for information on TASTy versioning: + | https://docs.scala-lang.org/scala3/reference/language-versions/binary-compatibility.html""".stripMargin } } @@ -162,7 +180,9 @@ class TastyHeaderUnpicklerTest { expectUnpickleError(runTest(file, read, "Scala 4.0.0")) { """TASTy file produced by Scala 4.0.0 has a forward incompatible TASTy version 29.0, | expected stable TASTy from 28.0 to 28.3. - | To read this TASTy file, use a more recent Scala compiler.""".stripMargin + | To read this TASTy file, use a more recent Scala compiler. + | Please refer to the documentation for information on TASTy versioning: + | https://docs.scala-lang.org/scala3/reference/language-versions/binary-compatibility.html""".stripMargin } } @@ -174,7 +194,9 @@ class TastyHeaderUnpicklerTest { """TASTy file produced by Scala 4.0.0-M1 has a forward incompatible TASTy version 29.0-experimental-1, | expected stable TASTy from 28.0 to 28.3. | To read this experimental TASTy file, use the same Scala compiler. - | Note that you are using a stable compiler, which can not read experimental TASTy.""".stripMargin + | Note that you are using a stable compiler, which can not read experimental TASTy. + | Please refer to the documentation for information on TASTy versioning: + | https://docs.scala-lang.org/scala3/reference/language-versions/binary-compatibility.html""".stripMargin } } @@ -187,7 +209,9 @@ class TastyHeaderUnpicklerTest { """TASTy file produced by Scala 4.0.0-M1 has a forward incompatible TASTy version 29.0-experimental-1, | expected stable TASTy from 28.0 to 28.3. | To read this experimental TASTy file, use the version of this tool compatible with TASTy 29.0-experimental-1. - | Note that this tool does not support reading experimental TASTy.""".stripMargin + | Note that this tool does not support reading experimental TASTy. + | Please refer to the documentation for information on TASTy versioning: + | https://docs.scala-lang.org/scala3/reference/language-versions/binary-compatibility.html""".stripMargin } } @@ -198,7 +222,9 @@ class TastyHeaderUnpicklerTest { expectUnpickleError(runTest(file, read, "Scala 3.4.2")) { """TASTy file produced by Scala 3.4.2 has a forward incompatible TASTy version 28.4, | expected stable TASTy from 28.0 to 28.3, or exactly 28.4-experimental-1. - | To read this TASTy file, use a Scala 3.4.0 compiler or newer.""".stripMargin + | To read this TASTy file, use a Scala 3.4.0 compiler or newer. + | Please refer to the documentation for information on TASTy versioning: + | https://docs.scala-lang.org/scala3/reference/language-versions/binary-compatibility.html""".stripMargin } } @@ -209,7 +235,9 @@ class TastyHeaderUnpicklerTest { expectUnpickleError(runTest(file, read, "Scala 3.3.3-RC2-NIGHTLY")) { """TASTy file produced by Scala 3.3.3-RC2-NIGHTLY has a forward incompatible TASTy version 28.4-experimental-2, | expected stable TASTy from 28.0 to 28.3, or exactly 28.4-experimental-1. - | To read this experimental TASTy file, use the same nightly or snapshot Scala 3.3 compiler.""".stripMargin + | To read this experimental TASTy file, use the same nightly or snapshot Scala 3.3 compiler. + | Please refer to the documentation for information on TASTy versioning: + | https://docs.scala-lang.org/scala3/reference/language-versions/binary-compatibility.html""".stripMargin } } @@ -221,7 +249,9 @@ class TastyHeaderUnpicklerTest { expectUnpickleError(runTest(file, read, "Scala 3.3.3-RC2-NIGHTLY", generic = true)) { """TASTy file produced by Scala 3.3.3-RC2-NIGHTLY has a forward incompatible TASTy version 28.4-experimental-2, | expected stable TASTy from 28.0 to 28.3, or exactly 28.4-experimental-1. - | To read this experimental TASTy file, use the version of this tool compatible with TASTy 28.4-experimental-2.""".stripMargin + | To read this experimental TASTy file, use the version of this tool compatible with TASTy 28.4-experimental-2. + | Please refer to the documentation for information on TASTy versioning: + | https://docs.scala-lang.org/scala3/reference/language-versions/binary-compatibility.html""".stripMargin } } From 7993b2993da0a8b23b2a5cb8e8f5a7e98db14e8e Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Thu, 2 Nov 2023 17:50:54 +0100 Subject: [PATCH 4/7] move scala3Compiler config to compiler module --- .../src/dotty/tools/backend/jvm/CodeGen.scala | 5 +-- .../dotty/tools/dotc/core/SymbolLoaders.scala | 3 +- .../dotc/core/tasty/TastyUnpickler.scala | 36 ++++++++++++++++++- .../tasty/TastyHeaderUnpicklerTest.scala | 19 ++++++---- .../tools/tasty/TastyHeaderUnpickler.scala | 32 ----------------- .../backend/jvm/GenBCode.scala | 3 +- 6 files changed, 54 insertions(+), 44 deletions(-) rename {tasty/test/dotty/tools => compiler/test/dotty/tools/dotc/core}/tasty/TastyHeaderUnpicklerTest.scala (96%) diff --git a/compiler/src/dotty/tools/backend/jvm/CodeGen.scala b/compiler/src/dotty/tools/backend/jvm/CodeGen.scala index c70bb3fac60c..4bf305f3387c 100644 --- a/compiler/src/dotty/tools/backend/jvm/CodeGen.scala +++ b/compiler/src/dotty/tools/backend/jvm/CodeGen.scala @@ -24,7 +24,8 @@ import StdNames.nme import java.io.DataOutputStream import java.nio.channels.ClosedByInterruptException -import dotty.tools.tasty.{ TastyBuffer, TastyHeaderUnpickler, UnpicklerConfig } +import dotty.tools.tasty.{ TastyBuffer, TastyHeaderUnpickler } +import dotty.tools.dotc.core.tasty.TastyUnpickler import scala.tools.asm import scala.tools.asm.tree._ @@ -94,7 +95,7 @@ class CodeGen(val int: DottyBackendInterface, val primitives: DottyPrimitives)( for (binary <- unit.pickled.get(claszSymbol.asClass)) { generatedTasty += GeneratedTasty(store, binary) val tasty = - val uuid = new TastyHeaderUnpickler(UnpicklerConfig.scala3Compiler, binary()).readHeader() + val uuid = new TastyHeaderUnpickler(TastyUnpickler.scala3CompilerConfig, binary()).readHeader() val lo = uuid.getMostSignificantBits val hi = uuid.getLeastSignificantBits diff --git a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala index 920b15973ffa..12eea3a26df4 100644 --- a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala +++ b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala @@ -25,6 +25,7 @@ import ast.desugar import parsing.JavaParsers.OutlineJavaParser import parsing.Parsers.OutlineParser import dotty.tools.tasty.{TastyHeaderUnpickler, UnpickleException, UnpicklerConfig} +import dotty.tools.dotc.core.tasty.TastyUnpickler object SymbolLoaders { @@ -447,7 +448,7 @@ class TastyLoader(val tastyFile: AbstractFile) extends SymbolLoader { val className = tastyFile.name.stripSuffix(".tasty") tastyFile.resolveSibling(className + ".class") if classfile != null then - val tastyUUID = new TastyHeaderUnpickler(UnpicklerConfig.scala3Compiler, tastyBytes).readHeader() + val tastyUUID = new TastyHeaderUnpickler(TastyUnpickler.scala3CompilerConfig, tastyBytes).readHeader() new ClassfileTastyUUIDParser(classfile)(ctx).checkTastyUUID(tastyUUID) else // This will be the case in any of our tests that compile with `-Youtput-only-tasty` diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala index 44802b4dbd46..59f5600ff44b 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala @@ -5,6 +5,7 @@ package tasty import scala.language.unsafeNulls import dotty.tools.tasty.{TastyFormat, TastyBuffer, TastyReader, TastyHeaderUnpickler, UnpicklerConfig} +import TastyHeaderUnpickler.TastyVersion import TastyFormat.NameTags._, TastyFormat.nameTagToString import TastyBuffer.NameRef @@ -24,6 +25,39 @@ object TastyUnpickler { def apply(ref: NameRef): TermName = names(ref.index) def contents: Iterable[TermName] = names } + + trait Scala3CompilerConfig extends UnpicklerConfig: + private def asScala3Compiler(version: TastyVersion): String = + if (version.major == 28) { + // scala 3.x.y series + if (version.experimental > 0) + // scenario here is someone using 3.4.0 to read 3.4.1-RC1-NIGHTLY, in this case, we should show 3.4 nightly. + s"the same nightly or snapshot Scala 3.${version.minor - 1} compiler" + else s"a Scala 3.${version.minor}.0 compiler or newer" + } + else if (version.experimental > 0) "the same Scala compiler" // unknown major version, just say same + else "a more recent Scala compiler" // unknown major version, just say later + + /** The description of the upgraded scala compiler that can read the given TASTy version */ + final def upgradedReaderTool(version: TastyVersion): String = asScala3Compiler(version) + + /** The description of the upgraded scala compiler that can produce the given TASTy version */ + final def upgradedProducerTool(version: TastyVersion): String = asScala3Compiler(version) + + final def recompileAdditionalInfo: String = """ + | Usually this means that the library dependency containing this file should be updated.""".stripMargin + + final def upgradeAdditionalInfo(fileVersion: TastyVersion): String = + if (fileVersion.isExperimental && experimentalVersion == 0) { + """ + | Note that you are using a stable compiler, which can not read experimental TASTy.""".stripMargin + } + else "" + end Scala3CompilerConfig + + /** A config for the TASTy reader of a scala 3 compiler */ + val scala3CompilerConfig = new Scala3CompilerConfig with UnpicklerConfig.DefaultTastyVersion {} + } import TastyUnpickler._ @@ -88,7 +122,7 @@ class TastyUnpickler(reader: TastyReader) { result } - new TastyHeaderUnpickler(UnpicklerConfig.scala3Compiler, reader).readHeader() + new TastyHeaderUnpickler(scala3CompilerConfig, reader).readHeader() locally { until(readEnd()) { nameAtRef.add(readNameContents()) } diff --git a/tasty/test/dotty/tools/tasty/TastyHeaderUnpicklerTest.scala b/compiler/test/dotty/tools/dotc/core/tasty/TastyHeaderUnpicklerTest.scala similarity index 96% rename from tasty/test/dotty/tools/tasty/TastyHeaderUnpicklerTest.scala rename to compiler/test/dotty/tools/dotc/core/tasty/TastyHeaderUnpicklerTest.scala index 58805ce27aee..c722af979d76 100644 --- a/tasty/test/dotty/tools/tasty/TastyHeaderUnpicklerTest.scala +++ b/compiler/test/dotty/tools/dotc/core/tasty/TastyHeaderUnpicklerTest.scala @@ -1,11 +1,16 @@ -package dotty.tools.tasty +package dotty.tools.dotc.core.tasty import org.junit.Assert._ import org.junit.{Test, Ignore} -import TastyFormat._ -import TastyBuffer._ -import TastyHeaderUnpickler.TastyVersion +import dotty.tools.tasty.TastyFormat._ +import dotty.tools.tasty.TastyBuffer._ +import dotty.tools.tasty.TastyBuffer +import dotty.tools.tasty.TastyReader +import dotty.tools.tasty.UnpickleException +import dotty.tools.tasty.TastyHeaderUnpickler +import dotty.tools.tasty.TastyHeaderUnpickler.TastyVersion +import dotty.tools.tasty.UnpicklerConfig class TastyHeaderUnpicklerTest { @@ -260,7 +265,7 @@ class TastyHeaderUnpicklerTest { object TastyHeaderUnpicklerTest { def fillHeader(maj: Int, min: Int, exp: Int, compiler: String): TastyBuffer = { - val compilerBytes = compiler.getBytes(java.nio.charset.StandardCharsets.UTF_8) + val compilerBytes = compiler.getBytes(java.nio.charset.StandardCharsets.UTF_8).nn val buf = new TastyBuffer(header.length + 32 + compilerBytes.length) for (ch <- header) buf.writeByte(ch.toByte) buf.writeNat(maj) @@ -273,7 +278,7 @@ object TastyHeaderUnpicklerTest { buf } - case class CustomScalaConfig(compilerVersion: TastyVersion) extends UnpicklerConfig.Scala3Compiler { + case class CustomScalaConfig(compilerVersion: TastyVersion) extends TastyUnpickler.Scala3CompilerConfig { override def majorVersion: Int = compilerVersion.major override def minorVersion: Int = compilerVersion.minor override def experimentalVersion: Int = compilerVersion.experimental @@ -299,7 +304,7 @@ object TastyHeaderUnpicklerTest { fail() } catch { - case err: UnpickleException => assert(err.getMessage.contains(message)) + case err: UnpickleException => assert(err.getMessage.nn.contains(message)) } } diff --git a/tasty/src/dotty/tools/tasty/TastyHeaderUnpickler.scala b/tasty/src/dotty/tools/tasty/TastyHeaderUnpickler.scala index ecfbda54d847..237544d99760 100644 --- a/tasty/src/dotty/tools/tasty/TastyHeaderUnpickler.scala +++ b/tasty/src/dotty/tools/tasty/TastyHeaderUnpickler.scala @@ -54,35 +54,6 @@ object UnpicklerConfig { override final def experimentalVersion: Int = ExperimentalVersion } - trait Scala3Compiler extends UnpicklerConfig { - private def asScala3Compiler(version: TastyVersion): String = - if (version.major == 28) { - // scala 3.x.y series - if (version.experimental > 0) - // scenario here is someone using 3.4.0 to read 3.4.1-RC1-NIGHTLY, in this case, we should show 3.4 nightly. - s"the same nightly or snapshot Scala 3.${version.minor - 1} compiler" - else s"a Scala 3.${version.minor}.0 compiler or newer" - } - else if (version.experimental > 0) "the same Scala compiler" // unknown major version, just say same - else "a more recent Scala compiler" // unknown major version, just say later - - /** The description of the upgraded scala compiler that can read the given TASTy version */ - final def upgradedReaderTool(version: TastyVersion): String = asScala3Compiler(version) - - /** The description of the upgraded scala compiler that can produce the given TASTy version */ - final def upgradedProducerTool(version: TastyVersion): String = asScala3Compiler(version) - - final def recompileAdditionalInfo: String = """ - | Usually this means that the library dependency containing this file should be updated.""".stripMargin - - final def upgradeAdditionalInfo(fileVersion: TastyVersion): String = - if (fileVersion.isExperimental && experimentalVersion == 0) { - """ - | Note that you are using a stable compiler, which can not read experimental TASTy.""".stripMargin - } - else "" - } - trait Generic extends UnpicklerConfig { final def upgradedProducerTool(version: TastyVersion): String = "a later version" @@ -102,9 +73,6 @@ object UnpicklerConfig { else "" } - /** A config for the TASTy reader of a scala 3 compiler */ - val scala3Compiler = new UnpicklerConfig with Scala3Compiler with DefaultTastyVersion {} - /** A config for the TASTy reader of a generic tool */ val generic = new UnpicklerConfig with Generic with DefaultTastyVersion {} } diff --git a/tests/pos-with-compiler-cc/backend/jvm/GenBCode.scala b/tests/pos-with-compiler-cc/backend/jvm/GenBCode.scala index 8ca2eab9ea8a..1af7e5dd705a 100644 --- a/tests/pos-with-compiler-cc/backend/jvm/GenBCode.scala +++ b/tests/pos-with-compiler-cc/backend/jvm/GenBCode.scala @@ -27,6 +27,7 @@ import java.io.DataOutputStream import java.nio.channels.ClosedByInterruptException import dotty.tools.tasty.{ TastyBuffer, TastyHeaderUnpickler, UnpicklerConfig } +import dotty.tools.tasty.core.TastyUnpickler import scala.tools.asm import scala.tools.asm.Handle @@ -285,7 +286,7 @@ class GenBCodePipeline(val int: DottyBackendInterface, val primitives: DottyPrim throw ex finally outstream.close() - val uuid = new TastyHeaderUnpickler(UnpicklerConfig.scala3Compiler, binary()).readHeader() + val uuid = new TastyHeaderUnpickler(TastyUnpickler.scala3CompilerConfig, binary()).readHeader() val lo = uuid.getMostSignificantBits val hi = uuid.getLeastSignificantBits From 88dd1cab1875bfd17a9c731102fea824fe15a827 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Thu, 2 Nov 2023 17:51:43 +0100 Subject: [PATCH 5/7] fix typo --- tasty/src/dotty/tools/tasty/TastyHeaderUnpickler.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasty/src/dotty/tools/tasty/TastyHeaderUnpickler.scala b/tasty/src/dotty/tools/tasty/TastyHeaderUnpickler.scala index 237544d99760..05cecf240770 100644 --- a/tasty/src/dotty/tools/tasty/TastyHeaderUnpickler.scala +++ b/tasty/src/dotty/tools/tasty/TastyHeaderUnpickler.scala @@ -128,7 +128,7 @@ class TastyHeaderUnpickler(config: UnpicklerConfig, reader: TastyReader) { ) check(validVersion, { - // failure means that the TASTy file is can not be read, therefore it is either: + // failure means that the TASTy file cannot be read, therefore it is either: // - backwards incompatible major, in which case the library should be recompiled by the minimum stable minor // version supported by this compiler // - any experimental in an older minor, in which case the library should be recompiled by the stable From 02ec718e4ad4e63b4152d9d71374c3e24e82adaf Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Thu, 2 Nov 2023 18:23:32 +0100 Subject: [PATCH 6/7] add explicit result types --- compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala | 2 +- tasty/src/dotty/tools/tasty/TastyHeaderUnpickler.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala index 59f5600ff44b..679df42daca8 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala @@ -56,7 +56,7 @@ object TastyUnpickler { end Scala3CompilerConfig /** A config for the TASTy reader of a scala 3 compiler */ - val scala3CompilerConfig = new Scala3CompilerConfig with UnpicklerConfig.DefaultTastyVersion {} + val scala3CompilerConfig: UnpicklerConfig = new Scala3CompilerConfig with UnpicklerConfig.DefaultTastyVersion {} } diff --git a/tasty/src/dotty/tools/tasty/TastyHeaderUnpickler.scala b/tasty/src/dotty/tools/tasty/TastyHeaderUnpickler.scala index 05cecf240770..1a67913e68ca 100644 --- a/tasty/src/dotty/tools/tasty/TastyHeaderUnpickler.scala +++ b/tasty/src/dotty/tools/tasty/TastyHeaderUnpickler.scala @@ -74,7 +74,7 @@ object UnpicklerConfig { } /** A config for the TASTy reader of a generic tool */ - val generic = new UnpicklerConfig with Generic with DefaultTastyVersion {} + val generic: UnpicklerConfig = new UnpicklerConfig with Generic with DefaultTastyVersion {} } class TastyHeaderUnpickler(config: UnpicklerConfig, reader: TastyReader) { From df4da02bcd947fb61991426c2f793a32c9bd1998 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Fri, 3 Nov 2023 11:44:11 +0100 Subject: [PATCH 7/7] tweak message header --- .../core/tasty/TastyHeaderUnpicklerTest.scala | 30 +++++++++---------- .../tools/tasty/TastyHeaderUnpickler.scala | 8 ++--- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/compiler/test/dotty/tools/dotc/core/tasty/TastyHeaderUnpicklerTest.scala b/compiler/test/dotty/tools/dotc/core/tasty/TastyHeaderUnpicklerTest.scala index c722af979d76..53c1f40638a4 100644 --- a/compiler/test/dotty/tools/dotc/core/tasty/TastyHeaderUnpicklerTest.scala +++ b/compiler/test/dotty/tools/dotc/core/tasty/TastyHeaderUnpicklerTest.scala @@ -56,7 +56,7 @@ class TastyHeaderUnpicklerTest { val file = TastyVersion(28, 4, 1) val read = TastyVersion(28, 4, 0) expectUnpickleError(runTest(file, read, "Scala 3.4.0-RC1-bin-SNAPSHOT")) { - """TASTy file produced by Scala 3.4.0-RC1-bin-SNAPSHOT has a backward incompatible TASTy version 28.4-experimental-1, + """Backward incompatible TASTy file has version 28.4-experimental-1, produced by Scala 3.4.0-RC1-bin-SNAPSHOT, | expected stable TASTy from 28.0 to 28.4. | The source of this file should be recompiled by a Scala 3.4.0 compiler or newer. | Usually this means that the library dependency containing this file should be updated. @@ -70,7 +70,7 @@ class TastyHeaderUnpicklerTest { val file = TastyVersion(28, 3, 1) val read = TastyVersion(28, 4, 0) expectUnpickleError(runTest(file, read, "Scala 3.2.1-RC1-bin-SNAPSHOT")) { - """TASTy file produced by Scala 3.2.1-RC1-bin-SNAPSHOT has a backward incompatible TASTy version 28.3-experimental-1, + """Backward incompatible TASTy file has version 28.3-experimental-1, produced by Scala 3.2.1-RC1-bin-SNAPSHOT, | expected stable TASTy from 28.0 to 28.4. | The source of this file should be recompiled by a Scala 3.3.0 compiler or newer. | Usually this means that the library dependency containing this file should be updated. @@ -84,7 +84,7 @@ class TastyHeaderUnpicklerTest { val file = TastyVersion(27, 3, 0) val read = TastyVersion(28, 3, 0) expectUnpickleError(runTest(file, read, "Scala 3.0.0-M1")) { - """TASTy file has a backward incompatible TASTy version 27.3, + """Backward incompatible TASTy file has version 27.3, | expected stable TASTy from 28.0 to 28.3. | The source of this file should be recompiled by a Scala 3.0.0 compiler or newer. | Usually this means that the library dependency containing this file should be updated. @@ -99,7 +99,7 @@ class TastyHeaderUnpicklerTest { val file = TastyVersion(27, 3, 0) val read = TastyVersion(28, 3, 0) expectUnpickleError(runTest(file, read, "Scala 3.0.0-M1", generic = true)) { - """TASTy file has a backward incompatible TASTy version 27.3, + """Backward incompatible TASTy file has version 27.3, | expected stable TASTy from 28.0 to 28.3. | The source of this file should be recompiled by a later version. | Usually this means that the classpath entry of this file should be updated. @@ -113,7 +113,7 @@ class TastyHeaderUnpicklerTest { val file = TastyVersion(28, 4, 1) val read = TastyVersion(28, 4, 2) expectUnpickleError(runTest(file, read, "Scala 3.3.3-RC1-NIGHTLY")) { - """TASTy file produced by Scala 3.3.3-RC1-NIGHTLY has a backward incompatible TASTy version 28.4-experimental-1, + """Backward incompatible TASTy file has version 28.4-experimental-1, produced by Scala 3.3.3-RC1-NIGHTLY, | expected stable TASTy from 28.0 to 28.3, or exactly 28.4-experimental-2. | The source of this file should be recompiled by the same nightly or snapshot Scala 3.3 compiler. | Usually this means that the library dependency containing this file should be updated. @@ -128,7 +128,7 @@ class TastyHeaderUnpicklerTest { val file = TastyVersion(28, 4, 1) val read = TastyVersion(28, 4, 2) expectUnpickleError(runTest(file, read, "Scala 3.3.3-RC1-NIGHTLY", generic = true)) { - """TASTy file produced by Scala 3.3.3-RC1-NIGHTLY has a backward incompatible TASTy version 28.4-experimental-1, + """Backward incompatible TASTy file has version 28.4-experimental-1, produced by Scala 3.3.3-RC1-NIGHTLY, | expected stable TASTy from 28.0 to 28.3, or exactly 28.4-experimental-2. | The source of this file should be recompiled by a later version. | Usually this means that the classpath entry of this file should be updated. @@ -142,7 +142,7 @@ class TastyHeaderUnpicklerTest { val file = TastyVersion(28, 3, 0) val read = TastyVersion(28, 2, 0) expectUnpickleError(runTest(file, read, "Scala 3.3.1")) { - """TASTy file produced by Scala 3.3.1 has a forward incompatible TASTy version 28.3, + """Forward incompatible TASTy file has version 28.3, produced by Scala 3.3.1, | expected stable TASTy from 28.0 to 28.2. | To read this TASTy file, use a Scala 3.3.0 compiler or newer. | Please refer to the documentation for information on TASTy versioning: @@ -156,7 +156,7 @@ class TastyHeaderUnpicklerTest { val file = TastyVersion(28, 3, 0) val read = TastyVersion(28, 2, 0) expectUnpickleError(runTest(file, read, "Scala 3.3.1", generic = true)) { - """TASTy file produced by Scala 3.3.1 has a forward incompatible TASTy version 28.3, + """Forward incompatible TASTy file has version 28.3, produced by Scala 3.3.1, | expected stable TASTy from 28.0 to 28.2. | To read this TASTy file, use a newer version of this tool compatible with TASTy 28.3. | Please refer to the documentation for information on TASTy versioning: @@ -169,7 +169,7 @@ class TastyHeaderUnpicklerTest { val file = TastyVersion(28, 3, 1) val read = TastyVersion(28, 2, 0) expectUnpickleError(runTest(file, read, "Scala 3.2.2-RC1-NIGHTLY")) { - """TASTy file produced by Scala 3.2.2-RC1-NIGHTLY has a forward incompatible TASTy version 28.3-experimental-1, + """Forward incompatible TASTy file has version 28.3-experimental-1, produced by Scala 3.2.2-RC1-NIGHTLY, | expected stable TASTy from 28.0 to 28.2. | To read this experimental TASTy file, use the same nightly or snapshot Scala 3.2 compiler. | Note that you are using a stable compiler, which can not read experimental TASTy. @@ -183,7 +183,7 @@ class TastyHeaderUnpicklerTest { val file = TastyVersion(29, 0, 0) val read = TastyVersion(28, 3, 0) expectUnpickleError(runTest(file, read, "Scala 4.0.0")) { - """TASTy file produced by Scala 4.0.0 has a forward incompatible TASTy version 29.0, + """Forward incompatible TASTy file has version 29.0, produced by Scala 4.0.0, | expected stable TASTy from 28.0 to 28.3. | To read this TASTy file, use a more recent Scala compiler. | Please refer to the documentation for information on TASTy versioning: @@ -196,7 +196,7 @@ class TastyHeaderUnpicklerTest { val file = TastyVersion(29, 0, 1) val read = TastyVersion(28, 3, 0) expectUnpickleError(runTest(file, read, "Scala 4.0.0-M1")) { - """TASTy file produced by Scala 4.0.0-M1 has a forward incompatible TASTy version 29.0-experimental-1, + """Forward incompatible TASTy file has version 29.0-experimental-1, produced by Scala 4.0.0-M1, | expected stable TASTy from 28.0 to 28.3. | To read this experimental TASTy file, use the same Scala compiler. | Note that you are using a stable compiler, which can not read experimental TASTy. @@ -211,7 +211,7 @@ class TastyHeaderUnpicklerTest { val file = TastyVersion(29, 0, 1) val read = TastyVersion(28, 3, 0) expectUnpickleError(runTest(file, read, "Scala 4.0.0-M1", generic = true)) { - """TASTy file produced by Scala 4.0.0-M1 has a forward incompatible TASTy version 29.0-experimental-1, + """Forward incompatible TASTy file has version 29.0-experimental-1, produced by Scala 4.0.0-M1, | expected stable TASTy from 28.0 to 28.3. | To read this experimental TASTy file, use the version of this tool compatible with TASTy 29.0-experimental-1. | Note that this tool does not support reading experimental TASTy. @@ -225,7 +225,7 @@ class TastyHeaderUnpicklerTest { val file = TastyVersion(28, 4, 0) val read = TastyVersion(28, 4, 1) // 3.4.0-RC1-NIGHTLY expectUnpickleError(runTest(file, read, "Scala 3.4.2")) { - """TASTy file produced by Scala 3.4.2 has a forward incompatible TASTy version 28.4, + """Forward incompatible TASTy file has version 28.4, produced by Scala 3.4.2, | expected stable TASTy from 28.0 to 28.3, or exactly 28.4-experimental-1. | To read this TASTy file, use a Scala 3.4.0 compiler or newer. | Please refer to the documentation for information on TASTy versioning: @@ -238,7 +238,7 @@ class TastyHeaderUnpicklerTest { val file = TastyVersion(28, 4, 2) val read = TastyVersion(28, 4, 1) expectUnpickleError(runTest(file, read, "Scala 3.3.3-RC2-NIGHTLY")) { - """TASTy file produced by Scala 3.3.3-RC2-NIGHTLY has a forward incompatible TASTy version 28.4-experimental-2, + """Forward incompatible TASTy file has version 28.4-experimental-2, produced by Scala 3.3.3-RC2-NIGHTLY, | expected stable TASTy from 28.0 to 28.3, or exactly 28.4-experimental-1. | To read this experimental TASTy file, use the same nightly or snapshot Scala 3.3 compiler. | Please refer to the documentation for information on TASTy versioning: @@ -252,7 +252,7 @@ class TastyHeaderUnpicklerTest { val file = TastyVersion(28, 4, 2) val read = TastyVersion(28, 4, 1) expectUnpickleError(runTest(file, read, "Scala 3.3.3-RC2-NIGHTLY", generic = true)) { - """TASTy file produced by Scala 3.3.3-RC2-NIGHTLY has a forward incompatible TASTy version 28.4-experimental-2, + """Forward incompatible TASTy file has version 28.4-experimental-2, produced by Scala 3.3.3-RC2-NIGHTLY, | expected stable TASTy from 28.0 to 28.3, or exactly 28.4-experimental-1. | To read this experimental TASTy file, use the version of this tool compatible with TASTy 28.4-experimental-2. | Please refer to the documentation for information on TASTy versioning: diff --git a/tasty/src/dotty/tools/tasty/TastyHeaderUnpickler.scala b/tasty/src/dotty/tools/tasty/TastyHeaderUnpickler.scala index 1a67913e68ca..db07666d3be1 100644 --- a/tasty/src/dotty/tools/tasty/TastyHeaderUnpickler.scala +++ b/tasty/src/dotty/tools/tasty/TastyHeaderUnpickler.scala @@ -103,7 +103,7 @@ class TastyHeaderUnpickler(config: UnpicklerConfig, reader: TastyReader) { val fileMinor = readNat() val fileVersion = TastyVersion(fileMajor, fileMinor, 0) val toolVersion = TastyVersion(toolMajor, toolMinor, toolExperimental) - val signature = signatureString(fileVersion, toolVersion, what = "backward", tool = None) + val signature = signatureString(fileVersion, toolVersion, what = "Backward", tool = None) val fix = recompileFix(toolVersion.minStable) throw new UnpickleException(signature + fix + tastyAddendum) } @@ -141,7 +141,7 @@ class TastyHeaderUnpickler(config: UnpicklerConfig, reader: TastyReader) { val compat = Compatibility.failReason(file = fileVersion, read = toolVersion) - val what = if (compat < 0) "backward" else "forward" + val what = if (compat < 0) "Backward" else "Forward" val signature = signatureString(fileVersion, toolVersion, what, tool = Some(toolingVersion)) val fix = ( if (compat < 0) { @@ -169,8 +169,8 @@ class TastyHeaderUnpickler(config: UnpicklerConfig, reader: TastyReader) { private def signatureString( fileVersion: TastyVersion, toolVersion: TastyVersion, what: String, tool: Option[String]) = { - val optProducedBy = tool.fold("")(t => s" produced by $t") - s"""TASTy file$optProducedBy has a $what incompatible TASTy version ${fileVersion.show}, + val optProducedBy = tool.fold("")(t => s", produced by $t") + s"""$what incompatible TASTy file has version ${fileVersion.show}$optProducedBy, | expected ${toolVersion.validRange}. |""".stripMargin }