1 {-# LANGUAGE DeriveDataTypeable #-}
2 {-# LANGUAGE DeriveGeneric #-}
3 {-# LANGUAGE FlexibleInstances #-}
4 {-# LANGUAGE RankNTypes #-}
6 -- | This module defines the core data types for Backpack. For more
9 -- <https://github.com/ezyang/ghc-proposals/blob/backpack/proposals/0000-backpack.rst>
10 module Distribution
.Backpack
28 , dispOpenModuleSubstEntry
29 , parsecOpenModuleSubst
30 , parsecOpenModuleSubstEntry
31 , openModuleSubstFreeHoles
33 -- * Conversions to 'UnitId'
38 import Distribution
.Compat
.Prelude
hiding (mod)
39 import Distribution
.Parsec
40 import Distribution
.Pretty
41 import Text
.PrettyPrint
(hcat
)
44 import qualified Distribution
.Compat
.CharParsing
as P
45 import qualified Text
.PrettyPrint
as Disp
47 import Distribution
.ModuleName
48 import Distribution
.Types
.ComponentId
49 import Distribution
.Types
.Module
50 import Distribution
.Types
.UnitId
51 import Distribution
.Utils
.Base62
53 import qualified Data
.Map
as Map
54 import qualified Data
.Set
as Set
56 -----------------------------------------------------------------------
59 -- | An 'OpenUnitId' describes a (possibly partially) instantiated
60 -- Backpack component, with a description of how the holes are filled
61 -- in. Unlike 'OpenUnitId', the 'ModuleSubst' is kept in a structured
62 -- form that allows for substitution (which fills in holes.) This form
63 -- of unit cannot be installed. It must first be converted to a
66 -- In the absence of Backpack, there are no holes to fill, so any such
67 -- component always has an empty module substitution; thus we can lossily
68 -- represent it as a 'DefiniteUnitId uid'.
70 -- For a source component using Backpack, however, there is more
71 -- structure as components may be parameterized over some signatures, and
72 -- these \"holes\" may be partially or wholly filled.
74 -- OpenUnitId plays an important role when we are mix-in linking,
75 -- and is recorded to the installed packaged database for indefinite
76 -- packages; however, for compiled packages that are fully instantiated,
77 -- we instantiate 'OpenUnitId' into 'UnitId'.
79 -- For more details see the Backpack spec
80 -- <https://github.com/ezyang/ghc-proposals/blob/backpack/proposals/0000-backpack.rst>
82 = -- | Identifies a component which may have some unfilled holes;
83 -- specifying its 'ComponentId' and its 'OpenModuleSubst'.
84 -- TODO: Invariant that 'OpenModuleSubst' is non-empty?
85 -- See also the Text instance.
86 IndefFullUnitId ComponentId OpenModuleSubst
87 |
-- | Identifies a fully instantiated component, which has
88 -- been compiled and abbreviated as a hash. The embedded 'UnitId'
89 -- MUST NOT be for an indefinite component; an 'OpenUnitId'
90 -- is guaranteed not to have any holes.
91 DefiniteUnitId DefUnitId
92 deriving (Generic
, Read, Show, Eq
, Ord
, Typeable
, Data
)
96 instance Binary OpenUnitId
97 instance Structured OpenUnitId
98 instance NFData OpenUnitId
where
99 rnf
(IndefFullUnitId cid subst
) = rnf cid `
seq` rnf subst
100 rnf
(DefiniteUnitId uid
) = rnf uid
102 instance Pretty OpenUnitId
where
103 pretty
(IndefFullUnitId cid insts
)
104 -- TODO: arguably a smart constructor to enforce invariant would be
106 | Map
.null insts
= pretty cid
107 |
otherwise = pretty cid
<<>> Disp
.brackets
(dispOpenModuleSubst insts
)
108 pretty
(DefiniteUnitId uid
) = pretty uid
112 -- >>> eitherParsec "foobar" :: Either String OpenUnitId
113 -- Right (DefiniteUnitId (DefUnitId {unDefUnitId = UnitId "foobar"}))
115 -- >>> eitherParsec "foo[Str=text-1.2.3:Data.Text.Text]" :: Either String OpenUnitId
116 -- Right (IndefFullUnitId (ComponentId "foo") (fromList [(ModuleName "Str",OpenModule (DefiniteUnitId (DefUnitId {unDefUnitId = UnitId "text-1.2.3"})) (ModuleName "Data.Text.Text"))]))
117 instance Parsec OpenUnitId
where
118 parsec
= P
.try parseOpenUnitId
<|
> fmap DefiniteUnitId parsec
126 parsecOpenModuleSubst
127 return (IndefFullUnitId cid insts
)
129 -- | Get the set of holes ('ModuleVar') embedded in a 'UnitId'.
130 openUnitIdFreeHoles
:: OpenUnitId
-> Set ModuleName
131 openUnitIdFreeHoles
(IndefFullUnitId _ insts
) = openModuleSubstFreeHoles insts
132 openUnitIdFreeHoles _
= Set
.empty
134 -- | Safe constructor from a UnitId. The only way to do this safely
135 -- is if the instantiation is provided.
136 mkOpenUnitId
:: UnitId
-> ComponentId
-> OpenModuleSubst
-> OpenUnitId
137 mkOpenUnitId uid cid insts
=
138 if Set
.null (openModuleSubstFreeHoles insts
)
139 then DefiniteUnitId
(unsafeMkDefUnitId uid
) -- invariant holds!
140 else IndefFullUnitId cid insts
142 -----------------------------------------------------------------------
145 -- | Create a 'DefUnitId' from a 'ComponentId' and an instantiation
147 mkDefUnitId
:: ComponentId
-> Map ModuleName Module
-> DefUnitId
148 mkDefUnitId cid insts
=
151 (unComponentId cid
++ maybe "" ("+" ++) (hashModuleSubst insts
))
156 -----------------------------------------------------------------------
159 -- | Unlike a 'Module', an 'OpenModule' is either an ordinary
160 -- module from some unit, OR an 'OpenModuleVar', representing a
161 -- hole that needs to be filled in. Substitutions are over
164 = OpenModule OpenUnitId ModuleName
165 | OpenModuleVar ModuleName
166 deriving (Generic
, Read, Show, Eq
, Ord
, Typeable
, Data
)
168 instance Binary OpenModule
169 instance Structured OpenModule
171 instance NFData OpenModule
where
172 rnf
(OpenModule uid mod_name
) = rnf uid `
seq` rnf mod_name
173 rnf
(OpenModuleVar mod_name
) = rnf mod_name
175 instance Pretty OpenModule
where
176 pretty
(OpenModule uid mod_name
) =
177 hcat
[pretty uid
, Disp
.text
":", pretty mod_name
]
178 pretty
(OpenModuleVar mod_name
) =
179 hcat
[Disp
.char
'<', pretty mod_name
, Disp
.char
'>']
183 -- >>> eitherParsec "Includes2-0.1.0.0-inplace-mysql:Database.MySQL" :: Either String OpenModule
184 -- Right (OpenModule (DefiniteUnitId (DefUnitId {unDefUnitId = UnitId "Includes2-0.1.0.0-inplace-mysql"})) (ModuleName "Database.MySQL"))
185 instance Parsec OpenModule
where
186 parsec
= parsecModuleVar
<|
> parsecOpenModule
188 parsecOpenModule
= do
192 return (OpenModule uid mod_name
)
198 return (OpenModuleVar mod_name
)
200 -- | Get the set of holes ('ModuleVar') embedded in a 'Module'.
201 openModuleFreeHoles
:: OpenModule
-> Set ModuleName
202 openModuleFreeHoles
(OpenModuleVar mod_name
) = Set
.singleton mod_name
203 openModuleFreeHoles
(OpenModule uid _n
) = openUnitIdFreeHoles uid
205 -----------------------------------------------------------------------
208 -- | An explicit substitution on modules.
210 -- NB: These substitutions are NOT idempotent, for example, a
211 -- valid substitution is (A -> B, B -> A).
212 type OpenModuleSubst
= Map ModuleName OpenModule
214 -- | Pretty-print the entries of a module substitution, suitable
215 -- for embedding into a 'OpenUnitId' or passing to GHC via @--instantiate-with@.
216 dispOpenModuleSubst
:: OpenModuleSubst
-> Disp
.Doc
217 dispOpenModuleSubst subst
=
219 . Disp
.punctuate Disp
.comma
220 $ map dispOpenModuleSubstEntry
(Map
.toAscList subst
)
222 -- | Pretty-print a single entry of a module substitution.
223 dispOpenModuleSubstEntry
:: (ModuleName
, OpenModule
) -> Disp
.Doc
224 dispOpenModuleSubstEntry
(k
, v
) = pretty k
<<>> Disp
.char
'=' <<>> pretty v
226 -- | Inverse to 'dispModSubst'.
229 parsecOpenModuleSubst
:: CabalParsing m
=> m OpenModuleSubst
230 parsecOpenModuleSubst
=
232 . flip P
.sepBy
(P
.char
',')
233 $ parsecOpenModuleSubstEntry
235 -- | Inverse to 'dispModSubstEntry'.
238 parsecOpenModuleSubstEntry
:: CabalParsing m
=> m
(ModuleName
, OpenModule
)
239 parsecOpenModuleSubstEntry
=
246 -- | Get the set of holes ('ModuleVar') embedded in a 'OpenModuleSubst'.
247 -- This is NOT the domain of the substitution.
248 openModuleSubstFreeHoles
:: OpenModuleSubst
-> Set ModuleName
249 openModuleSubstFreeHoles insts
= Set
.unions
(map openModuleFreeHoles
(Map
.elems insts
))
251 -----------------------------------------------------------------------
252 -- Conversions to UnitId
254 -- | When typechecking, we don't demand that a freshly instantiated
255 -- 'IndefFullUnitId' be compiled; instead, we just depend on the
256 -- installed indefinite unit installed at the 'ComponentId'.
257 abstractUnitId
:: OpenUnitId
-> UnitId
258 abstractUnitId
(DefiniteUnitId def_uid
) = unDefUnitId def_uid
259 abstractUnitId
(IndefFullUnitId cid _
) = newSimpleUnitId cid
261 -- | Take a module substitution and hash it into a string suitable for
262 -- 'UnitId'. Note that since this takes 'Module', not 'OpenModule',
263 -- you are responsible for recursively converting 'OpenModule'
264 -- into 'Module'. See also "Distribution.Backpack.ReadyComponent".
265 hashModuleSubst
:: Map ModuleName Module
-> Maybe String
266 hashModuleSubst subst
267 | Map
.null subst
= Nothing
269 Just
. hashToBase62
$
271 [ prettyShow mod_name
++ "=" ++ prettyShow m
++ "\n"
272 |
(mod_name
, m
) <- Map
.toList subst