# LANGUAGE CPP #
#if nothunks
{-# LANGUAGE BangPatterns              #-}
#endif
#if ghcDebug
#else
# LANGUAGE ExistentialQuantification #
#endif

module Vehicle.Debug
  ( -- * Export 'ghc-debug-stub'
    Box (Box),
    saveClosures,
    pause,
    resume,

  * Export '  '
    unsafeCheckThunks,
  )
where

-- Import ghc-debug-stub:
#if ghcDebug
import GHC.Debug.Stub (Box (Box), pause, resume, saveClosures)
#endif

-- Import nothunks and helpers:
#if nothunks
import GHC.Stack (HasCallStack, callStack)
import NoThunks.Class (NoThunks, unsafeNoThunks)
#if ghcDebug
import Control.Monad (void)
import System.IO.Unsafe (unsafePerformIO)
#endif
#endif

  Define ghc - debug operators if needed :
#if ghcDebug
  The operators are imported from GHC.Debug . Stub ,
-- so we do not need to define anything.
#else
data Box = forall a . Box a

saveClosures :: [Box] -> IO ()
saveClosures _ = return ()
# INLINE saveClosures #

pause :: IO ()
pause = return ()
# INLINE pause #

resume :: IO ()
resume = return ()
# INLINE resume #
#endif

-- Define unsafeCheckThunks:
#if nothunks
#if ghcDebug
  If nothunks is defined &  is defined :
  If we find a thunk , we pass it to ghc - debug , and pause execution .
unsafeCheckThunks :: (HasCallStack, NoThunks a) => a -> a
unsafeCheckThunks !x = case unsafeNoThunks x of
    Nothing    -> x
    Just thunk ->
      case unsafePerformIO $ do
        putStrLn $
               "THUNK ALERT:\n    "
            ++ show thunk ++ "\n    "
            ++ show callStack ++ "\n"
            ++ "pausing for ghc-debug analysis"
        saveClosures [Box x]
        void getLine
      of
        () -> x
#else
  If nothunks is defined & ghcDebug is NOT defined :
-- If we find a thunk, we throw an error.
unsafeCheckThunks :: (HasCallStack, NoThunks a) => a -> a
unsafeCheckThunks !x = case unsafeNoThunks x of
    Nothing    -> x
    Just thunk -> error $ unlines
      [ "Found a thunk: ",
        "- " <> show thunk,
        "- " <> show callStack
      ]
# INLINE unsafeCheckThunks #
#endif
#else
  If  is NOT defined :
-- We cannot check for thunks, so we do nothing.
unsafeCheckThunks :: a -> a
unsafeCheckThunks x = x
# INLINE unsafeCheckThunks #
#endif

# LANGUAGE BangPatterns              #
 * Export 'ghc-debug-stub'
 Import ghc-debug-stub:
 Import nothunks and helpers:
 so we do not need to define anything.
 Define unsafeCheckThunks:
 If we find a thunk, we throw an error.
 We cannot check for thunks, so we do nothing.
# LANGUAGE CPP #
#if nothunks
#endif
#if ghcDebug
#else
# LANGUAGE ExistentialQuantification #
#endif

module Vehicle.Debug
    Box (Box),
    saveClosures,
    pause,
    resume,

  * Export '  '
    unsafeCheckThunks,
  )
where

#if ghcDebug
import GHC.Debug.Stub (Box (Box), pause, resume, saveClosures)
#endif

#if nothunks
import GHC.Stack (HasCallStack, callStack)
import NoThunks.Class (NoThunks, unsafeNoThunks)
#if ghcDebug
import Control.Monad (void)
import System.IO.Unsafe (unsafePerformIO)
#endif
#endif

  Define ghc - debug operators if needed :
#if ghcDebug
  The operators are imported from GHC.Debug . Stub ,
#else
data Box = forall a . Box a

saveClosures :: [Box] -> IO ()
saveClosures _ = return ()
# INLINE saveClosures #

pause :: IO ()
pause = return ()
# INLINE pause #

resume :: IO ()
resume = return ()
# INLINE resume #
#endif

#if nothunks
#if ghcDebug
  If nothunks is defined &  is defined :
  If we find a thunk , we pass it to ghc - debug , and pause execution .
unsafeCheckThunks :: (HasCallStack, NoThunks a) => a -> a
unsafeCheckThunks !x = case unsafeNoThunks x of
    Nothing    -> x
    Just thunk ->
      case unsafePerformIO $ do
        putStrLn $
               "THUNK ALERT:\n    "
            ++ show thunk ++ "\n    "
            ++ show callStack ++ "\n"
            ++ "pausing for ghc-debug analysis"
        saveClosures [Box x]
        void getLine
      of
        () -> x
#else
  If nothunks is defined & ghcDebug is NOT defined :
unsafeCheckThunks :: (HasCallStack, NoThunks a) => a -> a
unsafeCheckThunks !x = case unsafeNoThunks x of
    Nothing    -> x
    Just thunk -> error $ unlines
      [ "Found a thunk: ",
        "- " <> show thunk,
        "- " <> show callStack
      ]
# INLINE unsafeCheckThunks #
#endif
#else
  If  is NOT defined :
unsafeCheckThunks :: a -> a
unsafeCheckThunks x = x
# INLINE unsafeCheckThunks #
#endif
