Skip to content

Commit

Permalink
Improve error message for mismatched tasty versions, allow configurat…
Browse files Browse the repository at this point in the history
…ion 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.
  • Loading branch information
bishabosha authored Nov 3, 2023
2 parents 9453cb1 + df4da02 commit a18ff54
Show file tree
Hide file tree
Showing 7 changed files with 527 additions and 140 deletions.
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/backend/jvm/CodeGen.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import java.io.DataOutputStream
import java.nio.channels.ClosedByInterruptException

import dotty.tools.tasty.{ TastyBuffer, TastyHeaderUnpickler }
import dotty.tools.dotc.core.tasty.TastyUnpickler

import scala.tools.asm
import scala.tools.asm.tree._
Expand Down Expand Up @@ -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(binary()).readHeader()
val uuid = new TastyHeaderUnpickler(TastyUnpickler.scala3CompilerConfig, binary()).readHeader()
val lo = uuid.getMostSignificantBits
val hi = uuid.getLeastSignificantBits

Expand Down
32 changes: 22 additions & 10 deletions compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ import ast.desugar

import parsing.JavaParsers.OutlineJavaParser
import parsing.Parsers.OutlineParser
import dotty.tools.tasty.TastyHeaderUnpickler
import dotty.tools.tasty.{TastyHeaderUnpickler, UnpickleException, UnpicklerConfig}
import dotty.tools.dotc.core.tasty.TastyUnpickler


object SymbolLoaders {
Expand Down Expand Up @@ -421,22 +422,33 @@ 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 =
val classfile =
val className = tastyFile.name.stripSuffix(".tasty")
tastyFile.resolveSibling(className + ".class")
if classfile != null then
val tastyUUID = new TastyHeaderUnpickler(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`
Expand Down
38 changes: 36 additions & 2 deletions compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ package tasty

import scala.language.unsafeNulls

import dotty.tools.tasty.{TastyFormat, TastyBuffer, TastyReader, TastyHeaderUnpickler}
import dotty.tools.tasty.{TastyFormat, TastyBuffer, TastyReader, TastyHeaderUnpickler, UnpicklerConfig}
import TastyHeaderUnpickler.TastyVersion
import TastyFormat.NameTags._, TastyFormat.nameTagToString
import TastyBuffer.NameRef

Expand All @@ -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: UnpicklerConfig = new Scala3CompilerConfig with UnpicklerConfig.DefaultTastyVersion {}

}

import TastyUnpickler._
Expand Down Expand Up @@ -88,7 +122,7 @@ class TastyUnpickler(reader: TastyReader) {
result
}

new TastyHeaderUnpickler(reader).readHeader()
new TastyHeaderUnpickler(scala3CompilerConfig, reader).readHeader()

locally {
until(readEnd()) { nameAtRef.add(readNameContents()) }
Expand Down
Loading

0 comments on commit a18ff54

Please sign in to comment.