{-# LANGUAGE CPP, ScopedTypeVariables, LambdaCase #-}
module Panic (
GhcException(..), showGhcException,
throwGhcException, throwGhcExceptionIO,
handleGhcException,
progName,
pgmError,
panic, sorry, assertPanic, trace,
panicDoc, sorryDoc, pgmErrorDoc,
cmdLineError, cmdLineErrorIO,
Exception.Exception(..), showException, safeShowException,
try, tryMost, throwTo,
withSignalHandlers,
) where
#include "HsVersions.h"
import GhcPrelude
import {-# SOURCE #-} Outputable (SDoc, showSDocUnsafe)
import Config
import Exception
import Control.Monad.IO.Class
import Control.Concurrent
import Debug.Trace ( trace )
import System.IO.Unsafe
import System.Environment
#if !defined(mingw32_HOST_OS)
import System.Posix.Signals as S
#endif
#if defined(mingw32_HOST_OS)
import GHC.ConsoleHandler as S
#endif
import GHC.Stack
import System.Mem.Weak ( deRefWeak )
data GhcException
= Signal Int
| UsageError String
| CmdLineError String
| Panic String
| PprPanic String SDoc
| Sorry String
| PprSorry String SDoc
| InstallationError String
| ProgramError String
| PprProgramError String SDoc
instance Exception GhcException
instance Show GhcException where
showsPrec _ e@(ProgramError _) = showGhcException e
showsPrec _ e@(CmdLineError _) = showString "<command line>: " . showGhcException e
showsPrec _ e = showString progName . showString ": " . showGhcException e
progName :: String
progName = unsafePerformIO (getProgName)
{-# NOINLINE progName #-}
short_usage :: String
short_usage = "Usage: For basic information, try the `--help' option."
showException :: Exception e => e -> String
showException = show
safeShowException :: Exception e => e -> IO String
safeShowException e = do
r <- try (return $! forceList (showException e))
case r of
Right msg -> return msg
Left e' -> safeShowException (e' :: SomeException)
where
forceList [] = []
forceList xs@(x : xt) = x `seq` forceList xt `seq` xs
showGhcException :: GhcException -> ShowS
showGhcException exception
= case exception of
UsageError str
-> showString str . showChar '\n' . showString short_usage
CmdLineError str -> showString str
PprProgramError str sdoc ->
showString str . showString "\n\n" .
showString (showSDocUnsafe sdoc)
ProgramError str -> showString str
InstallationError str -> showString str
Signal n -> showString "signal: " . shows n
PprPanic s sdoc ->
panicMsg $ showString s . showString "\n\n"
. showString (showSDocUnsafe sdoc)
Panic s -> panicMsg (showString s)
PprSorry s sdoc ->
sorryMsg $ showString s . showString "\n\n"
. showString (showSDocUnsafe sdoc)
Sorry s -> sorryMsg (showString s)
where
sorryMsg :: ShowS -> ShowS
sorryMsg s =
showString "sorry! (unimplemented feature or known bug)\n"
. showString (" (GHC version " ++ cProjectVersion ++ " for " ++ TargetPlatform_NAME ++ "):\n\t")
. s . showString "\n"
panicMsg :: ShowS -> ShowS
panicMsg s =
showString "panic! (the 'impossible' happened)\n"
. showString (" (GHC version " ++ cProjectVersion ++ " for " ++ TargetPlatform_NAME ++ "):\n\t")
. s . showString "\n\n"
. showString "Please report this as a GHC bug: https://www.haskell.org/ghc/reportabug\n"
throwGhcException :: GhcException -> a
throwGhcException = Exception.throw
throwGhcExceptionIO :: GhcException -> IO a
throwGhcExceptionIO = Exception.throwIO
handleGhcException :: ExceptionMonad m => (GhcException -> m a) -> m a -> m a
handleGhcException = ghandle
panic, sorry, pgmError :: String -> a
panic x = unsafeDupablePerformIO $ do
stack <- ccsToStrings =<< getCurrentCCS x
if null stack
then throwGhcException (Panic x)
else throwGhcException (Panic (x ++ '\n' : renderStack stack))
sorry x = throwGhcException (Sorry x)
pgmError x = throwGhcException (ProgramError x)
panicDoc, sorryDoc, pgmErrorDoc :: String -> SDoc -> a
panicDoc x doc = throwGhcException (PprPanic x doc)
sorryDoc x doc = throwGhcException (PprSorry x doc)
pgmErrorDoc x doc = throwGhcException (PprProgramError x doc)
cmdLineError :: String -> a
cmdLineError = unsafeDupablePerformIO . cmdLineErrorIO
cmdLineErrorIO :: String -> IO a
cmdLineErrorIO x = do
stack <- ccsToStrings =<< getCurrentCCS x
if null stack
then throwGhcException (CmdLineError x)
else throwGhcException (CmdLineError (x ++ '\n' : renderStack stack))
assertPanic :: String -> Int -> a
assertPanic file line =
Exception.throw (Exception.AssertionFailed
("ASSERT failed! file " ++ file ++ ", line " ++ show line))
tryMost :: IO a -> IO (Either SomeException a)
tryMost action = do r <- try action
case r of
Left se ->
case fromException se of
Just (Signal _) -> throwIO se
Just (Panic _) -> throwIO se
Just _ -> return (Left se)
Nothing ->
case fromException se of
Just (_ :: IOException) ->
return (Left se)
Nothing -> throwIO se
Right v -> return (Right v)
{-# NOINLINE signalHandlersRefCount #-}
#if !defined(mingw32_HOST_OS)
signalHandlersRefCount :: MVar (Word, Maybe (S.Handler,S.Handler
,S.Handler,S.Handler))
#else
signalHandlersRefCount :: MVar (Word, Maybe S.Handler)
#endif
signalHandlersRefCount = unsafePerformIO $ newMVar (0,Nothing)
withSignalHandlers :: (ExceptionMonad m, MonadIO m) => m a -> m a
withSignalHandlers act = do
main_thread <- liftIO myThreadId
wtid <- liftIO (mkWeakThreadId main_thread)
let
interrupt = do
r <- deRefWeak wtid
case r of
Nothing -> return ()
Just t -> throwTo t UserInterrupt
#if !defined(mingw32_HOST_OS)
let installHandlers = do
let installHandler' a b = installHandler a b Nothing
hdlQUIT <- installHandler' sigQUIT (Catch interrupt)
hdlINT <- installHandler' sigINT (Catch interrupt)
let fatal_signal n = throwTo main_thread (Signal (fromIntegral n))
hdlHUP <- installHandler' sigHUP (Catch (fatal_signal sigHUP))
hdlTERM <- installHandler' sigTERM (Catch (fatal_signal sigTERM))
return (hdlQUIT,hdlINT,hdlHUP,hdlTERM)
let uninstallHandlers (hdlQUIT,hdlINT,hdlHUP,hdlTERM) = do
_ <- installHandler sigQUIT hdlQUIT Nothing
_ <- installHandler sigINT hdlINT Nothing
_ <- installHandler sigHUP hdlHUP Nothing
_ <- installHandler sigTERM hdlTERM Nothing
return ()
#else
let sig_handler ControlC = interrupt
sig_handler Break = interrupt
sig_handler _ = return ()
let installHandlers = installHandler (Catch sig_handler)
let uninstallHandlers = installHandler
#endif
let mayInstallHandlers = liftIO $ modifyMVar_ signalHandlersRefCount $ \case
(0,Nothing) -> do
hdls <- installHandlers
return (1,Just hdls)
(c,oldHandlers) -> return (c+1,oldHandlers)
let mayUninstallHandlers = liftIO $ modifyMVar_ signalHandlersRefCount $ \case
(1,Just hdls) -> do
_ <- uninstallHandlers hdls
return (0,Nothing)
(c,oldHandlers) -> return (c-1,oldHandlers)
mayInstallHandlers
act `gfinally` mayUninstallHandlers