Skip to content

Refactor Hooks and Plugins

Several parts of the compiler support hooks, either via Hooks per-se or via Plugins (which provide stackable hooks).

Hooks and Plugin types are defined as:

data Hooks = Hooks
  { dsForeignsHook         :: Maybe DsForeignsHook
  -- ^ Actual type:
  -- @Maybe ([LForeignDecl GhcTc] -> DsM (ForeignStubs, OrdList (Id, CoreExpr)))@
  , tcForeignImportsHook   :: Maybe ([LForeignDecl GhcRn]
                          -> TcM ([Id], [LForeignDecl GhcTc], Bag GlobalRdrElt))
  , tcForeignExportsHook   :: Maybe ([LForeignDecl GhcRn]
            -> TcM (LHsBinds GhcTc, [LForeignDecl GhcTc], Bag GlobalRdrElt))
  , hscFrontendHook        :: Maybe (ModSummary -> Hsc FrontendResult)
  , hscCompileCoreExprHook ::
               Maybe (HscEnv -> SrcSpan -> CoreExpr -> IO ForeignHValue)
  , ghcPrimIfaceHook       :: Maybe ModIface
  , runPhaseHook           :: Maybe (PhasePlus -> FilePath -> DynFlags
                                         -> CompPipeline (PhasePlus, FilePath))
  , runMetaHook            :: Maybe (MetaHook TcM)
  , linkHook               :: Maybe (GhcLink -> DynFlags -> Bool
                                         -> HomePackageTable -> IO SuccessFlag)
  , runRnSpliceHook        :: Maybe (HsSplice GhcRn -> RnM (HsSplice GhcRn))
  , getValueSafelyHook     :: Maybe (HscEnv -> Name -> Type
                                                          -> IO (Maybe HValue))
  , createIservProcessHook :: Maybe (CreateProcess -> IO ProcessHandle)
  , stgToCmmHook           :: Maybe (DynFlags -> Module -> [TyCon] -> CollectedCCs
                                 -> [CgStgTopBinding] -> HpcInfo -> Stream IO CmmGroup ModuleLFInfos)
  , cmmToRawCmmHook        :: forall a . Maybe (DynFlags -> Maybe Module -> Stream IO CmmGroupSRTs a
                                 -> IO (Stream IO RawCmmGroup a))
  }

data Plugin = Plugin {
    installCoreToDos :: CorePlugin
  , tcPlugin :: TcPlugin
  , holeFitPlugin :: HoleFitPlugin
  , dynflagsPlugin :: [CommandLineOption] -> DynFlags -> IO DynFlags
  , pluginRecompile :: [CommandLineOption] -> IO PluginRecompile
  , parsedResultAction :: [CommandLineOption] -> ModSummary -> HsParsedModule -> Hsc HsParsedModule
  , renamedResultAction :: [CommandLineOption] -> TcGblEnv -> HsGroup GhcRn -> TcM (TcGblEnv, HsGroup GhcRn)
  , typeCheckResultAction :: [CommandLineOption] -> ModSummary -> TcGblEnv -> TcM TcGblEnv
  , spliceRunAction :: [CommandLineOption] -> LHsExpr GhcTc -> TcM (LHsExpr GhcTc)
  , interfaceLoadAction :: forall lcl . [CommandLineOption] -> ModIface -> IfM lcl ModIface
  }

These definitions make the code not very modular because any user of these types depends on modules defining every type mentioned in the signatures of the fields.

Solution 0: accept the dependencies and use .hs-boot files to break import cycles

  • Pros:
    1. Simpler to implement (nothing to do)
  • Cons:
    1. Usual issues with import cycles and .hs-boot files
    2. Make the module graph more complex than strictly required. In particular we can't enforce invariants such as "the type-checker mustn't depend in any way on the backend and vice-versa" because any module using DynFlags/HscEnv depends on Plugin/Hooks which depend on TC stuff and backend stuff.

Solution 1: keep the types of the hooks abstract using type families

For each hook, a type family is defined type family MyHook :: Type. Then an instance is defined in another module (e.g., in the module using the hook).

  • Pros:
    1. only codes interested in using a hook have to import the module with the type instance.
  • Cons:
    1. the list of hooks is fixed and can't be extended via ghc-api/plugins

DsForeignsHook already uses this method to avoid making the parser depend on the desugarer (cf CountParserDeps test). !4047 (closed) generalizes this approach to all hooks.

Solution 2: store hooks as Dynamic values.

  • Pros:
    1. only codes interested in using a hook have to import the module defining the hook type
    2. the list of hooks can be extended by plugins/ghc-api
  • Cons:
    1. We rely on introspection to extract hooks

!4363 (closed) implements this (only for hooks for now)

Edited by Sylvain Henry
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information