On Thu, 16 Jun 2022, Erdi, Gergo via ghc-devs wrote:

Is there a migration guide for GHC API clients for the new “multiple home units”
feature?

OK so in concrete terms, please see my attached program which is a heavily cut-down, standalone version of my real program. On commit fd42ab5fa1df847a6b595dfe4b63d9c7eecbf400^ (i.e. 3219610e3ba6cb6a5cd1f4e32e2b4befea5bd384) it compiles and works as expected. On commit fd42ab5fa1df847a6b595dfe4b63d9c7eecbf400 onwards, two problems pop up:

1. `extendMG` has changed and now requires manually specifying outgoing dependency edges. I thought the whole point of `summariseFile` was to collect this information? The reason I need to `extendMG` at that point is to get intra-unit orphan instances working.

2. `modifyUnitState` and its two uses (`addUnit` and `registerModule`) need to be updated to the new API. I think it makes sense that these need changing, since they touch exactly on the issue of which units are being compiled right now. However, I don't know how to update these. Also, I guess `setHomeUnit` should change the `CurrentUnit` instead of the `HomeUnit` now?

Thanks,
        Gergo
{-# LANGUAGE BlockArguments, RecordWildCards #-}

module Main where

import qualified Paths_ghc_lib as GHC

import GHC
import GHC.Core
import GHC.Core.FamInstEnv
import GHC.Core.InstEnv
import GHC.Core.Rules
import GHC.Core.TyCon
import GHC.CoreToStg.Prep
import GHC.Data.FastString
import GHC.Data.IOEnv
import GHC.Data.StringBuffer
import GHC.Driver.Env
import GHC.Driver.Errors.Types
import GHC.Driver.Main (hscSimplify)
import GHC.Driver.Make
import GHC.Driver.Monad
import GHC.Driver.Phases
import GHC.Driver.Session
import GHC.Iface.Make
import GHC.Iface.Tidy (tidyProgram)
import GHC.Tc.Types
import GHC.Tc.Utils.Monad
import GHC.Types.Annotations
import GHC.Types.Error
import GHC.Types.SourceError
import GHC.Types.SourceFile
import GHC.Types.TypeEnv
import GHC.Types.Unique.FM
import GHC.Unit.Env
import GHC.Unit.External
import GHC.Unit.Home
import GHC.Unit.Home.ModInfo
import GHC.Unit.Info
import GHC.Unit.Module.Env
import GHC.Unit.Module.Graph
import GHC.Unit.Module.ModDetails
import GHC.Unit.Module.ModGuts
import GHC.Unit.Module.ModSummary
import GHC.Unit.Module.Name
import GHC.Unit.State
import GHC.Unit.Types

import Data.Version
import qualified Data.Map as M
import Data.Foldable (for_)
import Control.Monad.Writer hiding ((<>))
import System.FilePath
import Data.Time

fooUnitId, barUnitId :: UnitId
fooUnitId = UnitId $ fsLit "foo"
barUnitId = UnitId $ fsLit "bar"

fooUnit, barUnit :: Unit
fooUnit = RealUnit $ Definite fooUnitId
barUnit = RealUnit $ Definite barUnitId

fooUnitInfo, barUnitInfo :: UnitInfo
fooUnitInfo = fakeUnitInfo fooUnitId []
barUnitInfo = fakeUnitInfo barUnitId []

main :: IO ()
main = do
    let mods = [ (fooUnitId, mkModuleName "A")
               , (barUnitId, mkModuleName "B")
               , (mainUnitId, mkModuleName "C")
               ]

    libDir <- liftIO $ GHC.getDataDir
    runGhcT (Just libDir) do
        setup

        mapM_ (modifySession . addUnit)
            [ fooUnitInfo
            , barUnitInfo
            ]

        for_ mods \(unitId, modName) -> do
            target <- mkTarget unitId $ "input" </> moduleNameSlashes modName <.> "mu"
            (ms, deps) <- prepareModule modName target
            liftIO $ print (modName, deps)
            let mod = ms_mod ms

            modifySession $ setHomeUnit $ toUnitId . moduleUnit $ mod

            (core, details, iface) <- compileToCore ms
            registerModule details iface
            return ()

compileToCore
    :: (GhcMonad m)
    => ModSummary
    -> m (CoreProgram, ModDetails, ModIface)
compileToCore ms = do
    dmod <- desugarModule =<< typecheckModule =<< parseModule ms
    let mguts = coreModule dmod

    env <- getSession
    mguts' <- liftIO $ hscSimplify env [] mguts
    (cg_guts, details) <- liftIO $ tidyProgram env mguts'
    let types = md_types details
        tycons = typeEnvTyCons types
        data_tycons = filter isDataTyCon tycons

    let partial_iface = mkPartialIface env details ms mguts'
    iface <- liftIO $ mkFullIface env partial_iface Nothing

    let tidy_binds = cg_binds cg_guts
    binds <- liftIO $ corePrepPgm env mod modLoc tidy_binds data_tycons
    return (binds, details, iface)
  where
    mod = ms_mod ms
    modLoc = ms_location ms

prepareModule :: (GhcMonad m) => ModuleName -> Target -> m (ModSummary, [ModuleName])
prepareModule modName target = do
    env <- getSession
    env <- return $ setHomeUnit (toUnitId unit) env

    mems <- liftIO $ summariseFile env [] file mb_phase fileContents
    case mems of
      Left err -> do
          reportErrors [fmap GhcDriverMessage err]
          error $ unwords ["prepareModule/lookupModuleEnv", unitString unit, moduleNameString . moduleName $ mod]
      Right ems -> do
          let ms = emsModSummary ems
              deps = ms_textual_imps ms
          when (unit == mainUnit) $ registerModSummary ems
          let deps = map (unLoc . snd) deps
          return (ms, deps)
  where
    unit = RealUnit . Definite $ targetUnitId target
    mod = mkModule unit modName

    Target
        { targetId = TargetFile file mb_phase
        , targetContents = fileContents
        } = target

registerModSummary :: (GhcMonad m) => ExtendedModSummary -> m ()
registerModSummary ems = modifySession \env ->
    let mg = hsc_mod_graph env
        mg' = extendMG mg ems
    in env{ hsc_mod_graph = mg' }

modifyUnitState :: (UnitState -> UnitState) -> HscEnv -> HscEnv
modifyUnitState f env = env
    { hsc_unit_env = let ue = hsc_unit_env env in ue
        { ue_units = f (ue_units ue)
        }
    }

mkTarget :: (MonadIO m) => UnitId -> FilePath -> m Target
mkTarget unitId filePath = do
    contents <- liftIO $ hGetStringBuffer filePath
    timestamp <- liftIO getCurrentTime

    return $ Target
        { targetId = TargetFile filePath (Just $ Cpp HsSrcFile)
        , targetAllowObjCode = False
        , targetContents = Just (contents, timestamp)
        , targetUnitId = unitId
        }

setup :: (GhcMonad m) => m ()
setup = do
    let primPkgDb = "ghc-lib/ghc-lib/stage1/lib/package.conf.d"

    dflags <- getSessionDynFlags
    dflags <- return $ dflags
        { backend = NoBackend
        , mainModuleNameIs = mkModuleName "No/Main"
        , packageDBFlags = [PackageDB $ PkgDbPath primPkgDb, ClearPackageDBs]
        }

    setSessionDynFlags dflags

reportErrors :: (GhcMonad m) => [ErrorMessages] -> m ()
reportErrors errs = do
    errs <- return $ unionManyMessages errs
    unless (isEmptyMessages errs) $ throwErrors errs

setHomeUnit :: UnitId -> HscEnv -> HscEnv
setHomeUnit unitId env = env
    { hsc_unit_env = let ue = hsc_unit_env env in ue
        { ue_home_unit = Just $ DefiniteHomeUnit unitId Nothing
        }
    }

mkModIface :: HscEnv -> TcGblEnv -> ModDetails -> ModSummary -> IO ModIface
mkModIface env tcg mod_details mod_summary = mkIfaceTc env Sf_Ignore mod_details mod_summary tcg

fakeUnitInfo :: UnitId -> [ModuleName] -> UnitInfo
fakeUnitInfo unit mods = GenericUnitInfo
    { unitId = unit
    , unitInstanceOf = unit
    , unitInstantiations = []
    , unitPackageId = pkgId
    , unitPackageName = pkgName
    , unitPackageVersion = makeVersion [0, 0]
    , unitComponentName = Nothing
    , unitAbiHash = mempty
    , unitDepends = []
    , unitAbiDepends = []
    , unitImportDirs = []
    , unitLibraries = []
    , unitExtDepLibsSys = []
    , unitExtDepLibsGhc = []
    , unitLibraryDirs   = []
    , unitLibraryDynDirs = []
    , unitExtDepFrameworks = []
    , unitExtDepFrameworkDirs = []
    , unitLinkerOptions = []
    , unitCcOptions = []
    , unitIncludes = []
    , unitIncludeDirs = []
    , unitHaddockInterfaces = []
    , unitHaddockHTMLs = []
    , unitExposedModules = [(mod, Nothing) | mod <- mods]
    , unitHiddenModules = []
    , unitIsIndefinite  = False
    , unitIsExposed  = True
    , unitIsTrusted  = True
    }
  where
    pkg = fsLit $ unitIdString unit <> "-pkg"
    pkgId = PackageId pkg
    pkgName = PackageName pkg

addUnit :: UnitInfo -> HscEnv -> HscEnv
addUnit unitInfo@GenericUnitInfo{..}<mailto:unitInfo@GenericUnitInfo%7b..%7d> = modifyUnitState $ \us -> us
    { packageNameMap = addToUFM (packageNameMap us) unitPackageName unitId
    , unitInfoMap = M.insert unitId unitInfo $ unitInfoMap us
    }

registerToEps :: (GhcMonad m) => ModDetails -> ModIface -> m ()
registerToEps details@ModDetails{..}<mailto:details@ModDetails%7b..%7d> iface = do
    env <- getSession
    liftIO $ runIOEnv (Env env '\0' () ()) $ updateEps_ extendEps
  where
    mod = mi_module iface

    extendEps :: ExternalPackageState -> ExternalPackageState
    extendEps eps = eps
        { eps_PIT = extendModuleEnv (eps_PIT eps) mod iface
        , eps_PTE = plusTypeEnv (eps_PTE eps) md_types
        , eps_rule_base = extendRuleBaseList (eps_rule_base eps) md_rules
        , eps_inst_env = extendInstEnvList (eps_inst_env eps) md_insts
        , eps_fam_inst_env = extendFamInstEnvList (eps_fam_inst_env eps) md_fam_insts
        , eps_ann_env = extendAnnEnvList (eps_ann_env eps) md_anns
        , eps_mod_fam_inst_env =
                let fam_inst_env = extendFamInstEnvList emptyFamInstEnv md_fam_insts
                in extendModuleEnv (eps_mod_fam_inst_env eps) mod fam_inst_env
        , eps_stats = addEpsInStats (eps_stats eps) (length $ typeEnvElts md_types) (length md_insts) (length md_rules)
        }

registerModule :: (GhcMonad m) => ModDetails -> ModIface -> m ()
registerModule details iface = modifySession $ extendHpt . addModule
  where
    hmi = HomeModInfo iface details Nothing

    mod = mi_module iface
    modOrig = ModOrigin (Just True) [] [] True

    addModule = modifyUnitState $ \us -> us
        { moduleNameProvidersMap = M.insert (moduleName mod) (M.singleton mod modOrig) $ moduleNameProvidersMap us
        }

    extendHpt env = env
        { hsc_unit_env = let ue = hsc_unit_env env in ue
            { ue_hpt = addHomeModInfoToHpt hmi (hsc_HPT env)
            }
        }
_______________________________________________
ghc-devs mailing list
ghc-devs@haskell.org
http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs

Reply via email to