`createCmd <interface value>` crashes at runtime
In SDK 2.4.2, createCmd : Template t => t -> Commands (ContractId t) only accepts templates because the definition of class Template before #15347 required HasAgreement, which interfaces do not have. After #15347, the definition of Template has been relaxed, so now Template x also holds for any interface x, so now the type checker allows using createCmd with an interface value as argument. However, this now crashes the scenario service with "SValue.toValue: unexpected SAny"
To reproduce
- Install
damlfromheadwithdaml-sdk-head - Place the files in a new directory
- Run
daml testfrom that directory
Expectation
- test passes
Reality
- test crashes with
2022-12-08 17:33:57.14 [ERROR] [test ScenarioService]
SCENARIO SERVICE STDERR: com.daml.lf.speedy.SError$SErrorCrash: CRASH (com.daml.lf.speedy.SValue.go): SValue.toValue: unexpected SAny
File: Main.daml
Hidden: no
Range: 18:1-18:5
Source: Script
Severity: DsError
Message:
"Scenario service backend error: BErrorClient (ClientIOError (GRPCIOBadStatusCode StatusUnknown
(StatusDetails {unStatusDetails = \"\"})))"
Files
-- Main.daml
module Main where
import Daml.Script
interface I
where
viewtype T
template T
with
p : Party
where
signatory p
interface instance I for T where
view = this
test : Script ()
test = do
alice <- allocateParty "alice"
alice `submit` do
createCmd $
toInterface @I T with
p = alice
pure ()
# daml.yaml
sdk-version: 0.0.0
build-options: [--target=1.15]
name: create-cmd-interface
source: Main.daml
version: 0.0.1
dependencies:
- daml-prim
- daml-stdlib
- daml-script
Similarly,
queryContractId : (Template t, IsParties p, HasCallStack) => p -> ContractId t -> Script (Optional t)can be used witht ~ <some interface>, crashing at runtime; andquery : (Template t, IsParties p) => p -> Script [(ContractId t, t)]can be used witht ~ <some interface>, returning the empty list (regardless of the existence of contracts with the requested interface on the ledger)
We should constrain the type to be templates only, possibly by restricting it to HasAgreement as a short term solution. Long term (or short term if feasible), we want a nicer solution to restrict it to templates in a way that's not dependent on HasAgreement.
the difficulty here is in naming; we could well add a compiler typeclass e.g. IsTemplate t but we already have a type class synonym Template t•, and it would be hard to explain to users when to use one or the other.
- https://github.com/digital-asset/daml/blob/b3f37dee04de3cbd13b6a92892c9e1f7c1c91691/sdk/compiler/damlc/daml-stdlib-src/DA/Internal/Template/Functions.daml#L23-L27
If we had another term for the existing Template t type class synonym, e.g. LedgerType t, we could then define Template t (and Interface t while we're at it) as an empty class with LedgerType t as superclass, with the compiler generating the appropriate instances while forbidding manually-written ones. Functions that only work with templates (resp. interfaces) would have the constraint Template t => (resp. Interface t =>), while functions that work with either would have LedgerType t =>.
type LedgerType t =
( HasTemplateTypeRep t
, HasToAnyTemplate t
, HasFromAnyTemplate t
)
class LedgerType t => Template t
class LedgerType t => Interface t
However, I'm not satisfied with the name LedgerType and, with backwards compatibility in mind, this would have to be a multi-step process to first deprecate the existing Template type class synonym and later introduce the new definitions.