packages feed

hs-bindgen-1.0.0.0: src-internal/HsBindgen/Frontend/Pass/ResolveBindingSpecs/IsPass.hs

module HsBindgen.Frontend.Pass.ResolveBindingSpecs.IsPass (
    ResolveBindingSpecs
  , PrescriptiveDeclSpec(..)
  , ResolveBindingSpecsMsg(..)
  ) where

import Text.SimplePrettyPrint ((<+>))

import HsBindgen.BindingSpec qualified as BindingSpec
import HsBindgen.Frontend.Pass.ReparseMacroExpansions.IsPass
import HsBindgen.Frontend.Pass.TypecheckMacros.IsPass
import HsBindgen.Imports
import HsBindgen.IR.C qualified as C
import HsBindgen.IR.Pass
import HsBindgen.Language.Haskell qualified as Hs
import HsBindgen.Util.Tracer

{-------------------------------------------------------------------------------
  Definition
-------------------------------------------------------------------------------}

-- | Resolve binding specifications
--
-- For every C name, we resolve:
--
-- * External binding specification, which is used to specify existing bindings
--   that should be used, external from the module being generated.  Matching
--   types are replaced with external references, and matching declarations are
--   removed from the AST.
-- * Prescriptive binding specification, which is used to configure how bindings
--   are generated.  This information is added to the AST as annotations.
type ResolveBindingSpecs :: Pass
data ResolveBindingSpecs a

type family AnnResolveBindingSpecs ix where
  AnnResolveBindingSpecs "Decl" = PrescriptiveDeclSpec
  AnnResolveBindingSpecs _      = NoAnn

instance IsPass ResolveBindingSpecs

instance PassId ResolveBindingSpecs

instance PassScopedName ResolveBindingSpecs

instance PassTypes ResolveBindingSpecs

instance PassMacro ResolveBindingSpecs where
  type MacroId         ResolveBindingSpecs = Id ResolveBindingSpecs
  type MacroBody       ResolveBindingSpecs = TypecheckedMacro ResolveBindingSpecs
  type MacroUnderlying ResolveBindingSpecs = C.Type ResolveBindingSpecs

  macroIdId _ = id

instance PassExtBinding ResolveBindingSpecs where
  type ExtBinding ResolveBindingSpecs = BindingSpec.ResolvedExtBinding

  extBindingId _ = (.cName)

instance PassCommentDecl ResolveBindingSpecs where
  type CommentDecl ResolveBindingSpecs = Maybe (C.Comment ResolveBindingSpecs)

instance PassAnn ResolveBindingSpecs where
  type Ann ix ResolveBindingSpecs = AnnResolveBindingSpecs ix

instance PassMsg ResolveBindingSpecs where
  type Msg ResolveBindingSpecs = ResolveBindingSpecsMsg

-- | Prescriptive binding specification for declaration
--
-- Although we have interpreted /part/ of this binding specification during
-- name mangling, we leave the /full/ binding specification in the AST, because
-- we need it when we /generate/ the output binding specification.
--
-- TODO <https://github.com/well-typed/hs-bindgen/issues/1770>
-- If we have binding specs for different kinds of things (types, functions, ..)
-- we may want to have different types for those kinds of specs, and then
-- associate the appropriate type with specific 'HsBindgen.Frontend.AST.Decl.DeclKind's (rather than this
-- "catch all" type with the top-level 'HsBindgen.Frontend.AST.Decl.Decl').
data PrescriptiveDeclSpec = PrescriptiveDeclSpec {
      cSpec  :: Maybe BindingSpec.CTypeSpec
    , hsSpec :: Maybe BindingSpec.HsTypeSpec
    }
  deriving stock (Show, Eq, Generic)

{-------------------------------------------------------------------------------
  Trace messages
-------------------------------------------------------------------------------}

data ResolveBindingSpecsMsg =
    ResolveBindingSpecsModuleMismatch       Hs.ModuleName Hs.ModuleName
  | ResolveBindingSpecsEnumTypeMismatch     C.DeclId
  | ResolveBindingSpecsExtHsRefNoIdentifier C.DeclId
  | ResolveBindingSpecsNoHsTypeSpec         C.DeclId
  | ResolveBindingSpecsOmittedType          C.DeclId
  | ResolveBindingSpecsTypeNotUsed          C.DeclId
  | ResolveBindingSpecsExtDecl              C.DeclId
  | ResolveBindingSpecsExtType              C.DeclId C.DeclId
  | ResolveBindingSpecsPreRequire           C.DeclId
  | ResolveBindingSpecsPreOmit              C.DeclId
  | ResolveBindingSpecsPreEmptyData         C.DeclId
  | ResolveBindingSpecsPreEmptyDataInvalid  C.DeclId
  | ResolveBindingSpecsIndirectFieldDropped C.DeclId C.ScopedName
  deriving stock (Show)

