{-# LANGUAGE MultiWayIf #-}

module GHC.HsToCore.Foreign.Utils
  ( Binding
  , getPrimTyOf
  , primTyDescChar
  , ppPrimTyConStgType
  )
where

import GHC.Prelude

import GHC.Platform

import GHC.Tc.Utils.TcType

import GHC.Core (CoreExpr)
import GHC.Core.DataCon
import GHC.Core.TyCon
import GHC.Core.TyCo.Rep

import GHC.Types.Id
import GHC.Types.RepType

import GHC.Builtin.Types
import GHC.Builtin.Types.Prim

import GHC.Utils.Outputable
import GHC.Utils.Panic

type Binding = (Id, CoreExpr) -- No rec/nonrec structure;
                              -- the occurrence analyser will sort it all out

-- This function returns the primitive type associated with the boxed
-- type argument to a foreign export (eg. Int ==> Int#).
getPrimTyOf :: Type -> UnaryType
getPrimTyOf ty
  | isBoolTy rep_ty = intPrimTy
  -- Except for Bool, the types we are interested in have a single constructor
  -- with a single primitive-typed argument (see TcType.legalFEArgTyCon).
  | otherwise =
  case splitDataProductType_maybe rep_ty of
     Just (_, _, data_con, [Scaled _ prim_ty]) ->
        assert (dataConSourceArity data_con == 1) $
        assertPpr (isUnliftedType prim_ty) (ppr prim_ty)
          -- NB: it's OK to call isUnliftedType here, as we don't allow
          -- representation-polymorphic types in foreign import/export declarations
        prim_ty
     _other -> pprPanic "getPrimTyOf" (ppr ty)
  where
        rep_ty = unwrapType ty

-- represent a primitive type as a Char, for building a string that
-- described the foreign function type.  The types are size-dependent,
-- e.g. 'W' is a signed 32-bit integer.
primTyDescChar :: Platform -> Type -> Char
primTyDescChar !platform ty
 | ty `eqType` unitTy = 'v'
 | otherwise
 = case typePrimRepU (getPrimTyOf ty) of
     IntRep      -> signed_word
     WordRep     -> unsigned_word
     Int8Rep     -> 'B'
     Word8Rep    -> 'b'
     Int16Rep    -> 'S'
     Word16Rep   -> 's'
     Int32Rep    -> 'W'
     Word32Rep   -> 'w'
     Int64Rep    -> 'L'
     Word64Rep   -> 'l'
     AddrRep     -> 'p'
     FloatRep    -> 'f'
     DoubleRep   -> 'd'
     _           -> pprPanic "primTyDescChar" (ppr ty)
  where
    (signed_word, unsigned_word) = case platformWordSize platform of
      PW4 -> ('W','w')
      PW8 -> ('L','l')

-- | Printed C Type to be used with CAPI calling convention
ppPrimTyConStgType :: TyCon -> Maybe String
ppPrimTyConStgType tc =
  if | tc == charPrimTyCon -> Just "StgChar"
     | tc == intPrimTyCon -> Just "StgInt"
     | tc == int8PrimTyCon -> Just "StgInt8"
     | tc == int16PrimTyCon -> Just "StgInt16"
     | tc == int32PrimTyCon -> Just "StgInt32"
     | tc == int64PrimTyCon -> Just "StgInt64"
     | tc == wordPrimTyCon -> Just "StgWord"
     | tc == word8PrimTyCon -> Just "StgWord8"
     | tc == word16PrimTyCon -> Just "StgWord16"
     | tc == word32PrimTyCon -> Just "StgWord32"
     | tc == word64PrimTyCon -> Just "StgWord64"
     | tc == floatPrimTyCon -> Just "StgFloat"
     | tc == doublePrimTyCon -> Just "StgDouble"
     | tc == addrPrimTyCon -> Just "StgAddr"
     | tc == stablePtrPrimTyCon -> Just "StgStablePtr"
     | tc == arrayPrimTyCon -> Just "const StgAddr"
     | tc == mutableArrayPrimTyCon -> Just "StgAddr"
     | tc == byteArrayPrimTyCon -> Just "const StgAddr"
     | tc == mutableByteArrayPrimTyCon -> Just "StgAddr"
     | tc == smallArrayPrimTyCon -> Just "const StgAddr"
     | tc == smallMutableArrayPrimTyCon -> Just "StgAddr"
     | otherwise -> Nothing
