packages feed

hs-bindgen-1.0.0.0: src-internal/HsBindgen/Frontend/Analysis/Deps.hs

module HsBindgen.Frontend.Analysis.Deps (
    depsOfDecl
    -- * Structs and unions
  , depsOfStruct
  , depsOfUnion
  , depsOfField
  , depsOfRegularField
    -- * Parsed macros
  , depsOfDeclParsedMacro
  ) where

import Data.Proxy (Proxy (..))

import HsBindgen.Frontend.Analysis
import HsBindgen.Frontend.Pass.ReparseMacroExpansions.IsPass
import HsBindgen.Frontend.Pass.TypecheckMacros.IsPass
import HsBindgen.IR.C qualified as C
import HsBindgen.IR.Pass
import HsBindgen.Macro.Interface qualified as Macro

{-------------------------------------------------------------------------------
  Get all dependencies
-------------------------------------------------------------------------------}


depsOfDeclWith ::
     forall l p. IsPass p
  => (MacroBody p l -> [(Id p, Dependency)])
  -> C.DeclKind l p
  -> [(Id p, Dependency)]
depsOfDeclWith depsOfMacro = \case
    (C.DeclStruct struct)          -> depsOfStruct struct
    (C.DeclUnion union)            -> depsOfUnion union
    (C.DeclEnum _)                 -> []
    (C.DeclUntaggedEnumConstant _) -> []
    (C.DeclTypedef ty)             -> depsOfTypedef ty
    C.DeclOpaque{}                 -> []
    (C.DeclMacro m)                -> depsOfMacro m
    (C.DeclFunction function)      -> C.depsOfType (C.typeOfFunction function)
    (C.DeclGlobal global)          -> C.depsOfType (cType (Proxy @p) global.typ)

{-------------------------------------------------------------------------------
  Dependencies of declarations with parsed macros only
-------------------------------------------------------------------------------}

-- | For parsed macros that have not been typechecked yet, we need to resolve
--   names, using all available declaration IDs
depsOfDeclParsedMacro ::
     forall l p.
     ( IsPass p
     , MacroBody p ~ Macro.Resolved
     , Id p ~ C.DeclId )
  => C.DeclKind l p
  -> [(Id p, Dependency)]
depsOfDeclParsedMacro = depsOfDeclWith (.deps)

{-------------------------------------------------------------------------------
  Dependencies of declaration after @ReparseMacroExpansions@
-------------------------------------------------------------------------------}

-- | Get all dependencies of a declaration in the 'ReparseMacroExpansions' pass
--
-- Disclaimer: For a specific declaration, we can only determine the full set of
-- dependencies after we have reparsed this declaration, because it may contain
-- macro expansions. This step happens in the @ReparseMacroExpansions@ pass.
--
-- Before reparsing, the dependencies of declarations with macro expansions can
-- only refer to the /underlying types/ of the expanded macros.
--
depsOfDecl ::
     C.DeclKind l ReparseMacroExpansions
  -> [(C.DeclId, Dependency)]
depsOfDecl = depsOfDeclTcMacro

depsOfDeclTcMacro ::
     C.DeclKind l ReparseMacroExpansions
  -> [(C.DeclId, Dependency)]
depsOfDeclTcMacro = depsOfDeclWith typecheckedMacroDeps

-- | Dependencies of typechecked macro declarations
typecheckedMacroDeps ::
     TypecheckedMacro ReparseMacroExpansions l
  -> [(C.DeclId, Dependency)]
typecheckedMacroDeps = \case
    MacroType  typ -> typ.deps
    MacroValue val -> val.deps

{-------------------------------------------------------------------------------
  Structs and unions
-------------------------------------------------------------------------------}

depsOfStruct :: IsPass p => C.Struct p -> [(Id p, Dependency)]
depsOfStruct struct = concat [
      concatMap depsOfField struct.fields
    , foldMap   depsOfRegularField (C.flamStructField struct.flam)
    ]

depsOfUnion :: IsPass p => C.Union p -> [(Id p, Dependency)]
depsOfUnion union = concatMap depsOfField union.fields

-- | Dependencies of struct or union field
depsOfField ::
     forall p. IsPass p
  => C.Field p
  -> [(Id p, Dependency)]
depsOfField = C.elimField depsOfRegularField depsOfImplicitField

depsOfRegularField ::
     forall p. IsPass p
  => C.RegularField p
  -> [(Id p, Dependency)]
depsOfRegularField field = C.depsOfType (cType (Proxy @p) field.typ)

depsOfImplicitField ::
     forall p. IsPass p
  => C.ImplicitField p
  -> [(Id p, Dependency)]
depsOfImplicitField field = C.depsOfType (cType (Proxy @p) field.typ)
    ++ concatMap depsOfIndirectField field.indirect

depsOfIndirectField ::
     forall p. IsPass p
  => C.IndirectField p
  -> [(Id p, Dependency)]
depsOfIndirectField field = C.depsOfType (cType (Proxy @p) field.typ)

{-------------------------------------------------------------------------------
  Typedefs
-------------------------------------------------------------------------------}

depsOfTypedef :: forall p. IsPass p => C.Typedef p -> [(Id p, Dependency)]
depsOfTypedef typedef = C.depsOfType (cType (Proxy @p) typedef.typ)