Skip to content

Commit a18ff54

Browse files
authored
Improve error message for mismatched tasty versions, allow configuration of header unpickler (#18828)
fixes #18427 Adds configuration ability to `TastyHeaderUnpickler`, why? `tasty-core` is intended to be a generic library, so for its error messages to suddenly assume the consumer is a scala compiler would be a breaking change, so we instead by default use a generic configuration (the old "tooling" style) and allow to plug-in a "scala compiler" configuration Also the configuration allows us to easily test the content of error messages.
2 parents 9453cb1 + df4da02 commit a18ff54

File tree

7 files changed

+527
-140
lines changed

7 files changed

+527
-140
lines changed

compiler/src/dotty/tools/backend/jvm/CodeGen.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import java.io.DataOutputStream
2525
import java.nio.channels.ClosedByInterruptException
2626

2727
import dotty.tools.tasty.{ TastyBuffer, TastyHeaderUnpickler }
28+
import dotty.tools.dotc.core.tasty.TastyUnpickler
2829

2930
import scala.tools.asm
3031
import scala.tools.asm.tree._
@@ -94,7 +95,7 @@ class CodeGen(val int: DottyBackendInterface, val primitives: DottyPrimitives)(
9495
for (binary <- unit.pickled.get(claszSymbol.asClass)) {
9596
generatedTasty += GeneratedTasty(store, binary)
9697
val tasty =
97-
val uuid = new TastyHeaderUnpickler(binary()).readHeader()
98+
val uuid = new TastyHeaderUnpickler(TastyUnpickler.scala3CompilerConfig, binary()).readHeader()
9899
val lo = uuid.getMostSignificantBits
99100
val hi = uuid.getLeastSignificantBits
100101

compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ import ast.desugar
2424

2525
import parsing.JavaParsers.OutlineJavaParser
2626
import parsing.Parsers.OutlineParser
27-
import dotty.tools.tasty.TastyHeaderUnpickler
27+
import dotty.tools.tasty.{TastyHeaderUnpickler, UnpickleException, UnpicklerConfig}
28+
import dotty.tools.dotc.core.tasty.TastyUnpickler
2829

2930

3031
object SymbolLoaders {
@@ -421,22 +422,33 @@ class TastyLoader(val tastyFile: AbstractFile) extends SymbolLoader {
421422
def description(using Context): String = "TASTy file " + tastyFile.toString
422423

423424
override def doComplete(root: SymDenotation)(using Context): Unit =
424-
val (classRoot, moduleRoot) = rootDenots(root.asClass)
425-
val tastyBytes = tastyFile.toByteArray
426-
val unpickler = new tasty.DottyUnpickler(tastyBytes)
427-
unpickler.enter(roots = Set(classRoot, moduleRoot, moduleRoot.sourceModule))(using ctx.withSource(util.NoSource))
428-
if mayLoadTreesFromTasty then
429-
classRoot.classSymbol.rootTreeOrProvider = unpickler
430-
moduleRoot.classSymbol.rootTreeOrProvider = unpickler
431-
checkTastyUUID(tastyFile, tastyBytes)
425+
try
426+
val (classRoot, moduleRoot) = rootDenots(root.asClass)
427+
val tastyBytes = tastyFile.toByteArray
428+
val unpickler = new tasty.DottyUnpickler(tastyBytes)
429+
unpickler.enter(roots = Set(classRoot, moduleRoot, moduleRoot.sourceModule))(using ctx.withSource(util.NoSource))
430+
if mayLoadTreesFromTasty then
431+
classRoot.classSymbol.rootTreeOrProvider = unpickler
432+
moduleRoot.classSymbol.rootTreeOrProvider = unpickler
433+
checkTastyUUID(tastyFile, tastyBytes)
434+
catch case e: RuntimeException =>
435+
val message = e match
436+
case e: UnpickleException =>
437+
i"""TASTy file ${tastyFile.canonicalPath} could not be read, failing with:
438+
| ${Option(e.getMessage).getOrElse("")}"""
439+
case _ =>
440+
i"""TASTy file ${tastyFile.canonicalPath} is broken, reading aborted with ${e.getClass}
441+
| ${Option(e.getMessage).getOrElse("")}"""
442+
if (ctx.debug) e.printStackTrace()
443+
throw IOException(message)
432444

433445

434446
private def checkTastyUUID(tastyFile: AbstractFile, tastyBytes: Array[Byte])(using Context): Unit =
435447
val classfile =
436448
val className = tastyFile.name.stripSuffix(".tasty")
437449
tastyFile.resolveSibling(className + ".class")
438450
if classfile != null then
439-
val tastyUUID = new TastyHeaderUnpickler(tastyBytes).readHeader()
451+
val tastyUUID = new TastyHeaderUnpickler(TastyUnpickler.scala3CompilerConfig, tastyBytes).readHeader()
440452
new ClassfileTastyUUIDParser(classfile)(ctx).checkTastyUUID(tastyUUID)
441453
else
442454
// This will be the case in any of our tests that compile with `-Youtput-only-tasty`

compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ package tasty
44

55
import scala.language.unsafeNulls
66

7-
import dotty.tools.tasty.{TastyFormat, TastyBuffer, TastyReader, TastyHeaderUnpickler}
7+
import dotty.tools.tasty.{TastyFormat, TastyBuffer, TastyReader, TastyHeaderUnpickler, UnpicklerConfig}
8+
import TastyHeaderUnpickler.TastyVersion
89
import TastyFormat.NameTags._, TastyFormat.nameTagToString
910
import TastyBuffer.NameRef
1011

@@ -24,6 +25,39 @@ object TastyUnpickler {
2425
def apply(ref: NameRef): TermName = names(ref.index)
2526
def contents: Iterable[TermName] = names
2627
}
28+
29+
trait Scala3CompilerConfig extends UnpicklerConfig:
30+
private def asScala3Compiler(version: TastyVersion): String =
31+
if (version.major == 28) {
32+
// scala 3.x.y series
33+
if (version.experimental > 0)
34+
// scenario here is someone using 3.4.0 to read 3.4.1-RC1-NIGHTLY, in this case, we should show 3.4 nightly.
35+
s"the same nightly or snapshot Scala 3.${version.minor - 1} compiler"
36+
else s"a Scala 3.${version.minor}.0 compiler or newer"
37+
}
38+
else if (version.experimental > 0) "the same Scala compiler" // unknown major version, just say same
39+
else "a more recent Scala compiler" // unknown major version, just say later
40+
41+
/** The description of the upgraded scala compiler that can read the given TASTy version */
42+
final def upgradedReaderTool(version: TastyVersion): String = asScala3Compiler(version)
43+
44+
/** The description of the upgraded scala compiler that can produce the given TASTy version */
45+
final def upgradedProducerTool(version: TastyVersion): String = asScala3Compiler(version)
46+
47+
final def recompileAdditionalInfo: String = """
48+
| Usually this means that the library dependency containing this file should be updated.""".stripMargin
49+
50+
final def upgradeAdditionalInfo(fileVersion: TastyVersion): String =
51+
if (fileVersion.isExperimental && experimentalVersion == 0) {
52+
"""
53+
| Note that you are using a stable compiler, which can not read experimental TASTy.""".stripMargin
54+
}
55+
else ""
56+
end Scala3CompilerConfig
57+
58+
/** A config for the TASTy reader of a scala 3 compiler */
59+
val scala3CompilerConfig: UnpicklerConfig = new Scala3CompilerConfig with UnpicklerConfig.DefaultTastyVersion {}
60+
2761
}
2862

2963
import TastyUnpickler._
@@ -88,7 +122,7 @@ class TastyUnpickler(reader: TastyReader) {
88122
result
89123
}
90124

91-
new TastyHeaderUnpickler(reader).readHeader()
125+
new TastyHeaderUnpickler(scala3CompilerConfig, reader).readHeader()
92126

93127
locally {
94128
until(readEnd()) { nameAtRef.add(readNameContents()) }

0 commit comments

Comments
 (0)