summaryrefslogtreecommitdiff
path: root/compiler/GHC/Unit/Home.hs
blob: 6baa8bf5fb11163e37f205d972a379281d160faa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
-- | The home unit is the unit (i.e. compiled package) that contains the module
-- we are compiling/typechecking.
module GHC.Unit.Home
   ( GenHomeUnit (..)
   , HomeUnit
   , homeUnitId
   , homeUnitInstantiations
   , homeUnitInstanceOf
   , homeUnitInstanceOfMaybe
   , homeUnitAsUnit
   , homeUnitMap
   -- * Predicates
   , isHomeUnitIndefinite
   , isHomeUnitDefinite
   , isHomeUnitInstantiating
   , isHomeUnit
   , isHomeUnitId
   , isHomeUnitInstanceOf
   , isHomeModule
   , isHomeInstalledModule
   , notHomeModule
   , notHomeModuleMaybe
   , notHomeInstalledModule
   , notHomeInstalledModuleMaybe
   -- * Helpers
   , mkHomeModule
   , mkHomeInstalledModule
   , homeModuleInstantiation
   , homeModuleNameInstantiation
   )
where

import GHC.Prelude
import GHC.Unit.Types
import GHC.Unit.Module.Name
import Data.Maybe

-- | Information about the home unit (i.e., the until that will contain the
-- modules we are compiling)
--
-- The unit identifier of the instantiating units is left open to allow
-- switching from UnitKey (what is provided by the user) to UnitId (internal
-- unit identifier) with `homeUnitMap`.
--
-- TODO: this isn't implemented yet. UnitKeys are still converted too early into
-- UnitIds in GHC.Unit.State.readUnitDataBase and wiring of home unit
-- instantiations is done inplace in DynFlags by
-- GHC.Unit.State.upd_wired_in_home_instantiations.
data GenHomeUnit u
   = DefiniteHomeUnit UnitId (Maybe (u, GenInstantiations u))
      -- ^ Definite home unit (i.e. that we can compile).
      --
      -- Nothing:        not an instantiated unit
      -- Just (i,insts): made definite by instantiating "i" with "insts"

   | IndefiniteHomeUnit UnitId (GenInstantiations u)
      -- ^ Indefinite home unit (i.e. that we can only typecheck)
      --
      -- All the holes are instantiated with fake modules from the Hole unit.
      -- See Note [Representation of module/name variables] in "GHC.Unit"

type HomeUnit = GenHomeUnit UnitId

-- | Return home unit id
homeUnitId :: GenHomeUnit u -> UnitId
homeUnitId (DefiniteHomeUnit u _)   = u
homeUnitId (IndefiniteHomeUnit u _) = u

-- | Return home unit instantiations
homeUnitInstantiations :: GenHomeUnit u -> GenInstantiations u
homeUnitInstantiations (DefiniteHomeUnit   _ Nothing)       = []
homeUnitInstantiations (DefiniteHomeUnit   _ (Just (_,is))) = is
homeUnitInstantiations (IndefiniteHomeUnit _ is)            = is

-- | Return the unit id of the unit that is instantiated by the home unit.
--
-- E.g. if home unit = q[A=p:B,...] we return q.
--
-- If the home unit is not an instance of another unit, we return its own unit
-- id (it is an instance of itself if you will).
homeUnitInstanceOf :: HomeUnit -> UnitId
homeUnitInstanceOf h = fromMaybe (homeUnitId h) (homeUnitInstanceOfMaybe h)

-- | Return the unit id of the unit that is instantiated by the home unit.
--
-- E.g. if home unit = q[A=p:B,...] we return (Just q).
--
-- If the home unit is not an instance of another unit, we return Nothing.
homeUnitInstanceOfMaybe :: GenHomeUnit u -> Maybe u
homeUnitInstanceOfMaybe (DefiniteHomeUnit   _ (Just (u,_))) = Just u
homeUnitInstanceOfMaybe _                                   = Nothing

-- | Return the home unit as a normal unit.
--
-- We infer from the home unit itself the kind of unit we create:
--    1. If the home unit is definite, we must be compiling so we return a real
--    unit. The definite home unit may be the result of a unit instantiation,
--    say `p = q[A=r:X]`. In this case we could have returned a virtual unit
--    `q[A=r:X]` but it's not what the clients of this function expect,
--    especially because `p` is lost when we do this. The unit id of a virtual
--    unit is made up internally so `unitId(q[A=r:X])` is not equal to `p`.
--
--    2. If the home unit is indefinite we can only create a virtual unit from
--    it. It's ok because we must be only typechecking the home unit so we won't
--    produce any code object that rely on the unit id of this virtual unit.
homeUnitAsUnit :: HomeUnit -> Unit
homeUnitAsUnit (DefiniteHomeUnit u _)    = RealUnit (Definite u)
homeUnitAsUnit (IndefiniteHomeUnit u is) = mkVirtUnit (Indefinite u) is

