[LF] reenable requires for interface (#15561)

Co-authored-by: Moisés Ackerman <6054733+akrmn@users.noreply.github.com>
This commit is contained in:
Remy 2022-11-15 09:59:24 +01:00 committed by GitHub
parent b1edee94f8
commit a18fe164ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 103 additions and 152 deletions

View File

@ -208,7 +208,7 @@ SNASPSHOT_VERSIONS = [ver for ver in PROTO_LF_VERSIONS if ver != "1.dev"]
] if not is_windows else []
sh_test(
name = "proto_immutability_test",
name = "proto_immutability_check",
srcs = ["proto_check_hash.sh"],
data = [
":daml_lf_%s_archive_proto_srcs" % version

View File

@ -8,7 +8,7 @@ set -e
declare -a checkSums=(
"500eefd480e9af6940adf12e7ec4c2cf4975d4cb9b25096c15edb0d57d364de8 daml-lf/archive/src/stable/protobuf/com/daml/daml_lf_1_14/daml_lf_1.proto"
"22e549209116e91d8073a255e6d868be60515824c321015cc424f0b83634f199 daml-lf/archive/src/stable/protobuf/com/daml/daml_lf_1_14/daml_lf.proto"
"b7e6ebf85f99d506c1b9b08ccf9543ac24c3d39ce8ba4583b0d8c1b97e979032 daml-lf/archive/src/stable/protobuf/com/daml/daml_lf_1_15/daml_lf_1.proto"
"99d2469b0294b18322492e6d7e7d4482f478c07e3cd0b0d3159f7b2923d23815 daml-lf/archive/src/stable/protobuf/com/daml/daml_lf_1_15/daml_lf_1.proto"
"5e6e33e885e80384fcfde6ac5072b7d6a2e2582430a6a449f96cb06d48a0edbf daml-lf/archive/src/stable/protobuf/com/daml/daml_lf_1_15/daml_lf.proto"
)

View File

@ -814,7 +814,7 @@ private[archive] class DecodeV1(minor: LV.Minor) {
DefInterface.build(
requires =
if (lfInterface.getRequiresCount != 0) {
assertSince(LV.Features.extendedInterfaces, "DefInterface.requires")
assertSince(LV.Features.basicInterfaces, "DefInterface.requires")
lfInterface.getRequiresList.asScala.view.map(decodeTypeConName)
} else
List.empty,
@ -1417,7 +1417,7 @@ private[archive] class DecodeV1(minor: LV.Minor) {
}
case PLF.Expr.SumCase.TO_REQUIRED_INTERFACE =>
assertSince(LV.Features.extendedInterfaces, "Expr.to_required_interface")
assertSince(LV.Features.basicInterfaces, "Expr.to_required_interface")
val toRequiredInterface = lfExpr.getToRequiredInterface
val requiredIfaceId = decodeTypeConName(toRequiredInterface.getRequiredInterface)
val requiringIfaceId = decodeTypeConName(toRequiredInterface.getRequiringInterface)
@ -1426,7 +1426,7 @@ private[archive] class DecodeV1(minor: LV.Minor) {
}
case PLF.Expr.SumCase.FROM_REQUIRED_INTERFACE =>
assertSince(LV.Features.extendedInterfaces, "Expr.from_required_interface")
assertSince(LV.Features.basicInterfaces, "Expr.from_required_interface")
val fromRequiredInterface = lfExpr.getFromRequiredInterface
decodeExpr(fromRequiredInterface.getExpr, definition) { body =>
Ret(
@ -1439,7 +1439,7 @@ private[archive] class DecodeV1(minor: LV.Minor) {
}
case PLF.Expr.SumCase.UNSAFE_FROM_REQUIRED_INTERFACE =>
assertSince(LV.Features.extendedInterfaces, "Expr.from_required_interface")
assertSince(LV.Features.basicInterfaces, "Expr.from_required_interface")
val unsafeFromRequiredInterface = lfExpr.getUnsafeFromRequiredInterface
val requiredIfaceId = decodeTypeConName(unsafeFromRequiredInterface.getRequiredInterface)
val requiringIfaceId =
@ -1644,7 +1644,7 @@ private[archive] class DecodeV1(minor: LV.Minor) {
decodeExpr(exercise.getArg, definition) { argE =>
bindWork(
if (exercise.hasGuard) {
assertSince(LV.Features.extendedInterfaces, "exerciseInterface.guard")
assertSince(LV.v1_dev, "exerciseInterface.guard")
decodeExpr(exercise.getGuard, definition) { e =>
Ret(Some(e))
}
@ -2348,7 +2348,7 @@ private[archive] object DecodeV1 {
BuiltinFunctionInfo(NUMERIC_TO_BIGNUMERIC, BNumericToBigNumeric, minVersion = bigNumeric),
BuiltinFunctionInfo(BIGNUMERIC_TO_TEXT, BBigNumericToText, minVersion = bigNumeric),
BuiltinFunctionInfo(ANY_EXCEPTION_MESSAGE, BAnyExceptionMessage, minVersion = exceptions),
BuiltinFunctionInfo(TYPEREP_TYCON_NAME, BTypeRepTyConName, minVersion = extendedInterfaces),
BuiltinFunctionInfo(TYPEREP_TYCON_NAME, BTypeRepTyConName, minVersion = LV.v1_dev),
BuiltinFunctionInfo(TEXT_TO_UPPER, BTextToUpper, minVersion = unstable),
BuiltinFunctionInfo(TEXT_TO_LOWER, BTextToLower, minVersion = unstable),
BuiltinFunctionInfo(TEXT_SLICE, BTextSlice, minVersion = unstable),

View File

@ -960,6 +960,31 @@ message Expr {
Expr interface_expr = 4;
}
// Upcast from an interface payload to an interface it requires.
// *Available in versions >= 1.15*
message ToRequiredInterface {
TypeConName required_interface = 1;
TypeConName requiring_interface = 2;
Expr expr = 3;
}
// Downcast from an interface payload to an interface that requires it, if possible.
// *Available in versions >= 1.15*
message FromRequiredInterface {
TypeConName required_interface = 1;
TypeConName requiring_interface = 2;
Expr expr = 3;
}
// Downcast from an interface payload to an interface that requires it, or raises WronglyTypedContract if not possible.
// *Available in versions >= 1.15*
message UnsafeFromRequiredInterface {
TypeConName required_interface = 1;
TypeConName requiring_interface = 2;
Expr contract_id_expr = 3;
Expr interface_expr = 4;
}
// Obtain the type representation of a contract through an interface
// *Available in versions >= 1.15*
message InterfaceTemplateTypeRep {
@ -1109,6 +1134,12 @@ message Expr {
// *Available in versions >= 1.15*
InterfaceTemplateTypeRep interface_template_type_rep = 43;
// Upcast/downcast interface payloads.
// *Available in versions >= 1.15*
ToRequiredInterface to_required_interface = 44;
FromRequiredInterface from_required_interface = 45;
UnsafeFromRequiredInterface unsafe_from_required_interface = 46;
Experimental experimental = 9999; // *Available only in 1.dev*
}
@ -1609,6 +1640,8 @@ message DefInterface {
// View type for this interface
Type view = 8;
repeated TypeConName requires = 9; // *Available in versions >= 1.15*
}
// Exception definition

View File

@ -951,8 +951,11 @@ class DecodeV1Spec
DamlLf1.TypeConName.newBuilder().setModule(modRef).setNameInternedDname(1)
val ifaceTyConName =
DamlLf1.TypeConName.newBuilder().setModule(modRef).setNameInternedDname(2)
val requiredIfaceTyConName =
DamlLf1.TypeConName.newBuilder().setModule(modRef).setNameInternedDname(3)
val scalaTemplateTyConName = Ref.TypeConName.assertFromString("noPkgId:Mod:T")
val scalaIfaceTyConName = Ref.TypeConName.assertFromString("noPkgId:Mod:I")
val scalaRequiredIfaceTyConName = Ref.TypeConName.assertFromString("noPkgId:Mod:J")
val signatoryInterface = DamlLf1.Expr
.newBuilder()
@ -1024,60 +1027,6 @@ class DecodeV1Spec
)
.build()
Table(
"input" -> "expected output",
signatoryInterface -> Ast
.ESignatoryInterface(ifaceId = scalaIfaceTyConName, body = EUnit),
observerInterface -> Ast.EObserverInterface(ifaceId = scalaIfaceTyConName, body = EUnit),
toInterface -> Ast.EToInterface(
interfaceId = scalaIfaceTyConName,
templateId = scalaTemplateTyConName,
value = EUnit,
),
fromInterface -> Ast.EFromInterface(
interfaceId = scalaIfaceTyConName,
templateId = scalaTemplateTyConName,
value = EUnit,
),
interfaceTemplateTypeRep -> Ast.EInterfaceTemplateTypeRep(
ifaceId = scalaIfaceTyConName,
body = EUnit,
),
unsafeFromInterface -> Ast.EUnsafeFromInterface(
interfaceId = scalaIfaceTyConName,
templateId = scalaTemplateTyConName,
contractIdExpr = EUnit,
ifaceExpr = EFalse,
),
)
}
forEveryVersion { version =>
forEvery(testCases) { (proto, scala) =>
val result = Try(interfacePrimitivesDecoder(version).decodeExprForTest(proto, "test"))
if (version < LV.Features.basicInterfaces)
inside(result) { case Failure(error) => error shouldBe a[Error.Parsing] }
else
result shouldBe Success(scala)
}
}
}
// TODO: #14770 Re-enable when lf 15 protobuf is cleaned up and ready
s"decode extended interface primitives iff version < ${LV.Features.extendedInterfaces}" in {
val testCases = {
val unit = DamlLf1.Unit.newBuilder().build()
val pkgRef = DamlLf1.PackageRef.newBuilder().setSelf(unit).build
val modRef =
DamlLf1.ModuleRef.newBuilder().setPackageRef(pkgRef).setModuleNameInternedDname(0).build()
val ifaceTyConName =
DamlLf1.TypeConName.newBuilder().setModule(modRef).setNameInternedDname(2)
val requiredIfaceTyConName =
DamlLf1.TypeConName.newBuilder().setModule(modRef).setNameInternedDname(3)
val scalaIfaceTyConName = Ref.TypeConName.assertFromString("noPkgId:Mod:I")
val scalaRequiredIfaceTyConName = Ref.TypeConName.assertFromString("noPkgId:Mod:J")
val toRequiredInterface = DamlLf1.Expr
.newBuilder()
.setToRequiredInterface(
@ -1113,15 +1062,31 @@ class DecodeV1Spec
)
.build()
val typeRepTyConName = DamlLf1.Expr
.newBuilder()
.setBuiltin(
DamlLf1.BuiltinFunction.TYPEREP_TYCON_NAME
)
.build()
Table(
"input" -> "expected output",
signatoryInterface -> Ast
.ESignatoryInterface(ifaceId = scalaIfaceTyConName, body = EUnit),
observerInterface -> Ast.EObserverInterface(ifaceId = scalaIfaceTyConName, body = EUnit),
toInterface -> Ast.EToInterface(
interfaceId = scalaIfaceTyConName,
templateId = scalaTemplateTyConName,
value = EUnit,
),
fromInterface -> Ast.EFromInterface(
interfaceId = scalaIfaceTyConName,
templateId = scalaTemplateTyConName,
value = EUnit,
),
interfaceTemplateTypeRep -> Ast.EInterfaceTemplateTypeRep(
ifaceId = scalaIfaceTyConName,
body = EUnit,
),
unsafeFromInterface -> Ast.EUnsafeFromInterface(
interfaceId = scalaIfaceTyConName,
templateId = scalaTemplateTyConName,
contractIdExpr = EUnit,
ifaceExpr = EFalse,
),
toRequiredInterface -> Ast.EToRequiredInterface(
requiredIfaceId = scalaRequiredIfaceTyConName,
requiringIfaceId = scalaIfaceTyConName,
@ -1138,6 +1103,31 @@ class DecodeV1Spec
contractIdExpr = EUnit,
ifaceExpr = EFalse,
),
)
}
forEveryVersion { version =>
forEvery(testCases) { (proto, scala) =>
val result = Try(interfacePrimitivesDecoder(version).decodeExprForTest(proto, "test"))
if (version < LV.Features.basicInterfaces)
inside(result) { case Failure(error) => error shouldBe a[Error.Parsing] }
else
result shouldBe Success(scala)
}
}
}
s"decode extended TypeRep iff version < ${LV.v1_dev}" in {
val testCases = {
val typeRepTyConName = DamlLf1.Expr
.newBuilder()
.setBuiltin(
DamlLf1.BuiltinFunction.TYPEREP_TYCON_NAME
)
.build()
Table(
"input" -> "expected output",
typeRepTyConName -> Ast.EBuiltin(Ast.BTypeRepTyConName),
)
}
@ -1145,7 +1135,7 @@ class DecodeV1Spec
forEveryVersion { version =>
forEvery(testCases) { (proto, scala) =>
val result = Try(interfacePrimitivesDecoder(version).decodeExprForTest(proto, "test"))
if (version < LV.Features.extendedInterfaces)
if (version < LV.v1_dev)
inside(result) { case Failure(error) => error shouldBe a[Error.Parsing] }
else
result shouldBe Success(scala)
@ -1213,7 +1203,7 @@ class DecodeV1Spec
}
}
s"translate interface exercise guard iff version >= ${LV.Features.extendedInterfaces}" in {
s"translate interface exercise guard iff version >= ${LV.v1_dev}" in {
val unit = DamlLf1.Unit.newBuilder().build()
val pkgRef = DamlLf1.PackageRef.newBuilder().setSelf(unit).build
@ -1242,7 +1232,7 @@ class DecodeV1Spec
Some(EUnit),
)
forEveryVersionSuchThat(_ >= LV.Features.extendedInterfaces) { version =>
forEveryVersionSuchThat(_ >= LV.v1_dev) { version =>
val decoder =
moduleDecoder(version, ImmArraySeq("Choice"), interfaceDottedNameTable, typeTable)
val proto = DamlLf1.Expr.newBuilder().setUpdate(exerciseInterfaceProto).build()
@ -1382,7 +1372,7 @@ class DecodeV1Spec
}
}
s"accept interface requires iff version >= ${LV.Features.extendedInterfaces}" in {
s"accept interface requires iff version >= ${LV.Features.basicInterfaces}" in {
val interfaceName = Ref.DottedName.assertFromString("I")
@ -1424,7 +1414,7 @@ class DecodeV1Spec
forEveryVersion { version =>
val decoder = interfaceDefDecoder(version)
val result = Try(decoder.decodeDefInterfaceForTest(interfaceName, requiresDefInterface))
if (version >= LV.Features.extendedInterfaces)
if (version >= LV.Features.basicInterfaces)
result shouldBe Success(requiresDefInterfaceScala)
else
inside(result) { case Failure(error) => error shouldBe an[Error.Parsing] }

View File

@ -388,7 +388,7 @@ private[daml] class EncodeV1(minor: LV.Minor) {
b.setCid(cid)
b.setArg(arg)
guard.foreach { g =>
assertSince(LV.Features.extendedInterfaces, "ExerciseInterface.guard")
assertSince(LV.v1_dev, "ExerciseInterface.guard")
b.setGuard(g)
}
builder.setExerciseInterface(b)
@ -683,7 +683,7 @@ private[daml] class EncodeV1(minor: LV.Minor) {
.setInterfaceExpr(value)
)
case EUnsafeFromInterface(iface, tpl, cid, value) =>
assertSince(LV.Features.extendedInterfaces, "Expr.UnsafeFromInterface")
assertSince(LV.Features.basicInterfaces, "Expr.UnsafeFromInterface")
builder.setUnsafeFromInterface(
PLF.Expr.UnsafeFromInterface
.newBuilder()
@ -693,7 +693,7 @@ private[daml] class EncodeV1(minor: LV.Minor) {
.setInterfaceExpr(value)
)
case EToRequiredInterface(superIface, iface, value) =>
assertSince(LV.Features.extendedInterfaces, "Expr.ToRequiredInterface")
assertSince(LV.Features.basicInterfaces, "Expr.ToRequiredInterface")
builder.setToRequiredInterface(
PLF.Expr.ToRequiredInterface
.newBuilder()
@ -702,7 +702,7 @@ private[daml] class EncodeV1(minor: LV.Minor) {
.setExpr(value)
)
case EFromRequiredInterface(superIface, iface, value) =>
assertSince(LV.Features.extendedInterfaces, "Expr.FromRequiredInterface")
assertSince(LV.Features.basicInterfaces, "Expr.FromRequiredInterface")
builder.setFromRequiredInterface(
PLF.Expr.FromRequiredInterface
.newBuilder()
@ -711,7 +711,7 @@ private[daml] class EncodeV1(minor: LV.Minor) {
.setExpr(value)
)
case EUnsafeFromRequiredInterface(superIface, iface, cid, value) =>
assertSince(LV.Features.extendedInterfaces, "Expr.UnsafeFromRequiredInterface")
assertSince(LV.Features.basicInterfaces, "Expr.UnsafeFromRequiredInterface")
builder.setUnsafeFromRequiredInterface(
PLF.Expr.UnsafeFromRequiredInterface
.newBuilder()
@ -807,7 +807,7 @@ private[daml] class EncodeV1(minor: LV.Minor) {
builder.accumulateLeft(interface.choices.sortByKey)(_ addChoices _)
builder.accumulateLeft(interface.methods.sortByKey)(_ addMethods _)
if (interface.requires.nonEmpty) {
assertSince(LV.Features.extendedInterfaces, "DefInterface.requires")
assertSince(LV.Features.basicInterfaces, "DefInterface.requires")
builder.accumulateLeft(interface.requires)(_ addRequires _)
}
builder.accumulateLeft(interface.coImplements.sortByKey)(_ addCoImplements _)

View File

@ -28,15 +28,6 @@ private[lf] object Command {
argument: SValue,
) extends Command
/** Exercise a template choice, by interface */
final case class ExerciseByInterface(
interfaceId: Identifier,
templateId: Identifier,
contractId: SContractId,
choiceId: ChoiceName,
argument: SValue,
) extends Command
/** Exercise an interface choice. This is used for exercising an interface
* on the ledger api, where the template id is unknown.
*/
@ -47,14 +38,6 @@ private[lf] object Command {
argument: SValue,
) extends Command
final case class ExerciseByInheritedInterface(
requiredIface: Identifier,
requiringIface: Identifier,
contractId: SContractId,
choiceId: ChoiceName,
argument: SValue,
) extends Command
final case class ExerciseByKey(
templateId: Identifier,
contractKey: SValue,

View File

@ -937,71 +937,17 @@ private[lf] final class Compiler(
}
}
private[this] def translateExerciseByInterface(
env: Env,
interfaceId: TypeConName,
templateId: TypeConName,
contractId: SValue,
choiceId: ChoiceName,
argument: SValue,
): s.SExpr =
unaryFunction(env) { (tokenPos, env) =>
t.InterfaceChoiceDefRef(interfaceId, choiceId)(
s.SEApp(s.SEBuiltin(SBGuardMatchTemplateId(templateId)), List(s.SEValue(contractId))),
s.SEValue(contractId),
s.SEValue(argument),
env.toSEVar(tokenPos),
)
}
private[this] def translateExerciseByInheritedInterface(
env: Env,
requiredIfaceId: TypeConName,
requiringIfaceId: TypeConName,
contractId: SValue,
choiceId: ChoiceName,
argument: SValue,
): s.SExpr =
unaryFunction(env) { (tokenPos, env) =>
t.InterfaceChoiceDefRef(requiredIfaceId, choiceId)(
s.SEApp(
s.SEBuiltin(SBGuardRequiredInterfaceId(requiredIfaceId, requiringIfaceId)),
List(s.SEValue(contractId)),
),
s.SEValue(contractId),
s.SEValue(argument),
env.toSEVar(tokenPos),
)
}
private[this] def translateCommand(env: Env, cmd: Command): s.SExpr = cmd match {
case Command.Create(templateId, argument) =>
t.CreateDefRef(templateId)(s.SEValue(argument))
case Command.ExerciseTemplate(templateId, contractId, choiceId, argument) =>
t.TemplateChoiceDefRef(templateId, choiceId)(s.SEValue(contractId), s.SEValue(argument))
case Command.ExerciseByInterface(interfaceId, templateId, contractId, choiceId, argument) =>
translateExerciseByInterface(env, interfaceId, templateId, contractId, choiceId, argument)
case Command.ExerciseInterface(interfaceId, contractId, choiceId, argument) =>
t.InterfaceChoiceDefRef(interfaceId, choiceId)(
s.SEBuiltin(SBGuardConstTrue),
s.SEValue(contractId),
s.SEValue(argument),
)
case Command.ExerciseByInheritedInterface(
requiredIfaceId,
requiringIfaceId,
contractId,
choiceId,
argument,
) =>
translateExerciseByInheritedInterface(
env,
requiredIfaceId,
requiringIfaceId,
contractId,
choiceId,
argument,
)
case Command.ExerciseByKey(templateId, contractKey, choiceId, argument) =>
t.ChoiceByKeyDefRef(templateId, choiceId)(s.SEValue(contractKey), s.SEValue(argument))
case Command.FetchTemplate(templateId, coid) =>

View File

@ -55,7 +55,6 @@ object LanguageVersion {
val bigNumeric = v1_13
val exceptions = v1_14
val basicInterfaces = v1_15
val extendedInterfaces = v1_dev
val explicitDisclosure = v1_dev
/** Unstable, experimental features. This should stay in 1.dev forever.