content: {-# LANGUAGE CPP #-}
  {-# LANGUAGE BangPatterns #-}
  {-# LANGUAGE MagicHash #-}
  {-# LANGUAGE TypeApplications #-}
  {-# LANGUAGE PolyKinds #-}
  {-# LANGUAGE DataKinds #-}
  {-# LANGUAGE ScopedTypeVariables #-}
  
  -- | Utilities for the @closure_size@ tests
  module ClosureSizeUtils (assertSize, assertSizeUnlifted) where
  
  import Control.Monad
  import GHC.Exts
  import GHC.Exts.Heap.Closures
  import GHC.Stack
  import Type.Reflection
  
  profHeaderSize :: Int
  #if PROFILING
  profHeaderSize = 2
  #else
  profHeaderSize = 0
  #endif
  
  assertSize
    :: forall a. (HasCallStack, Typeable a)
    => a     -- ^ closure
    -> Int   -- ^ expected size in words
    -> IO ()
  assertSize x =
    assertSizeBox (asBox x) (typeRep @a)
  
  assertSizeUnlifted
    :: forall (a :: TYPE ('BoxedRep 'Unlifted)). (HasCallStack, Typeable a)
    => a     -- ^ closure
    -> Int   -- ^ expected size in words
    -> IO ()
  assertSizeUnlifted x =
    assertSizeBox (Box (unsafeCoerce# x)) (typeRep @a)
  
  assertSizeBox
    :: forall a. (HasCallStack)
    => Box   -- ^ closure
    -> TypeRep a
    -> Int   -- ^ expected size in words
    -> IO ()
  assertSizeBox x ty expected = do
    let !size = closureSize x
    when (size /= expected') $ do
      putStrLn $ "closureSize ("++show ty++") == "++show size++", expected "++show expected'
      putStrLn $ prettyCallStack callStack
    where expected' = expected + profHeaderSize
  {-# NOINLINE assertSize #-}
  