Skip to content

Commit 496127d

Browse files
committed
fix #10375: address comments
1 parent ad7c20c commit 496127d

File tree

6 files changed

+148
-112
lines changed

6 files changed

+148
-112
lines changed

compiler/src/dotty/tools/dotc/config/Properties.scala

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,18 +64,24 @@ trait PropertiesTrait {
6464
*/
6565
def versionNumberString: String = scalaPropOrEmpty("version.number")
6666

67-
/** The version number of the jar this was loaded from plus "version " prefix,
68-
* or "version (unknown)" if it cannot be determined.
67+
/** The version number of the jar this was loaded from,
68+
* or `"(unknown)"` if it cannot be determined.
6969
*/
70-
val versionString: String = {
70+
val simpleVersionString: String = {
7171
val v = scalaPropOrElse("version.number", "(unknown)")
72-
"version " + scalaPropOrElse("version.number", "(unknown)") + {
72+
v + (
7373
if (v.contains("SNAPSHOT") || v.contains("NIGHTLY"))
7474
"-git-" + scalaPropOrElse("git.hash", "(unknown)")
75-
else ""
76-
}
75+
else
76+
""
77+
)
7778
}
7879

80+
/** The version number of the jar this was loaded from plus `"version "` prefix,
81+
* or `"version (unknown)"` if it cannot be determined.
82+
*/
83+
val versionString: String = "version " + simpleVersionString
84+
7985
/** Whether the current version of compiler is experimental
8086
*
8187
* 1. Snapshot and nightly releases are experimental.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import Decorators._
1515
object TastyPickler {
1616

1717
private val versionStringBytes = {
18-
val compilerString = s"Scala compiler ${config.Properties.versionString}"
18+
val compilerString = s"Scala ${config.Properties.simpleVersionString}"
1919
compilerString.getBytes(java.nio.charset.StandardCharsets.UTF_8)
2020
}
2121

tasty/src/dotty/tools/tasty/TastyFormat.scala

Lines changed: 85 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -263,27 +263,101 @@ Standard Section: "Comments" Comment*
263263

264264
object TastyFormat {
265265

266+
/** The first four bytes of a TASTy file, followed by four values:
267+
* - `MajorVersion: Int` - see definition in `TastyFormat`
268+
* - `MinorVersion: Int` - see definition in `TastyFormat`
269+
* - `ExperimentalVersion: Int` - see definition in `TastyFormat`
270+
* - `ToolingVersion: String` - arbitrary length string representing the tool that produced the TASTy.
271+
*/
266272
final val header: Array[Int] = Array(0x5C, 0xA1, 0xAB, 0x1F)
267273

268-
/** Each increment of the MajorVersion begins a new series of backward compatible TASTy versions
269-
* - The MajorVersion is and will always be the first value in a TASTy document after the header bytes.
270-
* - A TASTy document can then be further parsed if and only if the MajorVersion read
271-
* from a TASTy file is equal to or less than this value.
272-
* - i.e. `MajorVersion` and `header` are the only stable parts of TASTy that can be used to decide how to parse
273-
* the rest of the document.
274+
/**Natural number. Each increment of the `MajorVersion` begins a
275+
* new series of backward compatible TASTy versions.
276+
*
277+
* A TASTy file in either the preceeding or succeeding series is
278+
* incompatible with the current value.
274279
*/
275280
final val MajorVersion: Int = 28
276281

277-
/** The last minor version to break forward compatability */
282+
/**Natural number. Each increment of the `MinorVersion`, within
283+
* a series declared by the `MajorVersion`, breaks forward
284+
* compatibility, but remains backwards compatible, with all
285+
* preceeding `MinorVersion`.
286+
*/
278287
final val MinorVersion: Int = 0
279288

280-
/** A transitionary marker:
281-
* - 0 means that `MinorVersion` is from a final release of TASTy
282-
* - A positive number means TASTy was produced by an experimental compiler, released in between
283-
* increasing `MinorVersion`.
289+
/**Natural Number. The `ExperimentalVersion` allows for
290+
* experimentation with changes to TASTy without committing
291+
* to any guarantees of compatibility.
292+
*
293+
* A zero value indicates that the TASTy version is from a
294+
* stable, final release.
295+
*
296+
* A strictly positive value indicates that the TASTy
297+
* version is experimental. An experimental TASTy file
298+
* can only be read by a tool with the same version.
299+
* However, tooling with an experimental TASTy version
300+
* is able to read final TASTy documents if the file's
301+
* `MinorVersion` is strictly less than the current value.
284302
*/
285303
final val ExperimentalVersion: Int = 1
286304

305+
/**This method implements a binary relation (`<:<`) between two TASTy versions.
306+
* We label the lhs `file` and rhs `compiler`.
307+
* if `file <:< compiler` then the TASTy file is valid to be read.
308+
*
309+
* TASTy versions have a partial order,
310+
* for example `a <:< b` and `b <:< a` are both false if `a` and `b` have different major versions.
311+
*
312+
* We follow the given algorithm:
313+
* ```
314+
* if file.major != compiler.major then
315+
* return incompatible
316+
* if compiler.experimental == 0 then
317+
* if file.experimental != 0 then
318+
* return incompatible
319+
* if file.minor > compiler.minor then
320+
* return incompatible
321+
* else
322+
* return compatible
323+
* else invariant[compiler.experimental != 0]
324+
* if file.experimental == compiler.experimental then
325+
* if file.minor == compiler.minor then
326+
* return compatible (all fields equal)
327+
* else
328+
* return incompatible
329+
* else if file.experimental == 0,
330+
* if file.minor < compiler.minor then
331+
* return compatible (an experimental version can read a previous released version)
332+
* else
333+
* return incompatible (an experimental version cannot read its own minor version or any later version)
334+
* else invariant[file.experimental is non-0 and different than compiler.experimental]
335+
* return incompatible
336+
* ```
337+
*/
338+
def isVersionCompatible(
339+
fileMajor: Int,
340+
fileMinor: Int,
341+
fileExperimental: Int,
342+
compilerMajor: Int,
343+
compilerMinor: Int,
344+
compilerExperimental: Int
345+
): Boolean = (
346+
fileMajor == compilerMajor && (
347+
if (fileExperimental == compilerExperimental) {
348+
if (compilerExperimental == 0) {
349+
fileMinor <= compilerMinor
350+
}
351+
else {
352+
fileMinor == compilerMinor
353+
}
354+
}
355+
else {
356+
fileExperimental == 0 && fileMinor < compilerMinor
357+
}
358+
)
359+
)
360+
287361
final val ASTsSection = "ASTs"
288362
final val PositionsSection = "Positions"
289363
final val CommentsSection = "Comments"

tasty/src/dotty/tools/tasty/TastyHeaderUnpickler.scala

Lines changed: 35 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ sealed abstract case class TastyHeader(
2424
majorVersion: Int,
2525
minorVersion: Int,
2626
experimentalVersion: Int,
27-
toolingVersion: String // String could lead to a lot of duplication, perhaps cache headers?
27+
toolingVersion: String
2828
)
2929

3030
class TastyHeaderUnpickler(reader: TastyReader) {
@@ -45,8 +45,8 @@ class TastyHeaderUnpickler(reader: TastyReader) {
4545
val fileMajor = readNat()
4646
if (fileMajor <= 27) { // old behavior before `tasty-core` 3.0.0-M4
4747
val fileMinor = readNat()
48-
val signature = signatureString(expectedMinor, fileMajor, fileMinor.toString)
49-
throw new UnpickleException(signature + backIncompatAddendum + myAddendum)
48+
val signature = signatureString(fileMajor, fileMinor, 0)
49+
throw new UnpickleException(signature + backIncompatAddendum + toolingAddendum)
5050
}
5151
else {
5252
val fileMinor = readNat()
@@ -56,36 +56,29 @@ class TastyHeaderUnpickler(reader: TastyReader) {
5656
val start = currentAddr
5757
val end = start + length
5858
goto(end)
59-
new String(bytes, start.index, length) // NOTE: new string object here (should probably cache as it leaks)
59+
new String(bytes, start.index, length)
6060
}
6161

62-
val validVersion = ( // inlined from `dotty.tools.tasty.TastyVersionFormatTest.TastyVersion.<:<`
63-
fileMajor == MajorVersion && (
64-
if (fileExperimental == ExperimentalVersion) {
65-
if (ExperimentalVersion == 0) {
66-
fileMinor <= MinorVersion
67-
}
68-
else {
69-
fileMinor == MinorVersion
70-
}
71-
}
72-
else {
73-
fileExperimental == 0 && fileMinor < MinorVersion
74-
}
75-
)
62+
val validVersion = TastyFormat.isVersionCompatible(
63+
fileMajor = fileMajor,
64+
fileMinor = fileMinor,
65+
fileExperimental = fileExperimental,
66+
compilerMajor = MajorVersion,
67+
compilerMinor = MinorVersion,
68+
compilerExperimental = ExperimentalVersion
7669
)
70+
7771
check(validVersion, {
78-
val foundMinor = showMinorVersion(fileMinor, fileExperimental)
79-
val producedByAddendum = s"\nThe TASTy file was produced by $toolingVersion.$myAddendum"
80-
val signature = signatureString(expectedMinor, fileMajor, foundMinor)
81-
if (fileExperimental != 0)
82-
signature + unstableAddendum + producedByAddendum
83-
else if (fileMajor < MajorVersion)
84-
signature + backIncompatAddendum + producedByAddendum
85-
else
86-
signature + forwardIncompatAddendum + producedByAddendum
87-
}
88-
)
72+
val signature = signatureString(fileMajor, fileMinor, fileExperimental)
73+
val producedByAddendum = s"\nThe TASTy file was produced by $toolingVersion.$toolingAddendum"
74+
val msg = (
75+
if (fileExperimental != 0) unstableAddendum
76+
else if (fileMajor < MajorVersion) backIncompatAddendum
77+
else forwardIncompatAddendum
78+
)
79+
signature + msg + producedByAddendum
80+
})
81+
8982
val uuid = new UUID(readUncompressedLong(), readUncompressedLong())
9083
new TastyHeader(uuid, fileMajor, fileMinor, fileExperimental, toolingVersion) {}
9184
}
@@ -100,37 +93,36 @@ class TastyHeaderUnpickler(reader: TastyReader) {
10093

10194
object TastyHeaderUnpickler {
10295

103-
private def showMinorVersion(min: Int, exp: Int) = {
104-
val expStr = if (exp == 0) "" else s" [unstable release: $exp]"
105-
s"$min$expStr"
106-
}
107-
108-
private def expectedMinor = showMinorVersion(MinorVersion, ExperimentalVersion)
109-
110-
private def myAddendum = {
96+
private def toolingAddendum = (
11197
if (ExperimentalVersion > 0)
11298
"\nNote that your tooling is currently using an unstable TASTy version."
11399
else
114100
""
115-
}
101+
)
116102

117-
private def signatureString(minorVersion: String, fileMajor: Int, fileMinor: String) = (
103+
private def signatureString(fileMajor: Int, fileMinor: Int, fileExperimental: Int) = {
104+
def showMinorVersion(min: Int, exp: Int) = {
105+
val expStr = if (exp == 0) "" else s" [unstable release: $exp]"
106+
s"$min$expStr"
107+
}
108+
val minorVersion = showMinorVersion(MinorVersion, ExperimentalVersion)
109+
val fileMinorVersion = showMinorVersion(fileMinor, fileExperimental)
118110
s"""TASTy signature has wrong version.
119111
| expected: {majorVersion: $MajorVersion, minorVersion: $minorVersion}
120-
| found : {majorVersion: $fileMajor, minorVersion: $fileMinor}
112+
| found : {majorVersion: $fileMajor, minorVersion: $fileMinorVersion}
121113
|
122114
|""".stripMargin
123-
)
115+
}
124116

125117
private def unstableAddendum =
126118
"""This TASTy file was produced by an unstable release.
127119
|To read this TASTy file, your tooling must be at the same version.""".stripMargin
128120

129121
private def backIncompatAddendum =
130-
"""This TASTy file was produced by a backwards incompatible release.
122+
"""This TASTy file was produced by an earlier release that is not supported anymore.
131123
|Please recompile this TASTy with a later version.""".stripMargin
132124

133125
private def forwardIncompatAddendum =
134-
"""This TASTy file was produced by a forwards incompatible release.
126+
"""This TASTy file was produced by a more recent, forwards incompatible release.
135127
|To read this TASTy file, please upgrade your tooling.""".stripMargin
136128
}

tasty/test/dotty/tools/tasty/TastyHeaderUnpicklerTest.scala

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,35 +12,35 @@ class TastyHeaderUnpicklerTest {
1212
import TastyHeaderUnpicklerTest._
1313

1414
@Test def vanilla: Unit = {
15-
runTest(MajorVersion, MinorVersion, ExperimentalVersion, "Scala compiler 3.0.0-M4-bin-SNAPSHOT-git-12345")
15+
runTest(MajorVersion, MinorVersion, ExperimentalVersion, "Scala 3.0.0-M4-bin-SNAPSHOT-git-12345")
1616
}
1717

1818
@Test def failBumpExperimental: Unit = {
19-
(runTest(MajorVersion, MinorVersion, ExperimentalVersion + 1, "Scala compiler 3.0.0-M4-bin-SNAPSHOT-git-12345"))
19+
(runTest(MajorVersion, MinorVersion, ExperimentalVersion + 1, "Scala 3.0.0-M4-bin-SNAPSHOT-git-12345"))
2020
}
2121

2222
@Test def failBumpMinor: Unit = {
23-
(runTest(MajorVersion, MinorVersion + 1, ExperimentalVersion, "Scala compiler 3.1.0-RC1"))
23+
(runTest(MajorVersion, MinorVersion + 1, ExperimentalVersion, "Scala 3.1.0-RC1"))
2424
}
2525

2626
@Test def failBumpMajor: Unit = {
27-
(runTest(MajorVersion + 1, MinorVersion, ExperimentalVersion, "Scala compiler 4.0.0-M1"))
27+
(runTest(MajorVersion + 1, MinorVersion, ExperimentalVersion, "Scala 4.0.0-M1"))
2828
}
2929

3030
@Test def failBumpMajorFinal: Unit = {
31-
(runTest(MajorVersion + 1, MinorVersion, 0, "Scala compiler 4.0.0"))
31+
(runTest(MajorVersion + 1, MinorVersion, 0, "Scala 4.0.0"))
3232
}
3333

3434
@Test def okSubtractExperimental: Unit = {
35-
(runTest(MajorVersion, MinorVersion, ExperimentalVersion - 1, "Scala compiler 3.0.0"))
35+
(runTest(MajorVersion, MinorVersion, ExperimentalVersion - 1, "Scala 3.0.0"))
3636
}
3737

3838
@Test def okSubtractMinor: Unit = {
39-
(runTest(MajorVersion, MinorVersion - 1, ExperimentalVersion, "Scala compiler 3.0.0-M4-bin-SNAPSHOT-git-12345"))
39+
(runTest(MajorVersion, MinorVersion - 1, ExperimentalVersion, "Scala 3.0.0-M4-bin-SNAPSHOT-git-12345"))
4040
}
4141

4242
@Test def failSubtractMajor: Unit = {
43-
(runTest(MajorVersion - 1, MinorVersion, ExperimentalVersion, "Scala compiler 3.0.0-M4-bin-SNAPSHOT-git-12345"))
43+
(runTest(MajorVersion - 1, MinorVersion, ExperimentalVersion, "Scala 3.0.0-M4-bin-SNAPSHOT-git-12345"))
4444
}
4545

4646
}

tasty/test/dotty/tools/tasty/TastyVersionFormatTest.scala

Lines changed: 7 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -66,49 +66,13 @@ class TastyVersionFormatTest {
6666
object TastyVersionFormatTest {
6767

6868
case class TastyVersion(major: Int, minor: Int, experimental: Int) { file =>
69-
70-
/**if `file <:< compiler` then tasty file is valid to be read.
71-
*
72-
* Follows the given algorithm:
73-
* ```
74-
* if file.major != compiler.major then
75-
* return incompatible
76-
* if compiler.experimental == 0 then
77-
* if file.experimental != 0 then
78-
* return incompatible
79-
* if file.minor > compiler.minor then
80-
* return incompatible
81-
* else
82-
* return compatible
83-
* else invariant[compiler.experimental != 0]
84-
* if file.experimental == compiler.experimental then
85-
* if file.minor == compiler.minor then
86-
* return compatible (all fields equal)
87-
* else
88-
* return incompatible
89-
* else if file.experimental == 0,
90-
* if file.minor < compiler.minor then
91-
* return compatible (an experimental version can read a previous released version)
92-
* else
93-
* return incompatible (an experimental version cannot read its own minor version or any later version)
94-
* else invariant[file.experimental is non-0 and different than compiler.experimental]
95-
* return incompatible
96-
* ```
97-
*/
98-
def <:<(compiler: TastyVersion): Boolean = (
99-
file.major == compiler.major && (
100-
if (file.experimental == compiler.experimental) {
101-
if (compiler.experimental == 0) {
102-
file.minor <= compiler.minor
103-
}
104-
else {
105-
file.minor == compiler.minor
106-
}
107-
}
108-
else {
109-
file.experimental == 0 && file.minor < compiler.minor
110-
}
111-
)
69+
def <:<(compiler: TastyVersion): Boolean = TastyFormat.isVersionCompatible(
70+
fileMajor = file.major,
71+
fileMinor = file.minor,
72+
fileExperimental = file.experimental,
73+
compilerMajor = compiler.major,
74+
compilerMinor = compiler.minor,
75+
compilerExperimental = compiler.experimental
11276
)
11377

11478
/**if `file unrelated compiler` then tasty file must be rejected.*/

0 commit comments

Comments
 (0)