packages feed

polytropos-plugin-0.1.0.2: src/Polytropos/Plugin/Compat.hs

{-# LANGUAGE CPP #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ViewPatterns #-}

#if __GLASGOW_HASKELL__ >= 914
{-# OPTIONS_GHC -Wno-pattern-namespace-specifier #-}
#endif

-- | Compatibility layer for GHC 9.6 through 9.14.
--
-- Isolates all compiler AST and builtin namespace churn behind a uniform
-- interface, keeping the solver and source desugaring logic clean.
module Polytropos.Plugin.Compat
  ( modBase
  , modShow
  , modRecords
  , CompatRecUpdField
  , matchRecordUpd
  , extractRecUpdField
  , pattern PrefixConPat
  , stripHsPar
  , polyLabel
  , mkPolytroposImportDecl
  , mkHsApp
  , mkTupArg
  , mkLambda
  ) where

import GHC.Unit.Types
  ( mkModule
  , moduleUnit
  )

import GHC.Plugins
  ( Module
  , FastString
  , unLoc
  , mkModuleName
  , rdrNameOcc
  , occNameFS
  , RawPkgQual(..)
  )

import GHC.Hs.Extension
  ( GhcPs
  )

import GHC.Hs
  ( LHsExpr
  , LHsRecUpdField
  , LImportDecl
  , ImportDecl(..)
  , ImportDeclQualifiedStyle(..)
  , IsBootInterface(..)
  , noAnn
  , noLocA
#if __GLASGOW_HASKELL__ >= 910
  , noExtField
#endif
  , HsExpr(..)
  , HsFieldBind(..)
  , HsConDetails(..)
  , LPat
  , mkHsLam
#if __GLASGOW_HASKELL__ >= 908
  , LHsRecUpdFields(..)
#endif
#if __GLASGOW_HASKELL__ >= 914
  , ImportDeclLevelStyle(..)
#endif
#if __GLASGOW_HASKELL__ >= 912
  , FieldOcc(..)
#else
  , AmbiguousFieldOcc(..)
#endif
  )

import Language.Haskell.Syntax.Expr
  ( HsTupArg(..)
  )

import GHC.Hs.ImpExp
  ( XImportDeclPass(..)
  )

import GHC.Types.SourceText
  ( SourceText(..)
  )

#if __GLASGOW_HASKELL__ >= 910
import GHC.Builtin.Names
  ( gHC_INTERNAL_BASE
  , gHC_INTERNAL_SHOW
  )
#else
import GHC.Builtin.Names
  ( gHC_BASE
  , gHC_SHOW
  )
#endif

-------------------------------------------------------------------------------
-- Module locations for base / show / records
-------------------------------------------------------------------------------

-- | Location of GHC.Base (or GHC.Internal.Base in 9.10+)
modBase :: Module
#if __GLASGOW_HASKELL__ >= 910
modBase = gHC_INTERNAL_BASE
#else
modBase = gHC_BASE
#endif

-- | Location of GHC.Show (or GHC.Internal.Show in 9.10+)
modShow :: Module
#if __GLASGOW_HASKELL__ >= 910
modShow = gHC_INTERNAL_SHOW
#else
modShow = gHC_SHOW
#endif

-- | Location of GHC.Records (or GHC.Internal.Records in 9.10+)
modRecords :: Module
#if __GLASGOW_HASKELL__ >= 910
modRecords = mkModule (moduleUnit modBase) (mkModuleName "GHC.Internal.Records")
#else
modRecords = mkModule (moduleUnit modBase) (mkModuleName "GHC.Records")
#endif

-------------------------------------------------------------------------------
-- Record Update Compatibility (GHC 9.6 vs 9.8 vs 9.14)
-------------------------------------------------------------------------------

#if __GLASGOW_HASKELL__ >= 908
type CompatRecUpdField = LHsRecUpdField GhcPs GhcPs
#else
type CompatRecUpdField = LHsRecUpdField GhcPs
#endif

-- | Extract the updated record expression and fields from RecordUpd.
matchRecordUpd :: HsExpr GhcPs -> Maybe (LHsExpr GhcPs, [CompatRecUpdField])
#if __GLASGOW_HASKELL__ >= 908
matchRecordUpd (RecordUpd _ r (RegularRecUpdFields _ flds)) = Just (r, flds)
matchRecordUpd _ = Nothing
#else
matchRecordUpd (RecordUpd _ r (Left flds)) = Just (r, flds)
matchRecordUpd _ = Nothing
#endif

-- | Extract the label FastString and value expression from a record update field.
extractRecUpdField :: CompatRecUpdField -> (FastString, LHsExpr GhcPs)
#if __GLASGOW_HASKELL__ >= 912
extractRecUpdField (unLoc -> HsFieldBind _ (unLoc -> FieldOcc _ (unLoc -> lbl)) arg _) =
  (occNameFS (rdrNameOcc lbl), arg)
#else
extractRecUpdField (unLoc -> HsFieldBind _ (unLoc -> ambOcc) arg _) =
  let lbl = case ambOcc of
        Unambiguous _ (unLoc -> rdr) -> rdr
        Ambiguous _ (unLoc -> rdr)   -> rdr
  in (occNameFS (rdrNameOcc lbl), arg)
#endif

-------------------------------------------------------------------------------
-- Pattern & Expression AST Compatibility
-------------------------------------------------------------------------------

-- | Pattern synonym matching PrefixCon constructor details across GHC versions.
#if __GLASGOW_HASKELL__ >= 914
pattern PrefixConPat :: [arg] -> HsConDetails arg rec
pattern PrefixConPat args = PrefixCon args
#else
pattern PrefixConPat :: [arg] -> HsConDetails tyarg arg rec
pattern PrefixConPat args <- PrefixCon _ args
  where PrefixConPat args = PrefixCon [] args
#endif

-- | Strip enclosing parenthesis expressions across GHC versions.
stripHsPar :: LHsExpr GhcPs -> LHsExpr GhcPs
#if __GLASGOW_HASKELL__ >= 910
stripHsPar (unLoc -> HsPar _ e) = stripHsPar e
#else
stripHsPar (unLoc -> HsPar _ _ e _) = stripHsPar e
#endif
stripHsPar e = e

-- | Construct an overloaded label expression across GHC 9.6-9.14.
polyLabel :: FastString -> LHsExpr GhcPs
#if __GLASGOW_HASKELL__ >= 912
polyLabel fs = noLocA (HsOverLabel NoSourceText fs)
#elif __GLASGOW_HASKELL__ >= 910
polyLabel fs = noLocA (HsOverLabel noExtField NoSourceText fs)
#else
polyLabel fs = noLocA (HsOverLabel noAnn NoSourceText fs)
#endif

-- | Construct the 'import qualified Polytropos as Polytropos' declaration.
mkPolytroposImportDecl :: LImportDecl GhcPs
#if __GLASGOW_HASKELL__ >= 914
mkPolytroposImportDecl = noLocA $ ImportDecl
  { ideclExt       = XImportDeclPass noAnn NoSourceText False
  , ideclName      = noLocA (mkModuleName "Polytropos")
  , ideclPkgQual   = NoRawPkgQual
  , ideclSource    = NotBoot
  , ideclLevelSpec = NotLevelled
  , ideclSafe      = False
  , ideclQualified = QualifiedPre
  , ideclAs        = Just (noLocA (mkModuleName "Polytropos"))
  , ideclImportList = Nothing
  }
#else
mkPolytroposImportDecl = noLocA $ ImportDecl
  { ideclExt       = XImportDeclPass noAnn NoSourceText False
  , ideclName      = noLocA (mkModuleName "Polytropos")
  , ideclPkgQual   = NoRawPkgQual
  , ideclSource    = NotBoot
  , ideclSafe      = False
  , ideclQualified = QualifiedPre
  , ideclAs        = Just (noLocA (mkModuleName "Polytropos"))
  , ideclImportList = Nothing
  }
#endif

-- | Construct a function application expression across GHC 9.6-9.14.
mkHsApp :: LHsExpr GhcPs -> LHsExpr GhcPs -> LHsExpr GhcPs
#if __GLASGOW_HASKELL__ >= 910
mkHsApp f x = noLocA (HsApp noExtField f x)
#else
mkHsApp f x = noLocA (HsApp noAnn f x)
#endif

-- | Construct a tuple present argument across GHC 9.6-9.14.
mkTupArg :: LHsExpr GhcPs -> HsTupArg GhcPs
#if __GLASGOW_HASKELL__ >= 910
mkTupArg e = Present noExtField e
#else
mkTupArg e = Present noAnn e
#endif

-- | Construct a lambda expression across GHC 9.6-9.14.
mkLambda :: [LPat GhcPs] -> LHsExpr GhcPs -> LHsExpr GhcPs
#if __GLASGOW_HASKELL__ >= 912
mkLambda pats body = mkHsLam (noLocA pats) body
#else
mkLambda pats body = mkHsLam pats body
#endif