GHC/IO/Exception.hs

{-# LANGUAGE Trustworthy #-}
{-# LANGUAGE NoImplicitPrelude, DeriveDataTypeable, MagicHash,
 ExistentialQuantification #-}
{-# OPTIONS_GHC -funbox-strict-fields #-}
{-# OPTIONS_HADDOCK hide #-}

-----------------------------------------------------------------------------
-- |
-- Module : GHC.IO.Exception
-- Copyright : (c) The University of Glasgow, 2009
-- License : see libraries/base/LICENSE
--
-- Maintainer : libraries@haskell.org
-- Stability : internal
-- Portability : non-portable
--
-- IO-related Exception types and functions
--
-----------------------------------------------------------------------------

module GHC.IO.Exception (
 BlockedIndefinitelyOnMVar(..), blockedIndefinitelyOnMVar,
 BlockedIndefinitelyOnSTM(..), blockedIndefinitelyOnSTM,
 Deadlock(..),
 AssertionFailed(..),

 SomeAsyncException(..),
 asyncExceptionToException, asyncExceptionFromException,
 AsyncException(..), stackOverflow, heapOverflow,

 ArrayException(..),
 ExitCode(..),

 ioException,
 ioError,
 IOError,
 IOException(..),
 IOErrorType(..),
 userError,
 assertError,
 unsupportedOperation,
 untangle,
 ) where

import GHC.Base
import GHC.List
import GHC.IO
import GHC.Show
import GHC.Read
import GHC.Exception
import Data.Maybe
import GHC.IO.Handle.Types
import Foreign.C.Types

import Data.Typeable ( Typeable, cast )

-- ------------------------------------------------------------------------
-- Exception datatypes and operations

-- |The thread is blocked on an @MVar@, but there are no other references
-- to the @MVar@ so it can't ever continue.
data BlockedIndefinitelyOnMVar = BlockedIndefinitelyOnMVar
 deriving Typeable

instance Exception BlockedIndefinitelyOnMVar

instance Show BlockedIndefinitelyOnMVar where
 showsPrec _ BlockedIndefinitelyOnMVar = showString "thread blocked indefinitely in an MVar operation"

blockedIndefinitelyOnMVar :: SomeException -- for the RTS
blockedIndefinitelyOnMVar = toException BlockedIndefinitelyOnMVar

-----

-- |The thread is waiting to retry an STM transaction, but there are no
-- other references to any @TVar@s involved, so it can't ever continue.
data BlockedIndefinitelyOnSTM = BlockedIndefinitelyOnSTM
 deriving Typeable

instance Exception BlockedIndefinitelyOnSTM

instance Show BlockedIndefinitelyOnSTM where
 showsPrec _ BlockedIndefinitelyOnSTM = showString "thread blocked indefinitely in an STM transaction"

blockedIndefinitelyOnSTM :: SomeException -- for the RTS
blockedIndefinitelyOnSTM = toException BlockedIndefinitelyOnSTM

-----

-- |There are no runnable threads, so the program is deadlocked.
-- The @Deadlock@ exception is raised in the main thread only.
data Deadlock = Deadlock
 deriving Typeable

instance Exception Deadlock

instance Show Deadlock where
 showsPrec _ Deadlock = showString "<<deadlock>>"

-----

-- |'assert' was applied to 'False'.
data AssertionFailed = AssertionFailed String
 deriving Typeable

instance Exception AssertionFailed

instance Show AssertionFailed where
 showsPrec _ (AssertionFailed err) = showString err

-----

-- |Superclass for asynchronous exceptions.
--
-- /Since: 4.7.0.0/
data SomeAsyncException = forall e . Exception e => SomeAsyncException e
 deriving Typeable

instance Show SomeAsyncException where
 show (SomeAsyncException e) = show e

 instance Exception SomeAsyncException

-- |/Since: 4.7.0.0/
asyncExceptionToException :: Exception e => e -> SomeException
asyncExceptionToException = toException . SomeAsyncException

-- |/Since: 4.7.0.0/
asyncExceptionFromException :: Exception e => SomeException -> Maybe e
asyncExceptionFromException x = do
 SomeAsyncException a <- fromException x
 cast a


-- |Asynchronous exceptions.
data AsyncException
 = StackOverflow
 -- ^The current thread\'s stack exceeded its limit.
 -- Since an exception has been raised, the thread\'s stack
 -- will certainly be below its limit again, but the
 -- programmer should take remedial action
 -- immediately.
 | HeapOverflow
 -- ^The program\'s heap is reaching its limit, and
 -- the program should take action to reduce the amount of
 -- live data it has. Notes:
 --
 -- * It is undefined which thread receives this exception.
 --
 -- * GHC currently does not throw 'HeapOverflow' exceptions.
 | ThreadKilled
 -- ^This exception is raised by another thread
 -- calling 'Control.Concurrent.killThread', or by the system
 -- if it needs to terminate the thread for some
 -- reason.
 | UserInterrupt
 -- ^This exception is raised by default in the main thread of
 -- the program when the user requests to terminate the program
 -- via the usual mechanism(s) (e.g. Control-C in the console).
 deriving (Eq, Ord, Typeable)

instance Exception AsyncException where
 toException = asyncExceptionToException
 fromException = asyncExceptionFromException

-- | Exceptions generated by array operations
data ArrayException
 = IndexOutOfBounds String
 -- ^An attempt was made to index an array outside
 -- its declared bounds.
 | UndefinedElement String
 -- ^An attempt was made to evaluate an element of an
 -- array that had not been initialized.
 deriving (Eq, Ord, Typeable)

instance Exception ArrayException

stackOverflow, heapOverflow :: SomeException -- for the RTS
stackOverflow = toException StackOverflow
heapOverflow = toException HeapOverflow

instance Show AsyncException where
 showsPrec _ StackOverflow = showString "stack overflow"
 showsPrec _ HeapOverflow = showString "heap overflow"
 showsPrec _ ThreadKilled = showString "thread killed"
 showsPrec _ UserInterrupt = showString "user interrupt"

instance Show ArrayException where
 showsPrec _ (IndexOutOfBounds s)
 = showString "array index out of range"
 . (if not (null s) then showString ": " . showString s
 else id)
 showsPrec _ (UndefinedElement s)
 = showString "undefined array element"
 . (if not (null s) then showString ": " . showString s
 else id)

-- -----------------------------------------------------------------------------
-- The ExitCode type

-- We need it here because it is used in ExitException in the
-- Exception datatype (above).

-- | Defines the exit codes that a program can return.
data ExitCode
 = ExitSuccess -- ^ indicates successful termination;
 | ExitFailure Int
 -- ^ indicates program failure with an exit code.
 -- The exact interpretation of the code is
 -- operating-system dependent. In particular, some values
 -- may be prohibited (e.g. 0 on a POSIX-compliant system).
 deriving (Eq, Ord, Read, Show, Typeable)

 instance Exception ExitCode

ioException :: IOException -> IO a
ioException err = throwIO err

-- | Raise an 'IOError' in the 'IO' monad.
ioError :: IOError -> IO a
ioError = ioException

-- ---------------------------------------------------------------------------
-- IOError type

-- | The Haskell 2010 type for exceptions in the 'IO' monad.
-- Any I\/O operation may raise an 'IOError' instead of returning a result.
-- For a more general type of exception, including also those that arise
-- in pure code, see "Control.Exception.Exception".
--
-- In Haskell 2010, this is an opaque type.
type IOError = IOException

-- |Exceptions that occur in the @IO@ monad.
-- An @IOException@ records a more specific error type, a descriptive
-- string and maybe the handle that was used when the error was
-- flagged.
data IOException
 = IOError {
 ioe_handle :: Maybe Handle, -- the handle used by the action flagging
 -- the error.
 ioe_type :: IOErrorType, -- what it was.
 ioe_location :: String, -- location.
 ioe_description :: String, -- error type specific information.
 ioe_errno :: Maybe CInt, -- errno leading to this error, if any.
 ioe_filename :: Maybe FilePath -- filename the error is related to.
 }
 deriving Typeable

instance Exception IOException

instance Eq IOException where
 (IOError h1 e1 loc1 str1 en1 fn1) == (IOError h2 e2 loc2 str2 en2 fn2) =
 e1==e2 && str1==str2 && h1==h2 && loc1==loc2 && en1==en2 && fn1==fn2

-- | An abstract type that contains a value for each variant of 'IOError'.
data IOErrorType
 -- Haskell 2010:
 = AlreadyExists
 | NoSuchThing
 | ResourceBusy
 | ResourceExhausted
 | EOF
 | IllegalOperation
 | PermissionDenied
 | UserError
 -- GHC only:
 | UnsatisfiedConstraints
 | SystemError
 | ProtocolError
 | OtherError
 | InvalidArgument
 | InappropriateType
 | HardwareFault
 | UnsupportedOperation
 | TimeExpired
 | ResourceVanished
 | Interrupted

instance Eq IOErrorType where
 x == y = isTrue# (getTag x ==# getTag y)

instance Show IOErrorType where
 showsPrec _ e =
 showString $
 case e of
 AlreadyExists -> "already exists"
 NoSuchThing -> "does not exist"
 ResourceBusy -> "resource busy"
 ResourceExhausted -> "resource exhausted"
 EOF -> "end of file"
 IllegalOperation -> "illegal operation"
 PermissionDenied -> "permission denied"
 UserError -> "user error"
 HardwareFault -> "hardware fault"
 InappropriateType -> "inappropriate type"
 Interrupted -> "interrupted"
 InvalidArgument -> "invalid argument"
 OtherError -> "failed"
 ProtocolError -> "protocol error"
 ResourceVanished -> "resource vanished"
 SystemError -> "system error"
 TimeExpired -> "timeout"
 UnsatisfiedConstraints -> "unsatisified constraints" -- ultra-precise!
 UnsupportedOperation -> "unsupported operation"

-- | Construct an 'IOError' value with a string describing the error.
-- The 'fail' method of the 'IO' instance of the 'Monad' class raises a
-- 'userError', thus:
--
-- > instance Monad IO where
-- > ...
-- > fail s = ioError (userError s)
--
userError :: String -> IOError
userError str = IOError Nothing UserError "" str Nothing Nothing

-- ---------------------------------------------------------------------------
-- Showing IOErrors

instance Show IOException where
 showsPrec p (IOError hdl iot loc s _ fn) =
 (case fn of
 Nothing -> case hdl of
 Nothing -> id
 Just h -> showsPrec p h . showString ": "
 Just name -> showString name . showString ": ") .
 (case loc of
 "" -> id
 _ -> showString loc . showString ": ") .
 showsPrec p iot .
 (case s of
 "" -> id
 _ -> showString " (" . showString s . showString ")")

-- Note the use of "lazy". This means that
-- assert False (throw e)
-- will throw the assertion failure rather than e. See trac #5561.
assertError :: Addr# -> Bool -> a -> a
assertError str predicate v
 | predicate = lazy v
 | otherwise = throw (AssertionFailed (untangle str "Assertion failed"))

unsupportedOperation :: IOError
unsupportedOperation =
 (IOError Nothing UnsupportedOperation ""
 "Operation is not supported" Nothing Nothing)

{-
(untangle coded message) expects "coded" to be of the form
 "location|details"
It prints
 location message details
-}
untangle :: Addr# -> String -> String
untangle coded message
 = location
 ++ ": "
 ++ message
 ++ details
 ++ "\n"
 where
 coded_str = unpackCStringUtf8# coded

 (location, details)
 = case (span not_bar coded_str) of { (loc, rest) ->
 case rest of
 ('|':det) -> (loc, ' ' : det)
 _ -> (loc, "")
 }
 not_bar c = c /= '|'

AltStyle によって変換されたページ (->オリジナル) /