diff --git a/daml-lf/archive/src/main/scala/com/digitalasset/daml/lf/archive/DecodeV1.scala b/daml-lf/archive/src/main/scala/com/digitalasset/daml/lf/archive/DecodeV1.scala index 4e82533abf..37fa178eda 100644 --- a/daml-lf/archive/src/main/scala/com/digitalasset/daml/lf/archive/DecodeV1.scala +++ b/daml-lf/archive/src/main/scala/com/digitalasset/daml/lf/archive/DecodeV1.scala @@ -658,7 +658,7 @@ private[archive] class DecodeV1(minor: LV.Minor) { ): DefException = DefException(decodeExpr(lfException.getMessage, s"$exceptionName:message")) - private[this] def decodeDefInterface( + private[lf] def decodeDefInterface( id: DottedName, lfInterface: PLF.DefInterface, ): DefInterface = @@ -668,6 +668,7 @@ private[archive] class DecodeV1(minor: LV.Minor) { choices = lfInterface.getChoicesList.asScala.view.map(decodeChoice(id, _)), methods = lfInterface.getMethodsList.asScala.view.map(decodeInterfaceMethod), precond = decodeExpr(lfInterface.getPrecond, s"$id:ensure"), + coImplements = lfInterface.getCoImplementsList.asScala.view.map(decodeInterfaceCoImplements), ) private[this] def decodeInterfaceMethod( @@ -678,6 +679,23 @@ private[archive] class DecodeV1(minor: LV.Minor) { returnType = decodeType(lfMethod.getType), ) + private[this] def decodeInterfaceCoImplements( + lfCoImpl: PLF.DefInterface.CoImplements + ): InterfaceCoImplements = + InterfaceCoImplements.build( + templateId = decodeTypeConName(lfCoImpl.getTemplate), + methods = lfCoImpl.getMethodsList.asScala.view.map(decodeInterfaceCoImplementsMethod), + ) + + private[this] def decodeInterfaceCoImplementsMethod( + lfMethod: PLF.DefInterface.CoImplementsMethod + ): InterfaceCoImplementsMethod = + InterfaceCoImplementsMethod( + methodName = + getInternedName(lfMethod.getMethodInternedName, "InterfaceCoImplementsMethod.name"), + value = decodeExpr(lfMethod.getValue, "InterfaceCoImplementsMethod.value"), + ) + private[lf] def decodeKind(lfKind: PLF.Kind): Kind = lfKind.getSumCase match { case PLF.Kind.SumCase.STAR => KStar diff --git a/daml-lf/archive/src/test/scala/com/digitalasset/daml/lf/archive/DecodeV1Spec.scala b/daml-lf/archive/src/test/scala/com/digitalasset/daml/lf/archive/DecodeV1Spec.scala index 0c161710e4..ab439e0eb5 100644 --- a/daml-lf/archive/src/test/scala/com/digitalasset/daml/lf/archive/DecodeV1Spec.scala +++ b/daml-lf/archive/src/test/scala/com/digitalasset/daml/lf/archive/DecodeV1Spec.scala @@ -25,6 +25,7 @@ import com.daml.lf.language.Ast.{ EUnsafeFromRequiredInterface, FeatureFlags, GenDefInterface, + InterfaceMethod, GenModule, GenTemplate, GenTemplateImplements, @@ -52,9 +53,19 @@ class DecodeV1Spec .newBuilder() .setPrim(DamlLf1.Type.Prim.newBuilder().setPrim(DamlLf1.PrimType.UNIT)) .build() + val boolTyp: DamlLf1.Type = DamlLf1.Type + .newBuilder() + .setPrim(DamlLf1.Type.Prim.newBuilder().setPrim(DamlLf1.PrimType.BOOL)) + .build() + val textTyp: DamlLf1.Type = DamlLf1.Type + .newBuilder() + .setPrim(DamlLf1.Type.Prim.newBuilder().setPrim(DamlLf1.PrimType.TEXT)) + .build() - val typeTable = ImmArraySeq(TUnit) + val typeTable = ImmArraySeq(TUnit, TBool, TText) val unitTypInterned = DamlLf1.Type.newBuilder().setInterned(0).build() + val boolTypInterned = DamlLf1.Type.newBuilder().setInterned(1).build() + val textTypInterned = DamlLf1.Type.newBuilder().setInterned(2).build() val unitExpr: DamlLf1.Expr = DamlLf1.Expr .newBuilder() @@ -1050,6 +1061,7 @@ class DecodeV1Spec Map(), Map(), EPrimCon(PCUnit), + Map(), ) ), FeatureFlags(), @@ -1072,6 +1084,137 @@ class DecodeV1Spec } } + s"Decode interface definitions correctly iff version >= ${LV.Features.interfaces}" in { + + val unit = DamlLf1.Unit.newBuilder() + val pkgRef = DamlLf1.PackageRef.newBuilder().setSelf(unit) + val modRef = + DamlLf1.ModuleRef.newBuilder().setPackageRef(pkgRef).setModuleNameInternedDname(0) + + val emptyDefInterface = DamlLf1.DefInterface + .newBuilder() + .setTyconInternedDname(1) + .setParamInternedStr(0) + .setPrecond(unitExpr) + .build() + + val emptyDefInterfaceScala = + GenDefInterface( + Set.empty, + Ref.IdString.Name.assertFromString("this"), + Map(), + Map(), + EPrimCon(PCUnit), + Map(), + ) + + val requiresDefInterface = { + val typeConNameJ = + DamlLf1.TypeConName.newBuilder().setModule(modRef).setNameInternedDname(3) + val typeConNameK = + DamlLf1.TypeConName.newBuilder().setModule(modRef).setNameInternedDname(4) + + DamlLf1.DefInterface + .newBuilder() + .setTyconInternedDname(1) + .setParamInternedStr(0) + .setPrecond(unitExpr) + .addRequires(typeConNameJ) + .addRequires(typeConNameK) + .build() + } + + val requiresDefInterfaceScala = + GenDefInterface( + Set( + Ref.TypeConName.assertFromString("noPkgId:Mod:J"), + Ref.TypeConName.assertFromString("noPkgId:Mod:K"), + ), + Ref.IdString.Name.assertFromString("this"), + Map(), + Map(), + EPrimCon(PCUnit), + Map(), + ) + + val methodsDefInterface = { + val interfaceMethod1 = + DamlLf1.InterfaceMethod.newBuilder().setMethodInternedName(1).setType(textTypInterned) + val interfaceMethod2 = + DamlLf1.InterfaceMethod.newBuilder().setMethodInternedName(2).setType(boolTypInterned) + + DamlLf1.DefInterface + .newBuilder() + .setTyconInternedDname(1) + .setParamInternedStr(0) + .setPrecond(unitExpr) + .addMethods(interfaceMethod1) + .addMethods(interfaceMethod2) + .build() + } + + val methodsDefInterfaceScala = { + val methodName1 = Ref.MethodName.assertFromString("method1") + val methodName2 = Ref.MethodName.assertFromString("method2") + + GenDefInterface( + Set.empty, + Ref.IdString.Name.assertFromString("this"), + Map(), + Map( + methodName1 -> InterfaceMethod(methodName1, TText), + methodName2 -> InterfaceMethod(methodName2, TBool), + ), + EPrimCon(PCUnit), + Map(), + ) + } + + val coImplementsDefInterface = DamlLf1.DefInterface + .newBuilder() + .setTyconInternedDname(1) + .setParamInternedStr(0) + .setPrecond(unitExpr) + .build() + + val coImplementsDefInterfaceScala = + GenDefInterface( + Set.empty, + Ref.IdString.Name.assertFromString("this"), + Map(), + Map(), + EPrimCon(PCUnit), + Map(), + ) + + val interfaceDefTestCases = { + Table( + "input" -> "expected output", + emptyDefInterface -> emptyDefInterfaceScala, + requiresDefInterface -> requiresDefInterfaceScala, + methodsDefInterface -> methodsDefInterfaceScala, + coImplementsDefInterface -> coImplementsDefInterfaceScala, + ) + } + + val interfaceName = Ref.DottedName.assertFromString("I") + + val interfaceDefStringTable = ImmArraySeq("this", "method1", "method2") + + val interfaceDefDottedNameTable = + ImmArraySeq("Mod", "T", "I", "J", "K").map(Ref.DottedName.assertFromString) + + val interfaceDefDecoder = + (version: LV) => + moduleDecoder(version, interfaceDefStringTable, interfaceDefDottedNameTable, typeTable) + + forEveryVersionSuchThat(_ >= LV.Features.interfaces) { version => + forEvery(interfaceDefTestCases) { (proto, scala) => + interfaceDefDecoder(version).decodeDefInterface(interfaceName, proto) shouldBe scala + } + } + } + val interfacePrimitivesDottedNameTable = ImmArraySeq("Mod", "T", "I", "J").map(Ref.DottedName.assertFromString) diff --git a/daml-lf/encoder/src/main/scala/com/digitalasset/daml/lf/archive/testing/EncodeV1.scala b/daml-lf/encoder/src/main/scala/com/digitalasset/daml/lf/archive/testing/EncodeV1.scala index 5bedcf5c83..e81f85d03b 100644 --- a/daml-lf/encoder/src/main/scala/com/digitalasset/daml/lf/archive/testing/EncodeV1.scala +++ b/daml-lf/encoder/src/main/scala/com/digitalasset/daml/lf/archive/testing/EncodeV1.scala @@ -802,6 +802,7 @@ private[daml] class EncodeV1(minor: LV.Minor) { builder.accumulateLeft(interface.methods.sortByKey)(_ addMethods _) builder.accumulateLeft(interface.requires)(_ addRequires _) builder.setPrecond(interface.precond) + builder.accumulateLeft(interface.coImplements.sortByKey)(_ addCoImplements _) builder.build() } @@ -815,6 +816,26 @@ private[daml] class EncodeV1(minor: LV.Minor) { b.build() } + private implicit def encodeInterfaceCoImplements( + templateWithCoImplements: (TypeConName, InterfaceCoImplements) + ): PLF.DefInterface.CoImplements = { + val (template, coImplements) = templateWithCoImplements + val b = PLF.DefInterface.CoImplements.newBuilder() + b.setTemplate(template) + b.accumulateLeft(coImplements.methods.sortByKey)(_ addMethods _) + b.build() + } + + private implicit def encodeInterfaceCoImplementsMethod( + nameWithMethod: (MethodName, InterfaceCoImplementsMethod) + ): PLF.DefInterface.CoImplementsMethod = { + val (name, method) = nameWithMethod + val b = PLF.DefInterface.CoImplementsMethod.newBuilder() + b.setMethodInternedName(stringsTable.insert(name)) + b.setValue(method.value) + b.build() + } + private implicit def encodeSynonymDef(nameWithDef: (DottedName, DTypeSyn)): PLF.DefTypeSyn = { val (dottedName, typeSyn) = nameWithDef val builder = PLF.DefTypeSyn.newBuilder() diff --git a/daml-lf/encoder/src/test/lf/Interface_1.dev_.lf b/daml-lf/encoder/src/test/lf/Interface_1.dev_.lf index 080cbd2bfe..4753120328 100644 --- a/daml-lf/encoder/src/test/lf/Interface_1.dev_.lf +++ b/daml-lf/encoder/src/test/lf/Interface_1.dev_.lf @@ -15,6 +15,9 @@ module InterfaceMod { , controllers Cons @Party [call_method @InterfaceMod:Boxy getParty this] (Nil @Party) , observers Nil @Party to upure @Int64 i; + coimplements Other:Package { + method getParty = Other:Package {party} this; + }; }; record @serializable Box = { party: Party, name: Text }; diff --git a/daml-lf/language/src/main/scala/com/digitalasset/daml/lf/language/Ast.scala b/daml-lf/language/src/main/scala/com/digitalasset/daml/lf/language/Ast.scala index c3d0129753..3ff05f202a 100644 --- a/daml-lf/language/src/main/scala/com/digitalasset/daml/lf/language/Ast.scala +++ b/daml-lf/language/src/main/scala/com/digitalasset/daml/lf/language/Ast.scala @@ -702,6 +702,9 @@ object Ast { choices: Map[ChoiceName, GenTemplateChoice[E]], methods: Map[MethodName, InterfaceMethod], precond: E, // Interface creation precondition. + coImplements: Map[TypeConName, GenInterfaceCoImplements[ + E + ]], ) final class GenDefInterfaceCompanion[E] { @@ -712,6 +715,7 @@ object Ast { choices: Iterable[GenTemplateChoice[E]], methods: Iterable[InterfaceMethod], precond: E, + coImplements: Iterable[GenInterfaceCoImplements[E]], ): GenDefInterface[E] = { val requiresSet = toSetWithoutDuplicate( requires, @@ -725,7 +729,12 @@ object Ast { methods.view.map(c => c.name -> c), (name: MethodName) => PackageError(s"collision on interface method name $name"), ) - GenDefInterface(requiresSet, param, choiceMap, methodMap, precond) + val coImplementsMap = toMapWithoutDuplicate( + coImplements.view.map(c => c.templateId -> c), + (templateId: TypeConName) => + PackageError(s"repeated interface co-implementation ${templateId.toString}"), + ) + GenDefInterface(requiresSet, param, choiceMap, methodMap, precond, coImplementsMap) } def apply( @@ -734,8 +743,9 @@ object Ast { choices: Map[ChoiceName, GenTemplateChoice[E]], methods: Map[MethodName, InterfaceMethod], precond: E, + coImplements: Map[TypeConName, GenInterfaceCoImplements[E]], ): GenDefInterface[E] = - GenDefInterface(requires, param, choices, methods, precond) + GenDefInterface(requires, param, choices, methods, precond, coImplements) def unapply(arg: GenDefInterface[E]): Some[ ( @@ -744,9 +754,10 @@ object Ast { Map[ChoiceName, GenTemplateChoice[E]], Map[MethodName, InterfaceMethod], E, + Map[TypeConName, GenInterfaceCoImplements[E]], ) ] = - Some((arg.requires, arg.param, arg.choices, arg.methods, arg.precond)) + Some((arg.requires, arg.param, arg.choices, arg.methods, arg.precond, arg.coImplements)) } type DefInterface = GenDefInterface[Expr] @@ -760,6 +771,64 @@ object Ast { returnType: Type, ) + final case class GenInterfaceCoImplements[E]( + templateId: TypeConName, + methods: Map[MethodName, GenInterfaceCoImplementsMethod[E]], + ) + + final class GenInterfaceCoImplementsCompanion[E] private[Ast] { + @throws[PackageError] + def build( + templateId: TypeConName, + methods: Iterable[GenInterfaceCoImplementsMethod[E]], + ): GenInterfaceCoImplements[E] = + new GenInterfaceCoImplements[E]( + templateId = templateId, + methods = toMapWithoutDuplicate( + methods.map(m => m.name -> m), + (name: MethodName) => PackageError(s"repeated method co-implementation $name"), + ), + ) + + def apply( + templateId: TypeConName, + methods: Map[MethodName, GenInterfaceCoImplementsMethod[E]], + ): GenInterfaceCoImplements[E] = + GenInterfaceCoImplements[E](templateId, methods) + + def unapply( + arg: GenInterfaceCoImplements[E] + ): Some[(TypeConName, Map[MethodName, GenInterfaceCoImplementsMethod[E]])] = + Some((arg.templateId, arg.methods)) + } + + type InterfaceCoImplements = GenInterfaceCoImplements[Expr] + val InterfaceCoImplements = new GenInterfaceCoImplementsCompanion[Expr] + + type InterfaceCoImplementsSignature = GenInterfaceCoImplements[Unit] + val InterfaceCoImplementsSignature = new GenInterfaceCoImplementsCompanion[Unit] + + final case class GenInterfaceCoImplementsMethod[E]( + name: MethodName, + value: E, + ) + + final class GenInterfaceCoImplementsMethodCompanion[E] { + def apply(methodName: MethodName, value: E): GenInterfaceCoImplementsMethod[E] = + GenInterfaceCoImplementsMethod[E](methodName, value) + + def unapply( + arg: GenInterfaceCoImplementsMethod[E] + ): Some[(MethodName, E)] = + Some((arg.name, arg.value)) + } + + type InterfaceCoImplementsMethod = GenInterfaceCoImplementsMethod[Expr] + val InterfaceCoImplementsMethod = new GenInterfaceCoImplementsMethodCompanion[Expr] + + type InterfaceCoImplementsMethodSignature = GenInterfaceCoImplementsMethod[Unit] + val InterfaceCoImplementsMethodSignature = new GenInterfaceCoImplementsMethodCompanion[Unit] + final case class GenTemplate[E]( param: ExprVarName, // Binder for template argument. precond: E, // Template creation precondition. diff --git a/daml-lf/language/src/main/scala/com/digitalasset/daml/lf/language/Util.scala b/daml-lf/language/src/main/scala/com/digitalasset/daml/lf/language/Util.scala index a8296819fa..9082106b21 100644 --- a/daml-lf/language/src/main/scala/com/digitalasset/daml/lf/language/Util.scala +++ b/daml-lf/language/src/main/scala/com/digitalasset/daml/lf/language/Util.scala @@ -244,15 +244,35 @@ object Util { ) } + private[this] def toSignature( + coImplementsMethod: InterfaceCoImplementsMethod + ): InterfaceCoImplementsMethodSignature = + coImplementsMethod match { + case InterfaceCoImplementsMethod(name, _) => + InterfaceCoImplementsMethodSignature(name, ()) + } + + private[this] def toSignature( + coImplements: InterfaceCoImplements + ): InterfaceCoImplementsSignature = + coImplements match { + case InterfaceCoImplements(name, methods) => + InterfaceCoImplementsSignature( + name, + methods.transform((_, v) => toSignature(v)), + ) + } + private def toSignature(interface: DefInterface): DefInterfaceSignature = interface match { - case DefInterface(requires, param, choices, methods, _) => + case DefInterface(requires, param, choices, methods, _, coImplements) => DefInterfaceSignature( requires, param, choices.transform((_, choice) => toSignature(choice)), methods, (), + coImplements.transform((_, v) => toSignature(v)), ) } diff --git a/daml-lf/language/src/test/scala/com/digitalasset/daml/lf/language/AstSpec.scala b/daml-lf/language/src/test/scala/com/digitalasset/daml/lf/language/AstSpec.scala index bdc21a0976..62f95ecf96 100644 --- a/daml-lf/language/src/test/scala/com/digitalasset/daml/lf/language/AstSpec.scala +++ b/daml-lf/language/src/test/scala/com/digitalasset/daml/lf/language/AstSpec.scala @@ -67,6 +67,7 @@ class AstSpec extends AnyWordSpec with TableDrivenPropertyChecks with Matchers { choices = Map.empty, methods = Map.empty, requires = Set.empty, + coImplements = Map.empty, ) def exception = DefException( @@ -381,6 +382,7 @@ class AstSpec extends AnyWordSpec with TableDrivenPropertyChecks with Matchers { ), methods = List(ifaceMethod1, ifaceMethod2), precond = ETrue, + coImplements = List.empty, ) } @@ -396,6 +398,7 @@ class AstSpec extends AnyWordSpec with TableDrivenPropertyChecks with Matchers { ), methods = List.empty, precond = ETrue, + coImplements = List.empty, ) ) } @@ -408,6 +411,29 @@ class AstSpec extends AnyWordSpec with TableDrivenPropertyChecks with Matchers { choices = List.empty, methods = List(ifaceMethod1, ifaceMethod1), precond = ETrue, + coImplements = List.empty, + ) + ) + } + + "catch duplicate co-implementation" in { + DefInterface.build( + requires = List.empty, + param = Name.assertFromString("x"), + choices = List.empty, + methods = List(ifaceMethod1, ifaceMethod2), + precond = ETrue, + coImplements = List(ifaceCoImpl1, ifaceCoImpl2), + ) + + a[PackageError] shouldBe thrownBy( + DefInterface.build( + requires = List.empty, + param = Name.assertFromString("x"), + choices = List.empty, + methods = List(ifaceMethod1, ifaceMethod2), + precond = ETrue, + coImplements = List(ifaceCoImpl1, ifaceCoImpl1), ) ) } @@ -426,6 +452,14 @@ class AstSpec extends AnyWordSpec with TableDrivenPropertyChecks with Matchers { interfaceId = TypeConName.assertFromString("pkgId:Mod:I2"), methods = Map.empty, ) + private val ifaceCoImpl1 = InterfaceCoImplements( + templateId = TypeConName.assertFromString("pkgId:Mod:T1"), + methods = Map.empty, + ) + private val ifaceCoImpl2 = InterfaceCoImplements( + templateId = TypeConName.assertFromString("pkgId:Mod:T2"), + methods = Map.empty, + ) private val ifaceMethod1 = InterfaceMethod(name = Name.assertFromString("x"), returnType = TUnit) private val ifaceMethod2 = InterfaceMethod(name = Name.assertFromString("y"), returnType = TUnit) private def choiceBuilder(name: ChoiceName, typ: Type, expr: Expr) = TemplateChoice( diff --git a/daml-lf/parser/src/main/scala/com/digitalasset/daml/lf/testing/parser/AstRewriter.scala b/daml-lf/parser/src/main/scala/com/digitalasset/daml/lf/testing/parser/AstRewriter.scala index 2afce76840..4d1f30fe09 100644 --- a/daml-lf/parser/src/main/scala/com/digitalasset/daml/lf/testing/parser/AstRewriter.scala +++ b/daml-lf/parser/src/main/scala/com/digitalasset/daml/lf/testing/parser/AstRewriter.scala @@ -274,7 +274,7 @@ private[daml] class AstRewriter( }, apply(observers), key.map(apply), - implements.map({ case (t, x) => (apply(t), apply(x)) }), + implements.map { case (t, x) => (apply(t), apply(x)) }, ) } @@ -342,15 +342,39 @@ private[daml] class AstRewriter( InterfaceMethod(name, apply(returnType)) } + def apply(x: InterfaceCoImplements): InterfaceCoImplements = + x match { + case InterfaceCoImplements( + templateId, + methods, + ) => + InterfaceCoImplements( + apply(templateId), + methods.transform((_, x) => apply(x)), + ) + } + def apply(x: InterfaceCoImplementsMethod): InterfaceCoImplementsMethod = + x match { + case InterfaceCoImplementsMethod( + name, + value, + ) => + InterfaceCoImplementsMethod( + name, + apply(value), + ) + } + def apply(x: DefInterface): DefInterface = x match { - case DefInterface(requires, param, choices, methods, precond) => + case DefInterface(requires, param, choices, methods, precond, coImplements) => DefInterface( requires.map(apply(_)), param, choices.transform((_, v) => apply(v)), methods.transform((_, v) => apply(v)), apply(precond), + coImplements.map { case (t, x) => (apply(t), apply(x)) }, ) } } diff --git a/daml-lf/parser/src/main/scala/com/digitalasset/daml/lf/testing/parser/ModParser.scala b/daml-lf/parser/src/main/scala/com/digitalasset/daml/lf/testing/parser/ModParser.scala index 07d9be632c..9f0321668b 100644 --- a/daml-lf/parser/src/main/scala/com/digitalasset/daml/lf/testing/parser/ModParser.scala +++ b/daml-lf/parser/src/main/scala/com/digitalasset/daml/lf/testing/parser/ModParser.scala @@ -201,16 +201,18 @@ private[parser] class ModParser[P](parameters: ParserParameters[P]) { rep(interfaceRequires <~ `;`) ~ (Id("precondition") ~> expr <~ `;`) ~ rep(interfaceMethod <~ `;`) ~ - rep(templateChoice <~ `;`) <~ + rep(templateChoice <~ `;`) ~ + rep(coImplements <~ `;`) <~ `}` ^^ { case x ~ _ ~ tycon ~ _ ~ _ ~ _ ~ requires ~ precond ~ methods ~ - choices => + choices ~ + coImplements => IfaceDef( tycon, - DefInterface.build(Set.from(requires), x, choices, methods, precond), + DefInterface.build(Set.from(requires), x, choices, methods, precond, coImplements), ) } private val interfaceRequires: Parser[Ref.TypeConName] = @@ -221,6 +223,17 @@ private[parser] class ModParser[P](parameters: ParserParameters[P]) { InterfaceMethod(name, typ) } + private lazy val coImplementsMethod: Parser[InterfaceCoImplementsMethod] = + Id("method") ~>! id ~ `=` ~ expr ^^ { case (name ~ _ ~ value) => + InterfaceCoImplementsMethod(name, value) + } + + private lazy val coImplements: Parser[InterfaceCoImplements] = + Id("coimplements") ~>! fullIdentifier ~ (`{` ~> rep(coImplementsMethod <~ `;`) <~ `}`) ^^ { + case tplId ~ methods => + InterfaceCoImplements.build(tplId, methods) + } + private val serializableTag = Ref.Name.assertFromString("serializable") private val isTestTag = Ref.Name.assertFromString("isTest") private val nonConsumingTag = Ref.Name.assertFromString("nonConsuming") diff --git a/daml-lf/parser/src/test/scala/com/digitalasset/daml/lf/testing/parser/ParsersSpec.scala b/daml-lf/parser/src/test/scala/com/digitalasset/daml/lf/testing/parser/ParsersSpec.scala index ed31ce2893..a66df9bb72 100644 --- a/daml-lf/parser/src/test/scala/com/digitalasset/daml/lf/testing/parser/ParsersSpec.scala +++ b/daml-lf/parser/src/test/scala/com/digitalasset/daml/lf/testing/parser/ParsersSpec.scala @@ -795,10 +795,15 @@ class ParsersSpec extends AnyWordSpec with ScalaCheckPropertyChecks with Matcher , controllers Cons @Party [call_method @Mod:Person asParty this] (Nil @Party) , observers Nil @Party to upure @Int64 i; + coimplements Mod1:Company { + method asParty = Mod1:Company {party} this; + method getName = Mod1:Company {legalName} this; + }; } ; } """ + val TTyCon(company) = t"Mod1:Company" val interface = DefInterface( @@ -831,6 +836,22 @@ class ParsersSpec extends AnyWordSpec with ScalaCheckPropertyChecks with Matcher update = e"upure @Int64 i", ), ), + coImplements = Map( + company -> + InterfaceCoImplements( + company, + Map( + n"asParty" -> InterfaceCoImplementsMethod( + n"asParty", + e"Mod1:Company {party} this", + ), + n"getName" -> InterfaceCoImplementsMethod( + n"getName", + e"Mod1:Company {legalName} this", + ), + ), + ) + ), ) val person = DottedName.assertFromString("Person") diff --git a/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/Typing.scala b/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/Typing.scala index 0f85b402e5..6f5caff278 100644 --- a/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/Typing.scala +++ b/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/Typing.scala @@ -14,6 +14,8 @@ import com.daml.scalautil.Statement.discard import scala.annotation.tailrec +import annotation.nowarn + private[validation] object Typing { import Util.handleLookup @@ -458,7 +460,7 @@ private[validation] object Typing { private[Typing] def checkDefIface(ifaceName: TypeConName, iface: DefInterface): Unit = iface match { - case DefInterface(requires, param, choices, methods, precond) => + case DefInterface(requires, param, choices, methods, precond, coImplements) => val env = introExprVar(param, TTyCon(ifaceName)) if (requires(ifaceName)) throw ECircularInterfaceRequires(ctx, ifaceName) @@ -470,6 +472,7 @@ private[validation] object Typing { env.checkExpr(precond, TBool) methods.values.foreach(checkIfaceMethod) choices.values.foreach(env.checkChoice(ifaceName, _)) + env.checkIfaceCoImplementations(ifaceName, coImplements) } private def checkIfaceMethod(method: InterfaceMethod): Unit = { @@ -486,7 +489,7 @@ private[validation] object Typing { ): Unit = { impls.foreach { case (iface, impl) => - val DefInterfaceSignature(requires, _, _, methods, _) = + val DefInterfaceSignature(requires, _, _, methods, _, _) = handleLookup(ctx, interface.lookupInterface(impl.interfaceId)) requires @@ -508,6 +511,14 @@ private[validation] object Typing { } } + // TODO (MA): https://github.com/digital-asset/daml/issues/14047 + @nowarn("cat=unused&msg=parameter value ifaceTcon in method") + @nowarn("cat=unused&msg=parameter value coImpls in method") + private def checkIfaceCoImplementations( + ifaceTcon: TypeConName, + coImpls: Map[TypeConName, InterfaceCoImplements], + ): Unit = {} + private[Typing] def checkDefException( excepName: TypeConName, defException: DefException, diff --git a/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/iterable/ExprIterable.scala b/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/iterable/ExprIterable.scala index 4f65964b4d..c3f92fc43f 100644 --- a/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/iterable/ExprIterable.scala +++ b/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/iterable/ExprIterable.scala @@ -220,8 +220,25 @@ private[validation] object ExprIterable { choices, methods @ _, precond, + coImplements, ) => - Iterator(precond) ++ choices.values.iterator.flatMap(iterator(_)) + Iterator(precond) ++ choices.values.iterator.flatMap(iterator(_)) ++ + coImplements.values.iterator.flatMap(iterator(_)) + } + + private[iterable] def iterator(x: InterfaceCoImplements): Iterator[Expr] = + x match { + case InterfaceCoImplements( + template @ _, + methods, + ) => + methods.values.iterator.flatMap(iterator(_)) + } + + private[iterable] def iterator(x: InterfaceCoImplementsMethod): Iterator[Expr] = + x match { + case InterfaceCoImplementsMethod(name @ _, value) => + Iterator(value) } def apply(expr: Expr): Iterable[Expr] = diff --git a/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/iterable/TypeIterable.scala b/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/iterable/TypeIterable.scala index 40e4f67768..1057f69bb0 100644 --- a/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/iterable/TypeIterable.scala +++ b/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/iterable/TypeIterable.scala @@ -255,13 +255,27 @@ private[validation] object TypeIterable { iterator(value) } + private[validation] def iterator(coImpl: InterfaceCoImplements): Iterator[Type] = + coImpl match { + case InterfaceCoImplements(template, methods) => + Iterator(TTyCon(template)) ++ + methods.values.flatMap(iterator) + } + + private[validation] def iterator(method: InterfaceCoImplementsMethod): Iterator[Type] = + method match { + case InterfaceCoImplementsMethod(name @ _, value) => + iterator(value) + } + private[validation] def iterator(interface: DefInterface): Iterator[Type] = interface match { - case DefInterface(requires, _, choices, methods, precond) => + case DefInterface(requires, _, choices, methods, precond, coImplements) => requires.iterator.map(TTyCon) ++ iterator(precond) ++ choices.values.iterator.flatMap(iterator) ++ - methods.values.iterator.flatMap(iterator) + methods.values.iterator.flatMap(iterator) ++ + coImplements.values.flatMap(iterator) } private[validation] def iterator(imethod: InterfaceMethod): Iterator[Type] =