add LF 1.dev "minor dev version" (#709)

* clean up v3 contract key case in TransactionVersions

* clean up duplicate cases in ValueVersions.assignVersion

* make LanguageMinorVersion a proper ADT, aliased LanguageVersion.Minor

* port DecodeV1 to LanguageMinorVersion ADT via implicit conversion

- @remyhaemmerle-da might prefer the explicit application of Stable/Dev

* make releasePrecedes private

* add dev versions to the timeline

* copyright header for LanguageMinorVersion

* build the package database for 1.dev, support 1.dev as --target

* test presence and new-ness of "dev" in timeline

* report dev versions in engine info; note 1.dev target in release notes

* spec and governance rules on 1.dev and minor dev in general

* make the governance rule about dev more direct

* 1.x

* missed plural; thanks @leo-da

* further definition of 1.dev in LFv1 spec; thanks @remyhaemmerle-da
This commit is contained in:
Stephen Compall 2019-05-03 04:36:45 -04:00 committed by Francesco Mazzoli
parent 12e8baa93f
commit f5490a5454
18 changed files with 158 additions and 66 deletions

View File

@ -58,7 +58,7 @@ minorFromCliOption = \case
_ -> Nothing
supportedInputVersions :: [Version]
supportedInputVersions = [version1_1, version1_2, version1_3]
supportedInputVersions = [version1_1, version1_2, version1_3, V1 PointDev]
supportedOutputVersions :: [Version]
supportedOutputVersions = supportedInputVersions

View File

@ -12,6 +12,7 @@ import com.digitalasset.daml.lf.data.Ref.{
QualifiedName,
SimpleString
}
import com.digitalasset.daml.lf.archive.LanguageVersion
import com.digitalasset.daml.lf.lfpackage.Ast
import com.digitalasset.daml.lf.lfpackage.{Decode, DecodeV1}
import com.digitalasset.daml.lf.scenario.api.v1.{Module => ProtoModule}
@ -119,12 +120,17 @@ class Context(val contextId: Context.ContextId) {
val lfModules = loadModules.map(module =>
module.getModuleCase match {
case ProtoModule.ModuleCase.DAML_LF_1 =>
// TODO this duplicates/skips the similar logic and extra version
// support check from `Decode`'s functions; will improperly accept
// too-new versions as a result
val lfMod = DamlLf1.Module
.parser()
.parseFrom(
Decode.damlLfCodedInputStream(module.getDamlLf1.newInput)
)
new DecodeV1(module.getMinor).ModuleDecoder(homePackageId, lfMod).decode()
new DecodeV1(LanguageVersion.Minor fromProtoIdentifier module.getMinor)
.ModuleDecoder(homePackageId, lfMod)
.decode()
case ProtoModule.ModuleCase.DAML_LF_DEV | ProtoModule.ModuleCase.MODULE_NOT_SET =>
throw Context.ContextException("Module.MODULE_NOT_SET")
})

View File

@ -17,6 +17,7 @@ DAML_LF_VERSIONS = [
"1.1",
"1.2",
"1.3",
"1.dev",
]
daml_package_db(
@ -39,8 +40,6 @@ daml_package_db(
for ver in DAML_LF_VERSIONS
]
# TODO SC: revert a21570717b and update for 1.dev https://github.com/digital-asset/daml/issues/179
[
daml_package_rule(
name = "daml-stdlib-{}".format(ver),

View File

@ -6,21 +6,27 @@ package archive
// an ADT version of the DAML-LF version
sealed abstract class LanguageMajorVersion(
val maxSupportedMinorVersion: LanguageMinorVersion,
previousMinorVersions: List[LanguageMinorVersion])
extends LfVersions(maxSupportedMinorVersion, previousMinorVersions)(identity)
val pretty: String,
maxSupportedStable: String,
previousStable: List[String])
extends LfVersions(
LanguageMinorVersion.Dev,
(previousStable :+ maxSupportedStable) map LanguageMinorVersion.Stable)(_.toProtoIdentifier)
with Product
with Serializable {
val maxSupportedStableMinorVersion: LanguageMinorVersion.Stable =
LanguageMinorVersion.Stable(maxSupportedStable)
// do *not* use implicitly unless type `LanguageMinorVersion` becomes
// indexed by the enclosing major version's singleton type --SC
def minorVersionOrdering: Ordering[LanguageMinorVersion] =
final def minorVersionOrdering: Ordering[LanguageMinorVersion] =
Ordering.by(acceptedVersions.zipWithIndex.toMap)
val supportedMinorVersions: List[LanguageMinorVersion] =
acceptedVersions
final def supportsMinorVersion(fromLFFile: LanguageMinorVersion): Boolean =
private[archive] final def supportsMinorVersion(fromLFFile: String): Boolean =
isAcceptedVersion(fromLFFile).isDefined
}
@ -28,12 +34,13 @@ object LanguageMajorVersion {
// Note that DAML-LF v0 never had and never will have minor versions.
case object V0
extends LanguageMajorVersion(maxSupportedMinorVersion = "", previousMinorVersions = List())
extends LanguageMajorVersion(pretty = "0", maxSupportedStable = "", previousStable = List())
case object V1
extends LanguageMajorVersion(
maxSupportedMinorVersion = "3",
previousMinorVersions = List("0", "1", "2"))
pretty = "1",
maxSupportedStable = "3",
previousStable = List("0", "1", "2"))
val All: List[LanguageMajorVersion] = List(V0, V1)

View File

@ -0,0 +1,30 @@
// Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.digitalasset.daml.lf
package archive
sealed abstract class LanguageMinorVersion extends Product with Serializable {
import LanguageMinorVersion._
def toProtoIdentifier: String = this match {
case Stable(id) => id
case Dev => "dev"
}
}
object LanguageMinorVersion {
final case class Stable(identifier: String) extends LanguageMinorVersion
case object Dev extends LanguageMinorVersion
def fromProtoIdentifier(identifier: String): LanguageMinorVersion = identifier match {
case "dev" => Dev
case _ => Stable(identifier)
}
object Implicits {
import scala.language.implicitConversions
implicit def `LMV from proto identifier`(identifier: String): LanguageMinorVersion =
fromProtoIdentifier(identifier)
}
}

View File

@ -9,11 +9,20 @@ final case class LanguageVersion(major: LanguageMajorVersion, minor: LanguageMin
object LanguageVersion {
type Major = LanguageMajorVersion
val Major = LanguageMajorVersion
type Minor = LanguageMinorVersion
val Minor = LanguageMinorVersion
val defaultV0: LanguageVersion =
LanguageVersion(LMV.V0, LMV.V0.maxSupportedMinorVersion)
LanguageVersion(LMV.V0, LMV.V0.maxSupportedStableMinorVersion)
val defaultV1: LanguageVersion =
LanguageVersion(LMV.V1, LMV.V1.maxSupportedMinorVersion)
LanguageVersion(LMV.V1, LMV.V1.maxSupportedStableMinorVersion)
private[lf] def apply(major: LanguageMajorVersion, minor: String): LanguageVersion =
apply(major, Minor fromProtoIdentifier minor)
def default: LanguageVersion =
defaultV1

View File

@ -78,15 +78,16 @@ abstract class Reader[+Pkg] {
// since we introduced minor versions once DAML-LF v1 was already
// out, and we want to be able to parse packages that were compiled
// before minor versions were a thing. DO NOT replicate this code
// bejond major version 1!
// beyond major version 1!
val minorVersion = (majorVersion, lf.getMinor) match {
case (LanguageMajorVersion.V1, "") => "0"
case (_, minor) => minor
}
val version = LanguageVersion(majorVersion, minorVersion)
val version =
LanguageVersion(majorVersion, LanguageVersion.Minor fromProtoIdentifier minorVersion)
if (!(majorVersion supportsMinorVersion minorVersion)) {
throw ParseError(
s"LF file $majorVersion.$minorVersion unsupported; maximum supported $majorVersion.x is $majorVersion.${majorVersion.maxSupportedMinorVersion}")
s"LF file $majorVersion.$minorVersion unsupported; maximum supported $majorVersion.x is $majorVersion.${majorVersion.maxSupportedStableMinorVersion.toProtoIdentifier: String}")
}
(readArchivePayloadOfVersion(hash, lf, version), majorVersion)
}

View File

@ -1,10 +0,0 @@
// Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.digitalasset.daml.lf
package object archive {
type LanguageMinorVersion = String
}

View File

@ -3,7 +3,7 @@
package com.digitalasset.daml.lf.engine
import com.digitalasset.daml.lf.archive.{LanguageMajorVersion => LMV}
import com.digitalasset.daml.lf.archive.{LanguageVersion => LV}
import com.digitalasset.daml.lf.transaction.TransactionVersions
import com.digitalasset.daml.lf.value.ValueVersions
@ -22,18 +22,17 @@ object EngineInfo {
private def formatLfVersions: String = {
val allVersions: Iterable[String] =
lfVersions("0", LMV.V0.supportedMinorVersions) ++
lfVersions("1", LMV.V1.supportedMinorVersions)
LV.Major.All flatMap (mv => lfVersions(mv.pretty, mv.supportedMinorVersions))
format(allVersions)
}
private def lfVersions(
majorVersion: String,
minorVersions: Iterable[String]): Iterable[String] = {
val nonEmptyMinorVersions = minorVersions.filter(_.nonEmpty)
if (nonEmptyMinorVersions.isEmpty) Seq(majorVersion)
else nonEmptyMinorVersions.map(a => s"$majorVersion.$a")
}
minorVersions: Iterable[LV.Minor]): Iterable[String] =
minorVersions.map { a =>
val ap = a.toProtoIdentifier
s"$majorVersion${if (ap.isEmpty) "" else s".$ap"}"
}
private def format(as: Iterable[String]): String = as.mkString(", ")
}

View File

@ -9,7 +9,7 @@ class EngineInfoTest extends WordSpec with Matchers {
EngineInfo.getClass.getSimpleName should {
"show supported LF, Transaction and Value versions" in {
EngineInfo.show shouldBe
"DAML LF Engine supports LF versions: 0, 1.0, 1.1, 1.2, 1.3; Transaction versions: 1, 2, 3, 4, 5; Value versions: 1, 2, 3, 4"
"DAML LF Engine supports LF versions: 0, 0.dev, 1.0, 1.1, 1.2, 1.3, 1.dev; Transaction versions: 1, 2, 3, 4, 5; Value versions: 1, 2, 3, 4"
}
"toString returns the same value as show" in {

View File

@ -192,6 +192,10 @@ always considered to be newer than every stable minor version of that
major version, it must be backward-compatible with all such stable
versions.
All newly-implemented minor version features or changes must be staged
in the *dev* version for later release as a stable group of
backward-compatible features.
The DAML-LF dev version is enabled in the sandbox and ledger server, but
will never be emitted by damlc unless explicitly requested via
``--target 1.dev`` or similar.

View File

@ -37,6 +37,8 @@ private[lf] class DecodeV1(minor: LanguageMinorVersion) extends Decode.OfPackage
}
case class ModuleDecoder(val packageId: SimpleString, val lfModule: PLF.Module) {
import LanguageMinorVersion.Implicits._
val moduleName = eitherToParseError(
ModuleName.fromSegments(lfModule.getName.getSegmentsList.asScala))
@ -664,6 +666,7 @@ private[lf] class DecodeV1(minor: LanguageMinorVersion) extends Decode.OfPackage
}
object DecodeV1 {
import LanguageMinorVersion.Implicits._
protected[lfpackage] val primTypeTable: Map[PLF.PrimType, (BuiltinType, LanguageMinorVersion)] = {
import PLF.PrimType._

View File

@ -66,8 +66,8 @@ and operate on the same major version of the serialization format in
a backward compatible way. This document describes DAML-LF major version
1, including all its minor versions.
Each DAML-LF program is accompanied by the version number of the
language is was serialized in. This number enables the DAML-LF engine
Each DAML-LF program is accompanied by the version identifier of the
language it was serialized in. This number enables the DAML-LF engine
to interpret previous versions of the language in a backward
compatibility way.
@ -79,12 +79,18 @@ mark lines within inference rules with annotations of the form
``[DAML-LF < x.y]`` and ``[DAML-LF ≥ x.y]`` to make the respective
line conditional upon the DAML-LF version.
Below, we list the versions of DAML-LF that a DAML-LF engine
compliant with the present specification must handle. The list comes
with a brief description of the changes, and some links to help
unfamiliar readers learn about the features involved in the change.
One can refer also to the `Serialization` section which is
particularly concerned about versioning and backward compatibility.
The version 1.dev is a special staging area for the next 1.x version to
be released. Compliant implementations are not required to implement any
features exclusive to version 1.dev, but should take them under
advisement as likely elements of the next 1.x version.
Below, we list the versions of DAML-LF 1.x that a DAML-LF
engine compliant with the present specification must handle [except for
1.dev], in ascending order. The list comes with a brief description of
the changes, and some links to help unfamiliar readers learn about the
features involved in the change. One can refer also to the
`Serialization` section which is particularly concerned about versioning
and backward compatibility.
Version 1.0
@ -170,6 +176,21 @@ Version: 1.3
* **Add** support for built-in Map.
Version: 1.dev
..............
* Introduction date:
2019-05-02
* Last amendment date:
2019-05-02
* Description:
* **Change** nothing yet.
Abstract syntax
^^^^^^^^^^^^^^^

View File

@ -33,15 +33,13 @@ object TransactionVersions
},
// a NodeCreate with defined `key` or a NodeLookupByKey
// implies minimum version 3
a.fold(GenTransaction.AnyOrder, minVersion) {
case (z, (_, gn)) =>
val reqV3 = gn match {
case nc: Node.NodeCreate[_, _] => nc.key.isDefined
case _: Node.NodeLookupByKey[_, _] => true
case _: Node.NodeFetch[_] | _: Node.NodeExercises[_, _, _] => false
}
if (reqV3) minKeyOrLookupByKey else z
},
if (a.nodes.values.exists {
case nc: Node.NodeCreate[_, _] => nc.key.isDefined
case _: Node.NodeLookupByKey[_, _] => true
case _: Node.NodeFetch[_] | _: Node.NodeExercises[_, _, _] => false
}) minKeyOrLookupByKey
else minVersion,
// a NodeFetch with actingParties implies minimum version 5
if (a.nodes.values
.exists { case nf: Node.NodeFetch[_] => nf.actingParties.nonEmpty; case _ => false })
minFetchActors

View File

@ -28,6 +28,7 @@ import scalaz.syntax.std.option._
*/
private[lf] object VersionTimeline {
import \&/.{This, That, Both}
import LanguageVersion.Minor.Dev
type AllVersions[:&:[_, _]] = (ValueVersion :&: TransactionVersion) :&: LanguageVersion
type Release = AllVersions[\&/]
@ -39,6 +40,7 @@ private[lf] object VersionTimeline {
private[transaction] val inAscendingOrder: NonEmptyList[Release] =
NonEmptyList(
That(LanguageVersion(LMV.V0, "")),
That(LanguageVersion(LMV.V0, Dev)),
Both(Both(ValueVersion("1"), TransactionVersion("1")), LanguageVersion(LMV.V1, "0")),
Both(Both(ValueVersion("2"), TransactionVersion("2")), LanguageVersion(LMV.V1, "1")),
This(That(TransactionVersion("3"))),
@ -46,10 +48,18 @@ private[lf] object VersionTimeline {
This(That(TransactionVersion("5"))),
That(LanguageVersion(LMV.V1, "2")),
Both(This(ValueVersion("4")), LanguageVersion(LMV.V1, "3")),
That(LanguageVersion(LMV.V1, Dev)),
// add new versions above this line
// do *not* backfill to make more Boths, because such would
// invalidate the timeline; use This and That instead as needed.
// invalidate the timeline, except to accompany Dev language
// versions; use This and That instead as needed.
// Backfill *is* appropriate if a release of the last hasn't happened
//
// "Dev" versions float through the timeline with little rationale
// due to their ephemeral contents; don't worry too much about their exact
// positioning, except where you desire a temporal implication between dev
// and some other version you're introducing. Dev always means "the dev
// supported by this release".
)
def foldRelease[Z: Semigroup](av: AllVersions[\&/])(
@ -121,7 +131,7 @@ private[lf] object VersionTimeline {
case (None, None) => None
}
def releasePrecedes(left: SpecifiedVersion, right: SpecifiedVersion): Boolean =
private def releasePrecedes(left: SpecifiedVersion, right: SpecifiedVersion): Boolean =
compareReleaseTime(left, right) contains Ordering.LT
// not antisymmetric, as unknown versions can't be compared

View File

@ -39,18 +39,13 @@ object ValueVersions
case FrontStackCons(value, values) =>
value match {
// for things supported since version 1, we do not need to check
case ValueContractId(_) => go(currentVersion, values)
case ValueRecord(_, fs) => go(currentVersion, fs.map(v => v._2) ++: values)
case ValueVariant(_, _, arg) => go(currentVersion, arg +: values)
case ValueList(vs) => go(currentVersion, vs.toImmArray ++: values)
case ValueInt64(_) => go(currentVersion, values)
case ValueDecimal(_) => go(currentVersion, values)
case ValueText(_) => go(currentVersion, values)
case ValueTimestamp(_) => go(currentVersion, values)
case ValueParty(_) => go(currentVersion, values)
case ValueBool(_) => go(currentVersion, values)
case ValueDate(_) => go(currentVersion, values)
case ValueUnit => go(currentVersion, values)
case ValueContractId(_) | ValueInt64(_) | ValueDecimal(_) | ValueText(_) |
ValueTimestamp(_) | ValueParty(_) | ValueBool(_) | ValueDate(_) | ValueUnit =>
go(currentVersion, values)
// for things added after version 1, we raise the minimum if present
case ValueOptional(x) =>
go(maxVV(minOptional, currentVersion), ImmArray(x.toList) ++: values)
case ValueMap(map) =>

View File

@ -64,6 +64,21 @@ class VersionTimelineSpec extends WordSpec with Matchers with PropertyChecks {
}
"compareReleaseTime" when {
import scalaz.Ordering._, Implicits._
"given defined versions" should {
"be defined" in forAll(genSpecifiedVersion, genSpecifiedVersion) { (l, r) =>
compareReleaseTime(l, r) shouldBe 'defined
}
}
"given a dev version" should {
"never precede another version of same major" in forAll(genDefinedLanguageVersion) { lv =>
compareReleaseTime(lv, lv copy (minor = LanguageVersion.Minor.Dev)) should contain oneOf (LT, EQ)
}
}
}
"latestWhenAllPresent" when {
"given only one version" should {
"give it back" in forAll(genLatestInput) { easva =>
@ -86,7 +101,7 @@ class VersionTimelineSpec extends WordSpec with Matchers with PropertyChecks {
implicit val ev: SubVersion[easva.T] = easva.run._2
val result = latestWhenAllPresent(sv, svs: _*)
import Implicits._
((sv: SpecifiedVersion) :: svs).map(releasePrecedes(_, result)) should contain(false)
((sv: SpecifiedVersion) :: svs).map(_ precedes result) should contain(false)
}
"be idempotent" in forAll(genLatestInput, Gen.listOf(genSpecifiedVersion)) { (easva, svs) =>
@ -152,13 +167,16 @@ object VersionTimelineSpec {
ExistsImpl(_run)
}
private val genDefinedLanguageVersion: Gen[LanguageVersion] =
Gen oneOf inAscendingOrder.foldMap(foldRelease(_)(constNil, constNil, List(_)))
private final case class Variety[A](gen: Gen[A])(implicit val sv: SubVersion[A])
@SuppressWarnings(Array("org.wartremover.warts.Any"))
private val varieties = NonEmptyList[Variety[_]](
Variety(Gen oneOf ValueVersions.acceptedVersions),
Variety(Gen oneOf TransactionVersions.acceptedVersions),
Variety(Gen oneOf inAscendingOrder.foldMap(foldRelease(_)(constNil, constNil, List(_)))),
Variety(genDefinedLanguageVersion),
)
private def oneOf[A](xs: NonEmptyList[Gen[A]]): Gen[A] = xs.tail match {

View File

@ -21,6 +21,8 @@ HEAD — ongoing
and the `updated documentation <https://github.com/digital-asset/daml/pull/740>`_.
- Delete the `id` function, use `identity` instead.
- Drop support for DAML-LF 1.0 from compiler
- DAML-LF "dev" minor version. Write with ``--target 1.dev``, supported by all tools by
default.
0.12.13 - 2019-05-02
--------------------