-- | Map over the unit identifier for instantiating units
homeUnitMap :: IsUnitId v => (u -> v) -> GenHomeUnit u -> GenHomeUnit v
homeUnitMap _ (DefiniteHomeUnit u Nothing)       = DefiniteHomeUnit u Nothing
homeUnitMap f (DefiniteHomeUnit u (Just (i,is))) = DefiniteHomeUnit u (Just (f i, mapInstantiations f is))
homeUnitMap f (IndefiniteHomeUnit u is)          = IndefiniteHomeUnit u (mapInstantiations f is)

----------------------------
-- Predicates
----------------------------

-- | Test if we are type-checking an indefinite unit
--
-- (if it is not, we should never use on-the-fly renaming)
isHomeUnitIndefinite :: GenHomeUnit u -> Bool
isHomeUnitIndefinite (DefiniteHomeUnit {})   = False
isHomeUnitIndefinite (IndefiniteHomeUnit {}) = True

-- | Test if we are compiling a definite unit
--
-- (if it is, we should never use on-the-fly renaming)
isHomeUnitDefinite :: GenHomeUnit u -> Bool
isHomeUnitDefinite (DefiniteHomeUnit {})   = True
isHomeUnitDefinite (IndefiniteHomeUnit {}) = False

-- | Test if we are compiling by instantiating a definite unit
isHomeUnitInstantiating :: GenHomeUnit u -> Bool
isHomeUnitInstantiating u =
   isHomeUnitDefinite u && not (null (homeUnitInstantiations u))

-- | Test if the unit is the home unit
isHomeUnit :: HomeUnit -> Unit -> Bool
isHomeUnit hu u = u == homeUnitAsUnit hu

-- | Test if the unit-id is the home unit-id
isHomeUnitId :: GenHomeUnit u -> UnitId -> Bool
isHomeUnitId hu uid = uid == homeUnitId hu

-- | Test if the home unit is an instance of the given unit-id
isHomeUnitInstanceOf :: HomeUnit -> UnitId -> Bool
isHomeUnitInstanceOf hu u = homeUnitInstanceOf hu == u

-- | Test if the module comes from the home unit
isHomeModule :: HomeUnit -> Module -> Bool
isHomeModule hu m = isHomeUnit hu (moduleUnit m)

-- | Test if the module comes from the home unit
isHomeInstalledModule :: GenHomeUnit u -> InstalledModule -> Bool
isHomeInstalledModule hu m = isHomeUnitId hu (moduleUnit m)


-- | Test if a module doesn't come from the given home unit
notHomeInstalledModule :: GenHomeUnit u -> InstalledModule -> Bool
notHomeInstalledModule hu m = not (isHomeInstalledModule hu m)

-- | Test if a module doesn't come from the given home unit
notHomeInstalledModuleMaybe :: Maybe (GenHomeUnit u) -> InstalledModule -> Bool
notHomeInstalledModuleMaybe mh m = fromMaybe True $ fmap (`notHomeInstalledModule` m) mh


-- | Test if a module doesn't come from the given home unit
notHomeModule :: HomeUnit -> Module -> Bool
notHomeModule hu m = not (isHomeModule hu m)

-- | Test if a module doesn't come from the given home unit
notHomeModuleMaybe :: Maybe HomeUnit -> Module -> Bool
notHomeModuleMaybe mh m = fromMaybe True $ fmap (`notHomeModule` m) mh

----------------------------
-- helpers
----------------------------

-- | Make a module in home unit
mkHomeModule :: HomeUnit -> ModuleName -> Module
mkHomeModule hu = mkModule (homeUnitAsUnit hu)

-- | Make a module in home unit
mkHomeInstalledModule :: GenHomeUnit u -> ModuleName -> InstalledModule
mkHomeInstalledModule hu = mkModule (homeUnitId hu)

-- | Return the module that is used to instantiate the given home module name.
-- If the ModuleName doesn't refer to a signature, return the actual home
-- module.
--
-- E.g., the instantiating module of @A@ in @p[A=q[]:B]@ is @q[]:B@.
--       the instantiating module of @A@ in @p@ is @p:A@.
homeModuleNameInstantiation :: HomeUnit -> ModuleName -> Module
homeModuleNameInstantiation hu mod_name =
    case lookup mod_name (homeUnitInstantiations hu) of
        Nothing  -> mkHomeModule hu mod_name
        Just mod -> mod

-- | Return the module that is used to instantiate the given home module.
--
-- If the given module isn't a module hole, return the actual home module.
--
-- E.g., the instantiating module of @p:A@ in @p[A=q[]:B]@ is @q[]:B@.
--       the instantiating module of @r:A@ in @p[A=q[]:B]@ is @r:A@.
--       the instantiating module of @p:A@ in @p@ is @p:A@.
--       the instantiating module of @r:A@ in @p@ is @r:A@.
homeModuleInstantiation :: HomeUnit -> Module -> Module
homeModuleInstantiation hu mod
   | isHomeModule hu mod = homeModuleNameInstantiation hu (moduleName mod)
   | otherwise           = mod