instance PrettyForTrace ResolveBindingSpecsMsg where
  prettyForTrace = \case
      ResolveBindingSpecsModuleMismatch hsModuleName pSpecHsModuleName ->
        "Prescriptive binding specification for module"
          <+> prettyForTrace pSpecHsModuleName
          <+> "cannot be used to generate"
          <+> prettyForTrace hsModuleName
      ResolveBindingSpecsEnumTypeMismatch cDeclId ->
        "C enum specification for non-enum type:"
          <+> prettyForTrace cDeclId
      ResolveBindingSpecsExtHsRefNoIdentifier cDeclId ->
        "Haskell identifier not specified in binding specification:"
          <+> prettyForTrace cDeclId
      ResolveBindingSpecsNoHsTypeSpec cDeclId ->
        "Haskell type spec not specified in binding specification:"
          <+> prettyForTrace cDeclId
      ResolveBindingSpecsOmittedType cDeclId ->
        "Type omitted by binding specification used:"
          <+> prettyForTrace cDeclId
      ResolveBindingSpecsTypeNotUsed cDeclId ->
        "Binding specification for type not used:"
          <+> prettyForTrace cDeclId
      ResolveBindingSpecsExtDecl cDeclId ->
        "Declaration with external binding dropped:"
          <+> prettyForTrace cDeclId
      ResolveBindingSpecsExtType ctx cDeclId ->
        "Within declaration"
          <+> prettyForTrace ctx
          <+> "type replaced with external binding:"
          <+> prettyForTrace cDeclId
      ResolveBindingSpecsPreRequire cDeclId ->
        "Prescriptive binding specification found:"
          <+> prettyForTrace cDeclId
      ResolveBindingSpecsPreOmit cDeclId ->
        "Declaration omitted by prescriptive binding specification:"
          <+> prettyForTrace cDeclId
      ResolveBindingSpecsPreEmptyData cDeclId ->
        "Declaration opaqued by prescriptive binding specification:"
          <+> prettyForTrace cDeclId
      ResolveBindingSpecsPreEmptyDataInvalid cDeclId ->
        "Declaration opaqued by prescriptive binding specification invalid for kind:"
          <+> prettyForTrace cDeclId
      ResolveBindingSpecsIndirectFieldDropped cDeclId cFieldName ->
        "Within declaration"
          <+> prettyForTrace cDeclId
          <+> "dropped an indirect field that points into a dropped declaration"
          <+> ":"
          <+> prettyForTrace cFieldName

instance IsTrace Level ResolveBindingSpecsMsg where
  getDefaultLogLevel = \case
    ResolveBindingSpecsModuleMismatch{}       -> Warning
    ResolveBindingSpecsEnumTypeMismatch{}     -> Warning
    ResolveBindingSpecsExtHsRefNoIdentifier{} -> Warning
    ResolveBindingSpecsNoHsTypeSpec{}         -> Warning
    ResolveBindingSpecsOmittedType{}          -> Info
    ResolveBindingSpecsTypeNotUsed{}          -> Warning
    ResolveBindingSpecsExtDecl{}              -> Info
    ResolveBindingSpecsExtType{}              -> Info
    ResolveBindingSpecsPreRequire{}           -> Info
    ResolveBindingSpecsPreOmit{}              -> Info
    ResolveBindingSpecsPreEmptyData{}         -> Info
    ResolveBindingSpecsPreEmptyDataInvalid{}  -> Warning
    ResolveBindingSpecsIndirectFieldDropped{} -> Warning
  getSource          = const HsBindgen
  getTraceId         = const "resolve-binding-specs"

{-------------------------------------------------------------------------------
  CoercePass
-------------------------------------------------------------------------------}

instance CoercePassId               ReparseMacroExpansions ResolveBindingSpecs
instance CoercePassMacroId          ReparseMacroExpansions ResolveBindingSpecs
instance CoercePassAnn "TypeFunArg" ReparseMacroExpansions ResolveBindingSpecs

instance CoercePassCommentDecl ReparseMacroExpansions ResolveBindingSpecs where
  coercePassCommentDecl _ = fmap coercePass