From 4075389f925f14ddb9ff4541d4840e2e887b6227 Mon Sep 17 00:00:00 2001 From: Stephen Compall Date: Wed, 8 Jun 2022 08:37:56 -0400 Subject: [PATCH] remove inherited choices from Scala codegen, test interface/template IDs (#14113) * inherited choices are gone, convert to interface * use toInterface for conformance tests on interfaces CHANGELOG_BEGIN - [Scala codegen] Interface choices can no longer be invoked directly on template IDs, or via ``createAnd`` or ``key`` directly. Instead, use ``toInterface[Ifn]`` before calling the relevant ``exercise*`` method. The resulting ledger commands now contain the correct interface ID rather than template ID for looking up the choice, but see #13993 for a caveat regarding create-and-exercise and exercise-by-key. CHANGELOG_END --- .../src/main/daml/MyMain.daml | 5 ---- .../src/main/daml/MyMainIface.daml | 4 +++ .../codegen/GeneratedCommandsUT.scala | 28 +++++++++++++++++-- .../com/digitalasset/lf/codegen/CodeGen.scala | 2 +- .../testtool/suites/v1_dev/InterfaceIT.scala | 20 +++++++------ 5 files changed, 42 insertions(+), 17 deletions(-) diff --git a/language-support/scala/codegen-sample-app/src/main/daml/MyMain.daml b/language-support/scala/codegen-sample-app/src/main/daml/MyMain.daml index 007fa31f29..6b4bde7f84 100644 --- a/language-support/scala/codegen-sample-app/src/main/daml/MyMain.daml +++ b/language-support/scala/codegen-sample-app/src/main/daml/MyMain.daml @@ -597,11 +597,6 @@ interface InterfaceToMix where choice InheritedOnly: ContractId InterfaceToMix with controller getOwner this do return self -{- TODO(#13926) uncomment when overloaded choices compile - choice OverloadedInTemplate: () with - controller getOwner this - do return () --} template InterfaceMixer with party: Party diff --git a/language-support/scala/codegen-sample-app/src/main/daml/MyMainIface.daml b/language-support/scala/codegen-sample-app/src/main/daml/MyMainIface.daml index 94a215e000..195001c5ef 100644 --- a/language-support/scala/codegen-sample-app/src/main/daml/MyMainIface.daml +++ b/language-support/scala/codegen-sample-app/src/main/daml/MyMainIface.daml @@ -9,3 +9,7 @@ interface IfaceFromAnotherMod where sth: Int controller getOwner this do return $ sth + 1 + choice OverloadedInTemplate: () with + controller getOwner this + do return () + diff --git a/language-support/scala/codegen-sample-app/src/test/scala/com/digitalasset/codegen/GeneratedCommandsUT.scala b/language-support/scala/codegen-sample-app/src/test/scala/com/digitalasset/codegen/GeneratedCommandsUT.scala index b6a0123d31..cdcdc72692 100644 --- a/language-support/scala/codegen-sample-app/src/test/scala/com/digitalasset/codegen/GeneratedCommandsUT.scala +++ b/language-support/scala/codegen-sample-app/src/test/scala/com/digitalasset/codegen/GeneratedCommandsUT.scala @@ -65,6 +65,7 @@ class GeneratedCommandsUT extends AnyWordSpec with Matchers with Inside { val imId: P.ContractId[MyMain.InterfaceMixer] = P.ContractId("fakeimid") val itmId: P.ContractId[MyMainIface.IfaceFromAnotherMod] = P.ContractId("fakeitmid") val DirectTemplateId = ApiTypes.TemplateId unwrap MyMain.InterfaceMixer.id + val ITMTemplateId = ApiTypes.TemplateId unwrap MyMain.InterfaceToMix.id val FAMTemplateId = ApiTypes.TemplateId unwrap MyMainIface.IfaceFromAnotherMod.id "invoke directly-defined choices" in { @@ -82,11 +83,32 @@ class GeneratedCommandsUT extends AnyWordSpec with Matchers with Inside { } } - "invoke interface-inherited choices, directly from template" in { - inside(imId.exerciseInheritedOnly(alice).command.command) { + "invoke interface-defined choices, even when overloaded in template" in { + inside( + imId + .toInterface[MyMainIface.IfaceFromAnotherMod] + .exerciseOverloadedInTemplate(alice) + .command + .command + ) { case rpccmd.Command.Command.Exercise( rpccmd.ExerciseCommand( - Some(DirectTemplateId), // TODO(#13349, #13668) must be interface ID + Some(FAMTemplateId), + cid, + "OverloadedInTemplate", + Some(choiceArg), + ) + ) => + cid should ===(imId) + choiceArg should ===(encode(MyMainIface.OverloadedInTemplate())) + } + } + + "invoke interface-inherited choices by converting to interface" in { + inside(imId.toInterface[MyMain.InterfaceToMix].exerciseInheritedOnly(alice).command.command) { + case rpccmd.Command.Command.Exercise( + rpccmd.ExerciseCommand( + Some(ITMTemplateId), cid, "InheritedOnly", Some(choiceArg), diff --git a/language-support/scala/codegen/src/main/scala/com/digitalasset/lf/codegen/CodeGen.scala b/language-support/scala/codegen/src/main/scala/com/digitalasset/lf/codegen/CodeGen.scala index 8bf91bd484..c5606b8ab6 100644 --- a/language-support/scala/codegen/src/main/scala/com/digitalasset/lf/codegen/CodeGen.scala +++ b/language-support/scala/codegen/src/main/scala/com/digitalasset/lf/codegen/CodeGen.scala @@ -74,7 +74,7 @@ object CodeGen { roots: Seq[String], ): ValidationNel[String, Unit] = decodeInterfaces(files).map { interfaces: NonEmptyList[EnvironmentInterface] => - val combined = interfaces.suml1.resolveChoices + val combined = interfaces.suml1 val interface = combined.copy( typeDecls = Util.filterTemplatesBy(roots.map(_.r))(combined.typeDecls) ) diff --git a/ledger/ledger-api-tests/suites/src/main/scala/com/daml/ledger/api/testtool/suites/v1_dev/InterfaceIT.scala b/ledger/ledger-api-tests/suites/src/main/scala/com/daml/ledger/api/testtool/suites/v1_dev/InterfaceIT.scala index 9505842e48..531b3613cb 100644 --- a/ledger/ledger-api-tests/suites/src/main/scala/com/daml/ledger/api/testtool/suites/v1_dev/InterfaceIT.scala +++ b/ledger/ledger-api-tests/suites/src/main/scala/com/daml/ledger/api/testtool/suites/v1_dev/InterfaceIT.scala @@ -16,16 +16,20 @@ import com.daml.ledger.api.testtool.infrastructure.TransactionHelpers._ import com.daml.ledger.api.v1.value.{Identifier, Value} import com.daml.ledger.client.binding.Primitive import com.daml.ledger.test.semantic.Interface._ +import com.daml.ledger.test.semantic.{Interface1, Interface2} import scalaz.Tag class InterfaceIT extends LedgerTestSuite { private[this] val TId = Tag.unwrap(T.id) - private[this] val I1Id = Tag.unwrap(T.id).copy(moduleName = "Interface1", entityName = "I") - private[this] val I2Id = Tag.unwrap(T.id).copy(moduleName = "Interface2", entityName = "I") + private[this] val I1Id = Tag.unwrap(Interface1.I.id) + private[this] val I2Id = Tag.unwrap(Interface2.I.id) - // Workaround improper support of scala Codegen TODO(#13349) - private[this] def fixId[X](command: Primitive.Update[X], id: Identifier): Primitive.Update[X] = { + // replace identifier with the wrong identifier for some of these tests + private[this] def useWrongId[X]( + command: Primitive.Update[X], + id: Identifier, + ): Primitive.Update[X] = { val exe = command.command.getExercise val arg = exe.getChoiceArgument.getRecord command @@ -47,7 +51,7 @@ class InterfaceIT extends LedgerTestSuite { )(implicit ec => { case Participants(Participant(ledger, party)) => for { t <- ledger.create(party, T(party)) - tree <- ledger.exercise(party, x => fixId(t.exerciseMyArchive(x), TId)) + tree <- ledger.exercise(party, x => t.exerciseMyArchive(x)) } yield { val events = exercisedEvents(tree) assertLength(s"1 successful exercise", 1, events) @@ -63,7 +67,7 @@ class InterfaceIT extends LedgerTestSuite { )(implicit ec => { case Participants(Participant(ledger, party)) => for { t <- ledger.create(party, T(party)) - tree <- ledger.exercise(party, x => fixId(t.exerciseMyArchive(x), I1Id)) + tree <- ledger.exercise(party, x => t.toInterface[Interface1.I].exerciseMyArchive(x)) } yield { val events = exercisedEvents(tree) assertLength(s"1 successful exercise", 1, events) @@ -80,7 +84,7 @@ class InterfaceIT extends LedgerTestSuite { for { t <- ledger.create(party, T(party)) failure <- ledger - .exercise(party, x => fixId(t.exerciseChoiceI1(x), TId)) + .exercise(party, x => useWrongId(t.toInterface[Interface1.I].exerciseChoiceI1(x), TId)) .mustFail("unknown choice") } yield { assertGrpcError( @@ -100,7 +104,7 @@ class InterfaceIT extends LedgerTestSuite { for { t <- ledger.create(party, T(party)) failure <- ledger - .exercise(party, x => fixId(t.exerciseChoiceI1(x), I2Id)) + .exercise(party, x => useWrongId(t.toInterface[Interface1.I].exerciseChoiceI1(x), I2Id)) .mustFail("unknown choice") } yield { assertGrpcError(