Skip to content

Commit

Permalink
Desugar interface implements declarations (#10895)
Browse files Browse the repository at this point in the history
* Desugar interface implements declarations

This PR adds desugaring for tplImplements. This consists of the
corresponding typeclass instance (which we just ignore in LF for now,
we probably need it once we have pure functions) and a _implements_
top-level value.

changelog_begin
changelog_end

* Address review feedback

changelog_begin
changelog_end
  • Loading branch information
cocreature authored Sep 15, 2021
1 parent b5648c0 commit f08ac5f
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 17 deletions.
8 changes: 6 additions & 2 deletions compiler/daml-lf-ast/src/DA/Daml/LF/Ast/Pretty.hs
Original file line number Diff line number Diff line change
Expand Up @@ -590,11 +590,11 @@ pPrintTemplateChoice lvl modName tpl (TemplateChoice mbLoc name isConsuming cont

pPrintTemplate ::
PrettyLevel -> ModuleName -> Template -> Doc ann
pPrintTemplate lvl modName (Template mbLoc tpl param precond signatories observers agreement choices mbKey _implements) = -- TODO interfaces
pPrintTemplate lvl modName (Template mbLoc tpl param precond signatories observers agreement choices mbKey implements) =
withSourceLoc lvl mbLoc $
keyword_ "template" <-> pPrint tpl <-> pPrint param
<-> keyword_ "where"
$$ nest 2 (vcat ([signatoriesDoc, observersDoc, precondDoc, agreementDoc] ++ mbKeyDoc ++ choiceDocs))
$$ nest 2 (vcat ([signatoriesDoc, observersDoc, precondDoc, agreementDoc] ++ mbImplementsDoc ++ mbKeyDoc ++ choiceDocs))
where
signatoriesDoc = keyword_ "signatory" <-> pPrintPrec lvl 0 signatories
observersDoc = keyword_ "observer" <-> pPrintPrec lvl 0 observers
Expand All @@ -608,6 +608,10 @@ pPrintTemplate lvl modName (Template mbLoc tpl param precond signatories observe
, nest 2 (keyword_ "body" <-> pPrintPrec lvl 0 (tplKeyBody key))
, nest 2 (keyword_ "maintainers" <-> pPrintPrec lvl 0 (tplKeyMaintainers key))
]
mbImplementsDoc
| null implements = []
| otherwise = [keyword_ "implements" <-> hsep (map (pPrintPrec lvl 0) implements)]


pPrintFeatureFlags :: FeatureFlags -> Doc ann
pPrintFeatureFlags flags
Expand Down
57 changes: 45 additions & 12 deletions compiler/damlc/daml-lf-conversion/src/DA/Daml/LFConversion.hs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ data Env = Env
,envTemplateBinds :: MS.Map TypeConName TemplateBinds
,envExceptionBinds :: MS.Map TypeConName ExceptionBinds
,envChoiceData :: MS.Map TypeConName [ChoiceData]
,envImplements :: MS.Map TypeConName [GHC.Type]
,envInterfaces :: S.Set TypeConName
,envIsGenerated :: Bool
,envTypeVars :: !(MS.Map Var TypeVarName)
-- ^ Maps GHC type variables in scope to their LF type variable names
Expand Down Expand Up @@ -408,18 +410,21 @@ modInstanceInfoFromDetails :: ModDetails -> ModInstanceInfo
modInstanceInfoFromDetails ModDetails{..} = MS.fromList
[ (is_dfun, overlapMode is_flag) | ClsInst{..} <- md_insts ]

interfaceNames :: LF.Version -> [TyThing] -> S.Set TypeConName
interfaceNames lfVersion tyThings
| lfVersion `supports` featureInterfaces =
S.fromList [ mkTypeCon [getOccText t] | ATyCon t <- tyThings, hasDamlInterfaceCtx t ]
| otherwise = S.empty

convertInterfaces :: Env -> [TyThing] -> ConvertM [Definition]
convertInterfaces env tyThings
| envLfVersion env `supports` featureInterfaces = interfaceClasses
| otherwise = pure []
convertInterfaces env tyThings = interfaceClasses
where
interfaceCons = S.fromList [ getOccText t | ATyCon t <- tyThings, hasDamlInterfaceCtx t ]
interfaceClasses = sequence
[ DInterface <$> convertInterface interface cls
| ATyCon t <- tyThings
, Just cls <- [tyConClass_maybe t]
, Just interface <- [T.stripPrefix "Is" (getOccText t)]
, interface `S.member` interfaceCons
, TypeConName [interface] `S.member` (envInterfaces env)
]
convertInterface :: T.Text -> Class -> ConvertM DefInterface
convertInterface name cls = do
Expand Down Expand Up @@ -475,6 +480,13 @@ convertModule lfVersion pkgMap stablePackages isGenerated file x details = runCo
| otherwise -> [(name, body)]
Rec binds -> binds
]
interfaceCons = interfaceNames lfVersion (eltsUFM (cm_types x))
tplImplements = MS.fromListWith (++)
[ (mkTypeCon [getOccText tplTy], [ifaceTy])
| (name, _) <- binds
, "_implements_" `T.isPrefixOf` getOccText name
, TypeCon _ [TypeCon tplTy [], ifaceTy] <- [varType name]
]
choiceData = MS.fromListWith (++)
[ (mkTypeCon [getOccText tplTy], [ChoiceData ty v])
| (name, v) <- binds
Expand All @@ -496,8 +508,10 @@ convertModule lfVersion pkgMap stablePackages isGenerated file x details = runCo
, envPkgMap = pkgMap
, envStablePackages = stablePackages
, envLfVersion = lfVersion
, envInterfaces = interfaceCons
, envTemplateBinds = templateBinds
, envExceptionBinds = exceptionBinds
, envImplements = tplImplements
, envChoiceData = choiceData
, envIsGenerated = isGenerated
, envTypeVars = MS.empty
Expand All @@ -518,9 +532,9 @@ convertTypeDef env o@(ATyCon t) = withRange (convNameLoc t) $ if
, n `elementOfUniqSet` internalTypes
-> pure []
| NameIn DA_Internal_Prelude "Optional" <- t -> pure []
-- Consumption marker types used to transfer information from template desugaring to LF conversion.
-- Types used only for desugaring are dropped during the LF conversion.
| NameIn DA_Internal_Desugar n <- t
, n `elementOfUniqSet` consumingTypes
, n `elementOfUniqSet` desugarTypes
-> pure []

| hasDamlInterfaceCtx t && envLfVersion env `supports` featureInterfaces
Expand Down Expand Up @@ -743,8 +757,7 @@ convertTemplate env tplTypeCon tbinds@TemplateBinds{..}
tplAgreement <- useSingleMethodDict env fAgreement (`ETmApp` EVar this)
tplChoices <- convertChoices env tplTypeCon tbinds
tplKey <- convertTemplateKey env tplTypeCon tbinds
-- TODO https://github.com/digital-asset/daml/issues/10810
let tplImplements = []
tplImplements <- convertImplements env tplTypeCon
pure Template {..}

| otherwise =
Expand Down Expand Up @@ -814,6 +827,16 @@ useSingleMethodDict env (Cast ghcExpr _) f = do
useSingleMethodDict env x _ =
unhandled "useSingleMethodDict: not a single method type class dictionary" x

convertImplements :: Env -> LF.TypeConName -> ConvertM [Qualified TypeConName]
convertImplements env tplTypeCon =
mapM convertInterfaceCon (MS.findWithDefault [] tplTypeCon (envImplements env))
where
convertInterfaceCon ty = do
ty' <- convertType env ty
case ty' of
TCon con -> pure con
_ -> unhandled "interface type" ty

convertChoices :: Env -> LF.TypeConName -> TemplateBinds -> ConvertM (NM.NameMap TemplateChoice)
convertChoices env tplTypeCon tbinds =
NM.fromList <$> traverse (convertChoice env tbinds)
Expand All @@ -828,7 +851,7 @@ convertChoice env tbinds (ChoiceData ty expr)
, (_, action)
, _
, (_, optObservers)
] <- convertExpr env expr
] <- removeLocations <$> convertExpr env expr

