Skip to content

First stable Tasty release #12426

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion project/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,12 @@ object Build {
// Settings used when compiling dotty (both non-bootstrapped and bootstrapped)
lazy val commonDottySettings = commonSettings ++ Seq(
// Manually set the standard library to use
autoScalaLibrary := false
autoScalaLibrary := false,
classpathOptions ~= (old =>
old
.withAutoBoot(false) // no library on the compiler bootclasspath - we may need a more recent version
.withFilterLibrary(false) // ...instead, we put it on the compiler classpath
),
)

lazy val commonScala2Settings = commonSettings ++ Seq(
Expand Down
25 changes: 21 additions & 4 deletions tasty/src/dotty/tools/tasty/TastyFormat.scala
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ object TastyFormat {
* compatibility, but remains backwards compatible, with all
* preceeding `MinorVersion`.
*/
final val MinorVersion: Int = 0
final val MinorVersion: Int = 1

/**Natural Number. The `ExperimentalVersion` allows for
* experimentation with changes to TASTy without committing
Expand All @@ -300,14 +300,31 @@ object TastyFormat {
* is able to read final TASTy documents if the file's
* `MinorVersion` is strictly less than the current value.
*/
final val ExperimentalVersion: Int = 3
final val ExperimentalVersion: Int = 1

/**This method implements a binary relation (`<:<`) between two TASTy versions.
*
* We label the lhs `file` and rhs `compiler`.
* if `file <:< compiler` then the TASTy file is valid to be read.
*
* TASTy versions have a partial order,
* for example `a <:< b` and `b <:< a` are both false if `a` and `b` have different major versions.
* A TASTy version, e.g. `v := 28.0-3` is composed of three fields:
* - v.major == 28
* - v.minor == 0
* - v.experimental == 3
*
* TASTy versions have a partial order, for example,
* `a <:< b` and `b <:< a` are both false if
* - `a` and `b` have different `major` fields.
* - `a` and `b` have the same `major` & `minor` fields,
* but different `experimental` fields, both non-zero.
*
* A TASTy version with a zero value for its `experimental` field
* is considered to be stable. Files with a stable TASTy version
* can be read by a compiler with an unstable TASTy version,
* (where the compiler's TASTy version has a higher `minor` field).
*
* A compiler with a stable TASTy version can never read a file
* with an unstable TASTy version.
*
* We follow the given algorithm:
* ```
Expand Down
42 changes: 25 additions & 17 deletions tasty/test/dotty/tools/tasty/TastyVersionFormatTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,61 +11,69 @@ class TastyVersionFormatTest {
import TastyVersionFormatTest._

/** aliases `TastyVersion.apply` */
def compiler(major: Int, minor: Int, experimental: Int) = TastyVersion(major, minor, experimental)
def compiler(major: Int, minor: Int, experimental: Experimental) = TastyVersion(major, minor, experimental)

/** aliases `TastyVersion.apply` */
def file(major: Int, minor: Int, experimental: Int) = TastyVersion(major, minor, experimental)
def file(major: Int, minor: Int, experimental: Experimental) = TastyVersion(major, minor, experimental)

@Test def accept_ExperimentalReadEQExperimental_EQMinor: Unit = {
assert(file(28,1,1) <:< compiler(28,1,1)) // same minor, same experimental
assert(file(28,1,Exp(1)) <:< compiler(28,1,Exp(1))) // same minor, same experimental
}

@Test def accept_ExperimentalReadFinal_LTMinor: Unit = {
assert(file(28,0,0) <:< compiler(28,1,1)) // preceding minor
assert(file(28,0,Final) <:< compiler(28,1,Exp(1))) // preceding minor
}

@Test def accept_FinalReadFinal_LTEqualMinor: Unit = {
assert(file(28,0,0) <:< compiler(28,1,0)) // preceding minor
assert(file(28,0,0) <:< compiler(28,0,0)) // same minor
assert(file(28,0,Final) <:< compiler(28,1,Final)) // preceding minor
assert(file(28,0,Final) <:< compiler(28,0,Final)) // same minor
}

/** these cases are unrelated because a final compiler can only read final tasty of <= minor version */
@Test def reject_FinalReadFinal_GTMinor: Unit = {
assert(file(28,2,0) unrelatedTo compiler(28,1,0)) // succeeding minor
assert(file(28,2,Final) unrelatedTo compiler(28,1,Final)) // succeeding minor
}

/** these cases are unrelated because a final compiler can not read experimental tasty */
@Test def reject_FinalReadExperimental: Unit = {
assert(file(28,0,1) unrelatedTo compiler(28,1,0)) // preceding minor
assert(file(28,1,1) unrelatedTo compiler(28,1,0)) // same minor
assert(file(28,2,1) unrelatedTo compiler(28,1,0)) // succeeding minor
assert(file(28,0,Exp(1)) unrelatedTo compiler(28,1,Final)) // preceding minor
assert(file(28,1,Exp(1)) unrelatedTo compiler(28,1,Final)) // same minor
assert(file(28,2,Exp(1)) unrelatedTo compiler(28,1,Final)) // succeeding minor
}

/** These cases are unrelated because an experimental compiler can only read final tasty of < minor version */
@Test def reject_ExperimentalReadFinal_GTEqualMinor: Unit = {
assert(file(28,2,0) unrelatedTo compiler(28,1,1)) // succeeding minor
assert(file(28,1,0) unrelatedTo compiler(28,1,1)) // equal minor
assert(file(28,2,Final) unrelatedTo compiler(28,1,Exp(1))) // succeeding minor
assert(file(28,1,Final) unrelatedTo compiler(28,1,Exp(1))) // equal minor
}

/**These cases are unrelated because both compiler and file are experimental,
* and with unequal experimental part.
*/
@Test def reject_ExperimentalReadNEExperimental: Unit = {
assert(file(28,1,2) unrelatedTo compiler(28,1,1)) // same minor version, succeeding experimental
assert(file(28,1,1) unrelatedTo compiler(28,1,2)) // same minor version, preceding experimental
assert(file(28,1,Exp(2)) unrelatedTo compiler(28,1,Exp(1))) // same minor version, succeeding experimental
assert(file(28,1,Exp(1)) unrelatedTo compiler(28,1,Exp(2))) // same minor version, preceding experimental
}

/** these cases are unrelated because the major version must be identical */
@Test def reject_NEMajor: Unit = {
assert(file(27,0,0) unrelatedTo compiler(28,0,0)) // less than
assert(file(29,0,0) unrelatedTo compiler(28,0,0)) // greater than
assert(file(27,0,Final) unrelatedTo compiler(28,0,Final)) // less than
assert(file(29,0,Final) unrelatedTo compiler(28,0,Final)) // greater than
}

}

object TastyVersionFormatTest {

case class TastyVersion(major: Int, minor: Int, experimental: Int) { file =>
type Experimental = Int
val Final: Experimental = 0
def Exp(i: Int): Experimental = i.ensuring(_ > 0)

case class TastyVersion(major: Int, minor: Int, experimental: Experimental) { file =>
assert(major >= 0)
assert(minor >= 0)
assert(experimental >= 0)

def <:<(compiler: TastyVersion): Boolean = TastyFormat.isVersionCompatible(
fileMajor = file.major,
fileMinor = file.minor,
Expand Down