Skip to content

Commit bf9e225

Browse files
committed
Update reference, MiMa previous version and sync TASTy version (scala#22187)
* We now document better how and when tasty version should be set * Add additional runtime test to ensure we don't emit invalid TASTy version during Release / NIGHTLY releases and the expected version set in build matches version defined in TastyFormat
1 parent b15b55d commit bf9e225

File tree

3 files changed

+131
-0
lines changed

3 files changed

+131
-0
lines changed

project/Build.scala

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ object Build {
8585
* Reference version should track the latest version pushed to Maven:
8686
* - In main branch it should be the last RC version
8787
* - In release branch it should be the last stable release
88+
*
89+
* Warning: Change of this variable needs to be consulted with `expectedTastyVersion`
8890
*/
8991
val referenceVersion = "3.3.5"
9092

@@ -94,6 +96,8 @@ object Build {
9496
*
9597
* Should only be referred from `dottyVersion` or settings/tasks requiring simplified version string,
9698
* eg. `compatMode` or Windows native distribution version.
99+
*
100+
* Warning: Change of this variable might require updating `expectedTastyVersion`
97101
*/
98102
val developedVersion = "3.3.6"
99103

@@ -106,6 +110,25 @@ object Build {
106110
*/
107111
val baseVersion = s"$developedVersion-RC1"
108112

113+
/** The version of TASTY that should be emitted, checked in runtime test
114+
* For defails on how TASTY version should be set see related discussions:
115+
* - https://github.com/scala/scala3/issues/13447#issuecomment-912447107
116+
* - https://github.com/scala/scala3/issues/14306#issuecomment-1069333516
117+
* - https://github.com/scala/scala3/pull/19321
118+
*
119+
* Simplified rules, given 3.$minor.$patch = $developedVersion
120+
* - Major version is always 28
121+
* - TASTY minor version:
122+
* - in main (NIGHTLY): {if $patch == 0 || ${referenceVersion.matches(raw"3.$minor.0-RC\d")} then $minor else ${minor + 1}}
123+
* - in release branch is always equal to $minor
124+
* - TASTY experimental version:
125+
* - in main (NIGHTLY) is always experimental
126+
* - in release candidate branch is experimental if {patch == 0}
127+
* - in stable release is always non-experimetnal
128+
*/
129+
val expectedTastyVersion = "28.3"
130+
checkReleasedTastyVersion()
131+
109132
/** Final version of Scala compiler, controlled by environment variables. */
110133
val dottyVersion = {
111134
if (isRelease) baseVersion
@@ -2043,6 +2066,9 @@ object Build {
20432066
settings(disableDocSetting).
20442067
settings(
20452068
versionScheme := Some("semver-spec"),
2069+
Test / envVars ++= Map(
2070+
"EXPECTED_TASTY_VERSION" -> expectedTastyVersion,
2071+
),
20462072
if (mode == Bootstrapped) Def.settings(
20472073
commonMiMaSettings,
20482074
mimaBinaryIssueFilters ++= MiMaFilters.TastyCore,
@@ -2077,6 +2103,46 @@ object Build {
20772103
case Bootstrapped => commonBootstrappedSettings
20782104
})
20792105
}
2106+
2107+
/* Tests TASTy version invariants during NIGHLY, RC or Stable releases */
2108+
def checkReleasedTastyVersion(): Unit = {
2109+
case class ScalaVersion(minor: Int, patch: Int, isRC: Boolean)
2110+
def parseScalaVersion(version: String): ScalaVersion = version.split("\\.|-").take(4) match {
2111+
case Array("3", minor, patch) => ScalaVersion(minor.toInt, patch.toInt, false)
2112+
case Array("3", minor, patch, _) => ScalaVersion(minor.toInt, patch.toInt, true)
2113+
case other => sys.error(s"Invalid Scala base version string: $baseVersion")
2114+
}
2115+
lazy val version = parseScalaVersion(baseVersion)
2116+
lazy val referenceV = parseScalaVersion(referenceVersion)
2117+
lazy val (tastyMinor, tastyIsExperimental) = expectedTastyVersion.split("\\.|-").take(4) match {
2118+
case Array("28", minor) => (minor.toInt, false)
2119+
case Array("28", minor, "experimental", _) => (minor.toInt, true)
2120+
case other => sys.error(s"Invalid TASTy version string: $expectedTastyVersion")
2121+
}
2122+
2123+
if(isNightly) {
2124+
assert(tastyIsExperimental, "TASTY needs to be experimental in nightly builds")
2125+
val expectedTastyMinor = version.patch match {
2126+
case 0 => version.minor
2127+
case 1 if referenceV.patch == 0 && referenceV.isRC =>
2128+
// Special case for a period when reference version is a new unstable minor
2129+
// Needed for non_bootstrapped tests requiring either stable tasty or the same experimental version produced by both reference and bootstrapped compiler
2130+
assert(version.minor == referenceV.minor, "Expected reference and base version to use the same minor")
2131+
version.minor
2132+
case _ => version.minor + 1
2133+
}
2134+
assert(tastyMinor == expectedTastyMinor, "Invalid TASTy minor version")
2135+
}
2136+
2137+
if(isRelease) {
2138+
assert(version.minor == tastyMinor, "Minor versions of TASTY vesion and Scala version should match in release builds")
2139+
assert(!referenceV.isRC, "Stable release needs to use stable compiler version")
2140+
if (version.isRC && version.patch == 0)
2141+
assert(tastyIsExperimental, "TASTy should be experimental when releasing a new minor version RC")
2142+
else
2143+
assert(!tastyIsExperimental, "Stable version cannot use experimental TASTY")
2144+
}
2145+
}
20802146
}
20812147

20822148
object ScaladocConfigs {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package dotty.tools.tasty
2+
3+
import scala.annotation.internal.sharable
4+
5+
case class TastyVersion private(major: Int, minor: Int, experimental: Int) {
6+
def isExperimental: Boolean = experimental > 0
7+
8+
def nextStable: TastyVersion = copy(experimental = 0)
9+
10+
def minStable: TastyVersion = copy(minor = 0, experimental = 0)
11+
12+
def show: String = {
13+
val suffix = if (isExperimental) s"-experimental-$experimental" else ""
14+
s"$major.$minor$suffix"
15+
}
16+
17+
def kind: String =
18+
if (isExperimental) "experimental TASTy" else "TASTy"
19+
20+
def validRange: String = {
21+
val min = TastyVersion(major, 0, 0)
22+
val max = if (experimental == 0) this else TastyVersion(major, minor - 1, 0)
23+
val extra = Option.when(experimental > 0)(this)
24+
s"stable TASTy from ${min.show} to ${max.show}${extra.fold("")(e => s", or exactly ${e.show}")}"
25+
}
26+
}
27+
28+
object TastyVersion {
29+
30+
@sharable
31+
private val cache: java.util.concurrent.ConcurrentHashMap[TastyVersion, TastyVersion] =
32+
new java.util.concurrent.ConcurrentHashMap()
33+
34+
def apply(major: Int, minor: Int, experimental: Int): TastyVersion = {
35+
val version = new TastyVersion(major, minor, experimental)
36+
val cachedVersion = cache.putIfAbsent(version, version)
37+
if (cachedVersion == null) version else cachedVersion
38+
}
39+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package dotty.tools.tasty
2+
3+
import org.junit.Assert._
4+
import org.junit.Test
5+
6+
import TastyBuffer._
7+
8+
// Tests ensuring TASTY version emitted by compiler is matching expected TASTY version
9+
class BuildTastyVersionTest {
10+
11+
val CurrentTastyVersion = TastyVersion(TastyFormat.MajorVersion, TastyFormat.MinorVersion, TastyFormat.ExperimentalVersion)
12+
13+
// Needs to be defined in build Test/envVars
14+
val ExpectedTastyVersionEnvVar = "EXPECTED_TASTY_VERSION"
15+
16+
@Test def testBuildTastyVersion(): Unit = {
17+
val expectedVersion = sys.env.get(ExpectedTastyVersionEnvVar)
18+
.getOrElse(fail(s"Env variable $ExpectedTastyVersionEnvVar not defined"))
19+
.match {
20+
case s"$major.$minor-experimental-$experimental" => TastyVersion(major.toInt, minor.toInt, experimental.toInt)
21+
case s"$major.$minor" if minor.forall(_.isDigit) => TastyVersion(major.toInt, minor.toInt, 0)
22+
case other => fail(s"Invalid TASTY version string: $other")
23+
}
24+
assertEquals(expectedVersion, CurrentTastyVersion)
25+
}
26+
}

0 commit comments

Comments
 (0)