mbObservers <-
case optObservers of
Expand Down Expand Up @@ -876,11 +899,21 @@ convertBind env (name, x)
| "_choice_" `T.isPrefixOf` getOccText name
= pure []

-- These are only used as markers for the LF conversion.
| "_implements_" `T.isPrefixOf` getOccText name
= pure []

-- Remove internal functions.
| Just internals <- lookupUFM internalFunctions (envGHCModuleName env)
, getOccFS name `elementOfUniqSet` internals
= pure []

-- TODO https://github.com/digital-asset/daml/issues/10810
-- Reconsider once we have a constructor for existential interfaces
-- in LF.
| Just iface <- T.stripPrefix "$W" (getOccText name)
, mkTypeCon [iface] `S.member` (envInterfaces env) = pure []

-- NOTE(MH): Our inline return type syntax produces a local letrec for
-- recursive functions. We currently don't support local letrecs.
-- However, we can work around this issue by rewriting
Expand Down Expand Up @@ -963,8 +996,8 @@ internalTypes = mkUniqSet
, "Experimental"
]

consumingTypes :: UniqSet FastString
consumingTypes = mkUniqSet ["Consuming", "PreConsuming", "PostConsuming", "NonConsuming"]
desugarTypes :: UniqSet FastString
desugarTypes = mkUniqSet ["Consuming", "PreConsuming", "PostConsuming", "NonConsuming", "Implements"]

