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