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
This commit is contained in:
Stephen Compall 2022-06-08 08:37:56 -04:00 committed by GitHub
parent 1d8f195438
commit 4075389f92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 42 additions and 17 deletions

View File

@ -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

View File

@ -9,3 +9,7 @@ interface IfaceFromAnotherMod where
sth: Int
controller getOwner this
do return $ sth + 1
choice OverloadedInTemplate: () with
controller getOwner this
do return ()

View File

@ -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),

View File

@ -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)
)

View File

@ -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(