internalFunctions :: UniqFM (UniqSet FastString)
internalFunctions = listToUFM $ map (bimap mkModuleNameFS mkUniqSet)
Expand Down
4 changes: 3 additions & 1 deletion compiler/damlc/daml-stdlib-src/DA/Internal/Desugar.daml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ module DA.Internal.Desugar (
Bool(..), Text, Optional(..),
concat, magic,
Party, ContractId, Update, Any,
NonConsuming(..), PreConsuming(..), PostConsuming(..), Consuming(..)
NonConsuming(..), PreConsuming(..), PostConsuming(..), Consuming(..),
Implements(..),
) where

import DA.Internal.Prelude
Expand All @@ -33,3 +34,4 @@ data NonConsuming t = NonConsuming {}
data PreConsuming t = PreConsuming {}
data Consuming t = Consuming {}
data PostConsuming t = PostConsuming {}
data Implements t i = Implements
75 changes: 73 additions & 2 deletions compiler/damlc/tests/daml-test-files/InterfaceDesugared.daml
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,83 @@ class
data GHC.Types.DamlInterface => Token = Token GHC.Types.Opaque

instance HasExercise Token Split (ContractId Token, ContractId Token) where
exercise = GHC.Types.primitive @"UExercise"
exercise = error "unimplemented"
-- TODO https://github.com/digital-asset/daml/issues/10810
-- Switch to this once the Haskell typechecker is fixed
-- GHC.Types.primitive @"UExercise"

instance HasExercise Token Transfer (ContractId Token) where
exercise = GHC.Types.primitive @"UExercise"
exercise = error "unimplemented"
-- TODO https://github.com/digital-asset/daml/issues/10810
-- Switch to this once the Haskell typechecker is fixed
-- GHC.Types.primitive @"UExercise"

instance IsToken Token where
-- TODO https://github.com/digital-asset/daml/issues/10810
-- enable once we support pure functions
-- getAmount = primitivInterface @"getAmount"

data Asset = Asset { amount : Decimal, issuer : Party, owner : Party }
deriving (Eq, Show)

instance IsToken Asset where
-- TODO https://github.com/digital-asset/daml/issues/10810
-- enable once we support pure functions
-- getAmount Asset{..} = amount

_implements_AssetToken : DA.Internal.Desugar.Implements Asset Token
_implements_AssetToken = DA.Internal.Desugar.Implements

instance HasCreate Asset where
create = GHC.Types.primitive @"UCreate"

instance HasSignatory Asset where
signatory = error "unimplemented"

instance HasEnsure Asset where
ensure = error "unimplemented"

instance HasAgreement Asset where
agreement = error "agreement"

instance HasObserver Asset where
observer = error "unimplemented"

instance HasExercise Asset Transfer (ContractId Token) where
exercise = GHC.Types.primitive @"UExercise"

instance HasExercise Asset Archive () where
exercise = GHC.Types.primitive @"UExercise"

instance HasExercise Asset Split (ContractId Token, ContractId Token) where
exercise = GHC.Types.primitive @"UExercise"

instance HasArchive Asset where
archive cid = exercise cid Archive

_choice_AssetTransfer :
( Asset -> Transfer -> [DA.Internal.Desugar.Party]
, DA.Internal.Desugar.ContractId Asset -> Asset -> Transfer -> DA.Internal.Desugar.Update (ContractId Token)
, DA.Internal.Desugar.Consuming Asset
, DA.Internal.Desugar.Optional (Asset -> Transfer -> [DA.Internal.Desugar.Party])
)
_choice_AssetTransfer =
(error "abc", error "abc", error "abc", DA.Internal.Desugar.None)

_choice_AssetArchive :
( Asset -> Archive -> [DA.Internal.Desugar.Party]
, DA.Internal.Desugar.ContractId Asset -> Asset -> Archive -> DA.Internal.Desugar.Update ()
, DA.Internal.Desugar.Consuming Asset
, DA.Internal.Desugar.Optional (Asset -> Archive -> [DA.Internal.Desugar.Party])
)
_choice_AssetArchive =
(error "abc", error "abc", error "abc", DA.Internal.Desugar.None)

_choice_AssetSplit :
( Asset -> Split -> [DA.Internal.Desugar.Party]
, DA.Internal.Desugar.ContractId Asset -> Asset -> Split -> DA.Internal.Desugar.Update (ContractId Token, ContractId Token)
, DA.Internal.Desugar.Consuming Asset
, DA.Internal.Desugar.Optional (Asset -> Split -> [DA.Internal.Desugar.Party])
)
_choice_AssetSplit =
(error "abc", error "abc", error "abc", DA.Internal.Desugar.None)

0 comments on commit f08ac5f

Please sign in to comment.