    -- | 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
       , notHomeUnitId
       , notHomeModule
       , notHomeModuleMaybe
       , notHomeInstalledModule
       , notHomeInstalledModuleMaybe
       -- * Helpers
       , mkHomeModule
       , mkHomeInstalledModule
       , homeModuleInstantiation
       , homeModuleNameInstantiation
       )
    where

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

    import Language.Haskell.Syntax.Module.Name

    -- | 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 :: forall u. GenHomeUnit u -> UnitId
    homeUnitId (DefiniteHomeUnit UnitId
    u Maybe (u, GenInstantiations u)
    _)   = UnitId
    u
    homeUnitId (IndefiniteHomeUnit UnitId
    u GenInstantiations u
    _) = UnitId
    u

    -- | Return home unit instantiations
    homeUnitInstantiations :: GenHomeUnit u -> GenInstantiations u
    homeUnitInstantiations :: forall u. GenHomeUnit u -> GenInstantiations u
    homeUnitInstantiations (DefiniteHomeUnit   UnitId
    _ Maybe (u, GenInstantiations u)
    Nothing)       = []
    homeUnitInstantiations (DefiniteHomeUnit   UnitId
    _ (Just (u
    _,GenInstantiations u
    is))) = GenInstantiations u
    is
    homeUnitInstantiations (IndefiniteHomeUnit UnitId
    _ GenInstantiations u
    is)            = GenInstantiations u
    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 :: HomeUnit -> UnitId
    homeUnitInstanceOf HomeUnit
    h = UnitId -> Maybe UnitId -> UnitId
    forall a. a -> Maybe a -> a
    fromMaybe (HomeUnit -> UnitId
    forall u. GenHomeUnit u -> UnitId
    homeUnitId HomeUnit
    h) (HomeUnit -> Maybe UnitId
    forall u. GenHomeUnit u -> Maybe u
    homeUnitInstanceOfMaybe HomeUnit
    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 :: forall u. GenHomeUnit u -> Maybe u
    homeUnitInstanceOfMaybe (DefiniteHomeUnit   UnitId
    _ (Just (u
    u,GenInstantiations u
    _))) = u -> Maybe u
    forall a. a -> Maybe a
    Just u
    u
    homeUnitInstanceOfMaybe GenHomeUnit u
    _                                   = Maybe u
    forall a. Maybe a
    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 :: HomeUnit -> Unit
    homeUnitAsUnit (DefiniteHomeUnit UnitId
    u Maybe (UnitId, GenInstantiations UnitId)
    _)    = Definite UnitId -> Unit
    forall uid. Definite uid -> GenUnit uid
    RealUnit (UnitId -> Definite UnitId
    forall unit. unit -> Definite unit
    Definite UnitId
    u)
    homeUnitAsUnit (IndefiniteHomeUnit UnitId
    u GenInstantiations UnitId
    is) = UnitId -> GenInstantiations UnitId -> Unit
    forall u.
    IsUnitId u =>
    u -> [(ModuleName, GenModule (GenUnit u))] -> GenUnit u
    mkVirtUnit UnitId
    u GenInstantiations UnitId
    is

    -- | Map over the unit identifier for instantiating units
    homeUnitMap :: IsUnitId v => (u -> v) -> GenHomeUnit u -> GenHomeUnit v
    homeUnitMap :: forall v u.
    IsUnitId v =>
    (u -> v) -> GenHomeUnit u -> GenHomeUnit v
    homeUnitMap u -> v
    _ (DefiniteHomeUnit UnitId
    u Maybe (u, GenInstantiations u)
    Nothing)       = UnitId -> Maybe (v, GenInstantiations v) -> GenHomeUnit v
    forall u. UnitId -> Maybe (u, GenInstantiations u) -> GenHomeUnit u
    DefiniteHomeUnit UnitId
    u Maybe (v, GenInstantiations v)
    forall a. Maybe a
    Nothing
    homeUnitMap u -> v
    f (DefiniteHomeUnit UnitId
    u (Just (u
    i,GenInstantiations u
    is))) = UnitId -> Maybe (v, GenInstantiations v) -> GenHomeUnit v
    forall u. UnitId -> Maybe (u, GenInstantiations u) -> GenHomeUnit u
    DefiniteHomeUnit UnitId
    u ((v, GenInstantiations v) -> Maybe (v, GenInstantiations v)
    forall a. a -> Maybe a
    Just (u -> v
    f u
    i, (u -> v) -> GenInstantiations u -> GenInstantiations v
    forall v u.
    IsUnitId v =>
    (u -> v) -> GenInstantiations u -> GenInstantiations v
    mapInstantiations u -> v
    f GenInstantiations u
    is))
    homeUnitMap u -> v
    f (IndefiniteHomeUnit UnitId
    u GenInstantiations u
    is)          = UnitId -> GenInstantiations v -> GenHomeUnit v
    forall u. UnitId -> GenInstantiations u -> GenHomeUnit u
    IndefiniteHomeUnit UnitId
    u ((u -> v) -> GenInstantiations u -> GenInstantiations v
    forall v u.
    IsUnitId v =>
    (u -> v) -> GenInstantiations u -> GenInstantiations v
    mapInstantiations u -> v
    f GenInstantiations u
    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 :: forall u. GenHomeUnit u -> Bool
    isHomeUnitIndefinite (DefiniteHomeUnit {})   = Bool
    False
    isHomeUnitIndefinite (IndefiniteHomeUnit {}) = Bool
    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 :: forall u. GenHomeUnit u -> Bool
    isHomeUnitDefinite (DefiniteHomeUnit {})   = Bool
    True
    isHomeUnitDefinite (IndefiniteHomeUnit {}) = Bool
    False

    -- | Test if we are compiling by instantiating a definite unit
    isHomeUnitInstantiating :: GenHomeUnit u -> Bool
    isHomeUnitInstantiating :: forall u. GenHomeUnit u -> Bool
    isHomeUnitInstantiating GenHomeUnit u
    u =
       GenHomeUnit u -> Bool
    forall u. GenHomeUnit u -> Bool
    isHomeUnitDefinite GenHomeUnit u
    u Bool -> Bool -> Bool
    && Bool -> Bool
    not ([(ModuleName, GenModule (GenUnit u))] -> Bool
    forall a. [a] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null (GenHomeUnit u -> [(ModuleName, GenModule (GenUnit u))]
    forall u. GenHomeUnit u -> GenInstantiations u
    homeUnitInstantiations GenHomeUnit u
    u))

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

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

    -- | Test if the unit-id is not the home unit-id
    notHomeUnitId :: Maybe (GenHomeUnit u) -> UnitId -> Bool
    notHomeUnitId :: forall u. Maybe (GenHomeUnit u) -> UnitId -> Bool
    notHomeUnitId Maybe (GenHomeUnit u)
    Nothing   UnitId
    _   = Bool
    True
    notHomeUnitId (Just GenHomeUnit u
    hu) UnitId
    uid = Bool -> Bool
    not (GenHomeUnit u -> UnitId -> Bool
    forall u. GenHomeUnit u -> UnitId -> Bool
    isHomeUnitId GenHomeUnit u
    hu UnitId
    uid)

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

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

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

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

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

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

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

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

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

    -- | Make a module in home unit
    mkHomeInstalledModule :: GenHomeUnit u -> ModuleName -> InstalledModule
    mkHomeInstalledModule :: forall u. GenHomeUnit u -> ModuleName -> InstalledModule
    mkHomeInstalledModule GenHomeUnit u
    hu = UnitId -> ModuleName -> InstalledModule
    forall u. u -> ModuleName -> GenModule u
    mkModule (GenHomeUnit u -> UnitId
    forall u. GenHomeUnit u -> UnitId
    homeUnitId GenHomeUnit u
    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 :: HomeUnit -> ModuleName -> Module
    homeModuleNameInstantiation HomeUnit
    hu ModuleName
    mod_name =
        case ModuleName -> GenInstantiations UnitId -> Maybe Module
    forall a b. Eq a => a -> [(a, b)] -> Maybe b
    lookup ModuleName
    mod_name (HomeUnit -> GenInstantiations UnitId
    forall u. GenHomeUnit u -> GenInstantiations u
    homeUnitInstantiations HomeUnit
    hu) of
            Maybe Module
    Nothing  -> HomeUnit -> ModuleName -> Module
    mkHomeModule HomeUnit
    hu ModuleName
    mod_name
            Just Module
    mod -> Module
    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 :: Maybe HomeUnit -> Module -> Module
    homeModuleInstantiation :: Maybe HomeUnit -> Module -> Module
    homeModuleInstantiation Maybe HomeUnit
    mhu Module
    mod
       | Just HomeUnit
    hu <- Maybe HomeUnit
    mhu
       , HomeUnit -> Module -> Bool
    isHomeModule HomeUnit
    hu Module
    mod = HomeUnit -> ModuleName -> Module
    homeModuleNameInstantiation HomeUnit
    hu (Module -> ModuleName
    forall unit. GenModule unit -> ModuleName
    moduleName Module
    mod)
       | Bool
    otherwise           = Module
    mod