Use extensible exceptions at the lowest level
[ghc-base.git] / Control / Exception.hs
index e4e037e..3a92b15 100644 (file)
 -- 
 -- Maintainer  :  libraries@haskell.org
 -- Stability   :  experimental
--- Portability :  non-portable
+-- Portability :  non-portable (extended exceptions)
 --
 -- This module provides support for raising and catching both built-in
 -- and user-defined exceptions.
 --
+-- In addition to exceptions thrown by 'IO' operations, exceptions may
+-- be thrown by pure code (imprecise exceptions) or by external events
+-- (asynchronous exceptions), but may only be caught in the 'IO' monad.
+-- For more details, see:
+--
+--  * /A semantics for imprecise exceptions/, by Simon Peyton Jones,
+--    Alastair Reid, Tony Hoare, Simon Marlow, Fergus Henderson,
+--    in /PLDI'99/.
+--
+--  * /Asynchronous exceptions in Haskell/, by Simon Marlow, Simon Peyton
+--    Jones, Andy Moran and John Reppy, in /PLDI'01/.
+--
 -----------------------------------------------------------------------------
 
 module Control.Exception (
 
-       -- * The Exception type
-       Exception(..),          -- instance Eq, Ord, Show, Typeable
-       IOException,            -- instance Eq, Ord, Show, Typeable
-       ArithException(..),     -- instance Eq, Ord, Show, Typeable
-       ArrayException(..),     -- instance Eq, Ord, Show, Typeable
-       AsyncException(..),     -- instance Eq, Ord, Show, Typeable
-
-       -- * Throwing exceptions
-       throwIO,        -- :: Exception -> IO a
-       throw,          -- :: Exception -> a
-       ioError,        -- :: IOError -> IO a
+        -- * The Exception type
+        Exception(..),          -- instance Eq, Ord, Show, Typeable
+        IOException,            -- instance Eq, Ord, Show, Typeable
+        ArithException(..),     -- instance Eq, Ord, Show, Typeable
+        ArrayException(..),     -- instance Eq, Ord, Show, Typeable
+        AsyncException(..),     -- instance Eq, Ord, Show, Typeable
+
+        -- * Throwing exceptions
+        throwIO,        -- :: Exception -> IO a
+        throw,          -- :: Exception -> a
+        ioError,        -- :: IOError -> IO a
 #ifdef __GLASGOW_HASKELL__
-       throwTo,        -- :: ThreadId -> Exception -> a
+        throwTo,        -- :: ThreadId -> Exception -> a
 #endif
 
-       -- * Catching Exceptions
+        -- * Catching Exceptions
 
-       -- |There are several functions for catching and examining
-       -- exceptions; all of them may only be used from within the
-       -- 'IO' monad.
+        -- |There are several functions for catching and examining
+        -- exceptions; all of them may only be used from within the
+        -- 'IO' monad.
 
-       -- ** The @catch@ functions
-       catch,     -- :: IO a -> (Exception -> IO a) -> IO a
-       catchJust, -- :: (Exception -> Maybe b) -> IO a -> (b -> IO a) -> IO a
+        -- ** The @catch@ functions
+        catch,     -- :: IO a -> (Exception -> IO a) -> IO a
+        catchAny,
+        catchJust, -- :: (Exception -> Maybe b) -> IO a -> (b -> IO a) -> IO a
 
-       -- ** The @handle@ functions
-       handle,    -- :: (Exception -> IO a) -> IO a -> IO a
-       handleJust,-- :: (Exception -> Maybe b) -> (b -> IO a) -> IO a -> IO a
+        -- ** The @handle@ functions
+        handle,    -- :: (Exception -> IO a) -> IO a -> IO a
+        handleJust,-- :: (Exception -> Maybe b) -> (b -> IO a) -> IO a -> IO a
 
-       -- ** The @try@ functions
-       try,       -- :: IO a -> IO (Either Exception a)
-       tryJust,   -- :: (Exception -> Maybe b) -> a    -> IO (Either b a)
+        -- ** The @try@ functions
+        try,       -- :: IO a -> IO (Either Exception a)
+        tryJust,   -- :: (Exception -> Maybe b) -> a    -> IO (Either b a)
 
-       -- ** The @evaluate@ function
-       evaluate,  -- :: a -> IO a
+        -- ** The @evaluate@ function
+        evaluate,  -- :: a -> IO a
 
-       -- ** The @mapException@ function
-       mapException,           -- :: (Exception -> Exception) -> a -> a
+        -- ** The @mapException@ function
+        mapException,           -- :: (Exception -> Exception) -> a -> a
 
-       -- ** Exception predicates
-       
-       -- $preds
+        -- ** Exception predicates
+        
+        -- $preds
 
-       ioErrors,               -- :: Exception -> Maybe IOError
-       arithExceptions,        -- :: Exception -> Maybe ArithException
-       errorCalls,             -- :: Exception -> Maybe String
-       dynExceptions,          -- :: Exception -> Maybe Dynamic
-       assertions,             -- :: Exception -> Maybe String
-       asyncExceptions,        -- :: Exception -> Maybe AsyncException
-       userErrors,             -- :: Exception -> Maybe String
+        ioErrors,               -- :: Exception -> Maybe IOError
+        arithExceptions,        -- :: Exception -> Maybe ArithException
+        errorCalls,             -- :: Exception -> Maybe String
+        dynExceptions,          -- :: Exception -> Maybe Dynamic
+        assertions,             -- :: Exception -> Maybe String
+        asyncExceptions,        -- :: Exception -> Maybe AsyncException
+        userErrors,             -- :: Exception -> Maybe String
 
-       -- * Dynamic exceptions
+        -- * Dynamic exceptions
 
-       -- $dynamic
-       throwDyn,       -- :: Typeable ex => ex -> b
+        -- $dynamic
+        throwDyn,       -- :: Typeable ex => ex -> b
 #ifdef __GLASGOW_HASKELL__
-       throwDynTo,     -- :: Typeable ex => ThreadId -> ex -> b
+        throwDynTo,     -- :: Typeable ex => ThreadId -> ex -> b
 #endif
-       catchDyn,       -- :: Typeable ex => IO a -> (ex -> IO a) -> IO a
-       
-       -- * Asynchronous Exceptions
+        catchDyn,       -- :: Typeable ex => IO a -> (ex -> IO a) -> IO a
+        
+        -- * Asynchronous Exceptions
 
-       -- $async
+        -- $async
 
-       -- ** Asynchronous exception control
+        -- ** Asynchronous exception control
 
-       -- |The following two functions allow a thread to control delivery of
-       -- asynchronous exceptions during a critical region.
+        -- |The following two functions allow a thread to control delivery of
+        -- asynchronous exceptions during a critical region.
 
         block,          -- :: IO a -> IO a
         unblock,        -- :: IO a -> IO a
+        blocked,        -- :: IO Bool
 
-       -- *** Applying @block@ to an exception handler
+        -- *** Applying @block@ to an exception handler
 
-       -- $block_handler
+        -- $block_handler
 
-       -- *** Interruptible operations
+        -- *** Interruptible operations
 
-       -- $interruptible
+        -- $interruptible
 
-       -- * Assertions
+        -- * Assertions
 
-       assert,         -- :: Bool -> a -> a
+        assert,         -- :: Bool -> a -> a
 
-       -- * Utilities
+        -- * Utilities
 
-       bracket,        -- :: IO a -> (a -> IO b) -> (a -> IO c) -> IO ()
-       bracket_,       -- :: IO a -> IO b -> IO c -> IO ()
+        bracket,        -- :: IO a -> (a -> IO b) -> (a -> IO c) -> IO ()
+        bracket_,       -- :: IO a -> IO b -> IO c -> IO ()
+        bracketOnError,
 
-       finally,        -- :: IO a -> IO b -> IO a
-       
+        finally,        -- :: IO a -> IO b -> IO a
+        
 #ifdef __GLASGOW_HASKELL__
-       setUncaughtExceptionHandler,      -- :: (Exception -> IO ()) -> IO ()
-       getUncaughtExceptionHandler       -- :: IO (Exception -> IO ())
+        setUncaughtExceptionHandler,      -- :: (Exception -> IO ()) -> IO ()
+        getUncaughtExceptionHandler       -- :: IO (Exception -> IO ())
 #endif
   ) where
 
 #ifdef __GLASGOW_HASKELL__
-import GHC.Base                ( assert )
-import GHC.Exception   as ExceptionBase hiding (catch)
-import GHC.Conc                ( throwTo, ThreadId )
-import GHC.IOBase      ( IO(..), IORef(..), newIORef, readIORef, writeIORef )
-import GHC.Handle       ( stdout, hFlush )
+import GHC.Base         ( assert )
+import GHC.IOBase
+import GHC.Exception    as ExceptionBase hiding (Exception, catch)
+import GHC.Conc         ( throwTo, ThreadId )
+import Data.IORef       ( IORef, newIORef, readIORef, writeIORef )
+import Foreign.C.String ( CString, withCString )
+import System.IO        ( stdout, hFlush )
 #endif
 
 #ifdef __HUGS__
-import Hugs.Exception  as ExceptionBase
+import Hugs.Exception   as ExceptionBase
 #endif
 
-import Foreign.C.String ( CString, withCStringLen )
-
-import Prelude                 hiding ( catch )
-import System.IO.Error hiding ( catch, try )
+import Prelude          hiding ( catch )
+import System.IO.Error  hiding ( catch, try )
 import System.IO.Unsafe (unsafePerformIO)
 import Data.Dynamic
 
-#include "Typeable.h"
-INSTANCE_TYPEABLE0(Exception,exceptionTc,"Exception")
-INSTANCE_TYPEABLE0(IOException,ioExceptionTc,"IOException")
-INSTANCE_TYPEABLE0(ArithException,arithExceptionTc,"ArithException")
-INSTANCE_TYPEABLE0(ArrayException,arrayExceptionTc,"ArrayException")
-INSTANCE_TYPEABLE0(AsyncException,asyncExceptionTc,"AsyncException")
+#ifdef __NHC__
+import qualified System.IO.Error as H'98 (catch)
+import System.IO.Error (ioError)
+import IO              (bracket)
+import DIOError         -- defn of IOError type
+import System          (ExitCode())
+
+-- minimum needed for nhc98 to pretend it has Exceptions
+data Exception   = IOException    IOException
+                 | ArithException ArithException
+                 | ArrayException ArrayException
+                 | AsyncException AsyncException
+                 | ExitException  ExitCode
+                 deriving Show
+type IOException = IOError
+data ArithException
+data ArrayException
+data AsyncException
+instance Show ArithException
+instance Show ArrayException
+instance Show AsyncException
+
+catch    :: IO a -> (Exception -> IO a) -> IO a
+a `catch` b = a `H'98.catch` (b . IOException)
+
+throwIO  :: Exception -> IO a
+throwIO (IOException e) = ioError e
+throwIO _               = ioError (UserError "Control.Exception.throwIO"
+                                             "unknown exception")
+throw    :: Exception -> a
+throw     = unsafePerformIO . throwIO
+
+evaluate :: a -> IO a
+evaluate x = x `seq` return x
+
+ioErrors        :: Exception -> Maybe IOError
+ioErrors        (IOException e)     = Just e
+ioErrors        _                   = Nothing
+arithExceptions :: Exception -> Maybe ArithException
+arithExceptions (ArithException e)  = Just e
+arithExceptions _                   = Nothing
+errorCalls      :: Exception -> Maybe String
+errorCalls       = const Nothing
+dynExceptions   :: Exception -> Maybe Dynamic
+dynExceptions    = const Nothing
+assertions      :: Exception -> Maybe String
+assertions       = const Nothing
+asyncExceptions :: Exception -> Maybe AsyncException
+asyncExceptions  = const Nothing
+userErrors      :: Exception -> Maybe String
+userErrors (IOException (UserError _ s)) = Just s
+userErrors  _                            = Nothing
+
+assert :: Bool -> a -> a
+assert True  x = x
+assert False _ = throw (IOException (UserError "" "Assertion failed"))
+#endif
+
+#ifndef __GLASGOW_HASKELL__
+-- Dummy definitions for implementations lacking asynchonous exceptions
+
+block   :: IO a -> IO a
+block    = id
+unblock :: IO a -> IO a
+unblock  = id
+blocked :: IO Bool
+blocked  = return False
+#endif
 
 -----------------------------------------------------------------------------
 -- Catching exceptions
@@ -147,7 +224,7 @@ INSTANCE_TYPEABLE0(AsyncException,asyncExceptionTc,"AsyncException")
 -- argument.  Otherwise, the result is returned as normal.  For example:
 --
 -- >   catch (openFile f ReadMode) 
--- >       (\e -> hPutStr stderr (\"Couldn\'t open \"++f++\": \" ++ show e))
+-- >       (\e -> hPutStr stderr ("Couldn't open "++f++": " ++ show e))
 --
 -- For catching exceptions in pure (non-'IO') expressions, see the
 -- function 'evaluate'.
@@ -170,21 +247,28 @@ INSTANCE_TYPEABLE0(AsyncException,asyncExceptionTc,"AsyncException")
 -- might be a 'ThreadKilled', for example).  In this case it is usually better
 -- to use 'catchJust' and select the kinds of exceptions to catch.
 --
--- Also note that The "Prelude" also exports a
--- function called 'catch' which has the same type as
--- 'Control.Exception.catch', the difference being that the
--- "Prelude" version only catches the IO and user
--- families of exceptions (as required by Haskell 98).  We recommend
--- either hiding the "Prelude" version of
--- 'catch' when importing
--- "Control.Exception", or importing
--- "Control.Exception" qualified, to avoid name-clashes.
-
-catch          :: IO a                 -- ^ The computation to run
-       -> (Exception -> IO a)  -- ^ Handler to invoke if an exception is raised
-       -> IO a                 
+-- Also note that the "Prelude" also exports a function called
+-- 'Prelude.catch' with a similar type to 'Control.Exception.catch',
+-- except that the "Prelude" version only catches the IO and user
+-- families of exceptions (as required by Haskell 98).  
+--
+-- We recommend either hiding the "Prelude" version of 'Prelude.catch'
+-- when importing "Control.Exception": 
+--
+-- > import Prelude hiding (catch)
+--
+-- or importing "Control.Exception" qualified, to avoid name-clashes:
+--
+-- > import qualified Control.Exception as C
+--
+-- and then using @C.catch@
+--
+#ifndef __NHC__
+catch   :: IO a                 -- ^ The computation to run
+        -> (Exception -> IO a)  -- ^ Handler to invoke if an exception is raised
+        -> IO a                 
 catch =  ExceptionBase.catchException
-
+#endif
 -- | The function 'catchJust' is like 'catch', but it takes an extra
 -- argument which is an /exception predicate/, a function which
 -- selects which type of exceptions we\'re interested in.  There are
@@ -198,21 +282,21 @@ catch =  ExceptionBase.catchException
 -- are re-raised, and may be caught by an enclosing
 -- 'catch' or 'catchJust'.
 catchJust
-       :: (Exception -> Maybe b) -- ^ Predicate to select exceptions
-       -> IO a                   -- ^ Computation to run
-       -> (b -> IO a)            -- ^ Handler
-       -> IO a
+        :: (Exception -> Maybe b) -- ^ Predicate to select exceptions
+        -> IO a                   -- ^ Computation to run
+        -> (b -> IO a)            -- ^ Handler
+        -> IO a
 catchJust p a handler = catch a handler'
   where handler' e = case p e of 
-                       Nothing -> throw e
-                       Just b  -> handler b
+                        Nothing -> throw e
+                        Just b  -> handler b
 
 -- | A version of 'catch' with the arguments swapped around; useful in
 -- situations where the code for the handler is shorter.  For example:
 --
 -- >   do handle (\e -> exitWith (ExitFailure 1)) $
--- >     ...
-handle    :: (Exception -> IO a) -> IO a -> IO a
+-- >      ...
+handle     :: (Exception -> IO a) -> IO a -> IO a
 handle     =  flip catch
 
 -- | A version of 'catchJust' with the arguments swapped around (see
@@ -221,25 +305,6 @@ handleJust :: (Exception -> Maybe b) -> (b -> IO a) -> IO a -> IO a
 handleJust p =  flip (catchJust p)
 
 -----------------------------------------------------------------------------
--- evaluate
-
--- | Forces its argument to be evaluated, and returns the result in
--- the 'IO' monad.  It can be used to order evaluation with respect to
--- other 'IO' operations; its semantics are given by
---
--- >   evaluate undefined `seq` return ()  ==> return ()
--- >   catch (evaluate undefined) (\e -> return ())  ==> return ()
---
--- NOTE: @(evaluate a)@ is /not/ the same as @(a \`seq\` return a)@.
-#ifdef __GLASGOW_HASKELL__
-evaluate :: a -> IO a
-evaluate a = IO $ \s -> case a `seq` () of () -> (# s, a #)
-       -- NB. can't write  
-       --      a `seq` (# s, a #)
-       -- because we can't have an unboxed tuple as a function argument
-#endif
-
------------------------------------------------------------------------------
 -- 'mapException'
 
 -- | This function maps one exception into another as proposed in the
@@ -255,15 +320,20 @@ mapException f v = unsafePerformIO (catch (evaluate v)
 -- 'try' and variations.
 
 -- | Similar to 'catch', but returns an 'Either' result which is
--- @(Right a)@ if no exception was raised, or @(Left e)@ if an
+-- @('Right' a)@ if no exception was raised, or @('Left' e)@ if an
 -- exception was raised and its value is @e@.
 --
--- >  try a = catch (Right \`liftM\` a) (return . Left)
+-- >  try a = catch (Right `liftM` a) (return . Left)
 --
 -- Note: as with 'catch', it is only polite to use this variant if you intend
 -- to re-throw the exception after performing whatever cleanup is needed.
 -- Otherwise, 'tryJust' is generally considered to be better.
 --
+-- Also note that "System.IO.Error" also exports a function called
+-- 'System.IO.Error.try' with a similar type to 'Control.Exception.try',
+-- except that it catches only the IO and user families of exceptions
+-- (as required by the Haskell 98 @IO@ module).
+
 try :: IO a -> IO (Either Exception a)
 try a = catch (a >>= \ v -> return (Right v)) (\e -> return (Left e))
 
@@ -274,10 +344,10 @@ tryJust :: (Exception -> Maybe b) -> IO a -> IO (Either b a)
 tryJust p a = do
   r <- try a
   case r of
-       Right v -> return (Right v)
-       Left  e -> case p e of
-                       Nothing -> throw e
-                       Just b  -> return (Left b)
+        Right v -> return (Right v)
+        Left  e -> case p e of
+                        Nothing -> throw e
+                        Just b  -> return (Left b)
 
 -----------------------------------------------------------------------------
 -- Dynamic exceptions
@@ -291,7 +361,11 @@ tryJust p a = do
 -- | Raise any value as an exception, provided it is in the
 -- 'Typeable' class.
 throwDyn :: Typeable exception => exception -> b
+#ifdef __NHC__
+throwDyn exception = throw (IOException (UserError "" "dynamic exception"))
+#else
 throwDyn exception = throw (DynException (toDyn exception))
+#endif
 
 #ifdef __GLASGOW_HASKELL__
 -- | A variant of 'throwDyn' that throws the dynamic exception to an
@@ -309,13 +383,17 @@ throwDynTo t exception = throwTo t (DynException (toDyn exception))
 -- with dynamic exceptions used in other libraries.
 --
 catchDyn :: Typeable exception => IO a -> (exception -> IO a) -> IO a
+#ifdef __NHC__
+catchDyn m k = m        -- can't catch dyn exceptions in nhc98
+#else
 catchDyn m k = catchException m handle
   where handle ex = case ex of
-                          (DynException dyn) ->
-                               case fromDynamic dyn of
-                                   Just exception  -> k exception
-                                   Nothing -> throw ex
-                          _ -> throw ex
+                           (DynException dyn) ->
+                                case fromDynamic dyn of
+                                    Just exception  -> k exception
+                                    Nothing -> throw ex
+                           _ -> throw ex
+#endif
 
 -----------------------------------------------------------------------------
 -- Exception Predicates
@@ -324,14 +402,14 @@ catchDyn m k = catchException m handle
 -- These pre-defined predicates may be used as the first argument to
 -- 'catchJust', 'tryJust', or 'handleJust' to select certain common
 -- classes of exceptions.
-
-ioErrors               :: Exception -> Maybe IOError
-arithExceptions        :: Exception -> Maybe ArithException
-errorCalls             :: Exception -> Maybe String
-assertions             :: Exception -> Maybe String
-dynExceptions          :: Exception -> Maybe Dynamic
-asyncExceptions        :: Exception -> Maybe AsyncException
-userErrors             :: Exception -> Maybe String
+#ifndef __NHC__
+ioErrors                :: Exception -> Maybe IOError
+arithExceptions         :: Exception -> Maybe ArithException
+errorCalls              :: Exception -> Maybe String
+assertions              :: Exception -> Maybe String
+dynExceptions           :: Exception -> Maybe Dynamic
+asyncExceptions         :: Exception -> Maybe AsyncException
+userErrors              :: Exception -> Maybe String
 
 ioErrors (IOException e) = Just e
 ioErrors _ = Nothing
@@ -353,7 +431,7 @@ asyncExceptions _ = Nothing
 
 userErrors (IOException e) | isUserError e = Just (ioeGetErrorString e)
 userErrors _ = Nothing
-
+#endif
 -----------------------------------------------------------------------------
 -- Some Useful Functions
 
@@ -374,36 +452,37 @@ userErrors _ = Nothing
 -- The arguments to 'bracket' are in this order so that we can partially apply 
 -- it, e.g.:
 --
--- > withFile name = bracket (openFile name) hClose
+-- > withFile name mode = bracket (openFile name mode) hClose
 --
+#ifndef __NHC__
 bracket 
-       :: IO a         -- ^ computation to run first (\"acquire resource\")
-       -> (a -> IO b)  -- ^ computation to run last (\"release resource\")
-       -> (a -> IO c)  -- ^ computation to run in-between
-       -> IO c         -- returns the value from the in-between computation
+        :: IO a         -- ^ computation to run first (\"acquire resource\")
+        -> (a -> IO b)  -- ^ computation to run last (\"release resource\")
+        -> (a -> IO c)  -- ^ computation to run in-between
+        -> IO c         -- returns the value from the in-between computation
 bracket before after thing =
   block (do
     a <- before 
     r <- catch 
-          (unblock (thing a))
-          (\e -> do { after a; throw e })
+           (unblock (thing a))
+           (\e -> do { after a; throw e })
     after a
     return r
  )
-   
+#endif
 
 -- | A specialised variant of 'bracket' with just a computation to run
 -- afterward.
 -- 
-finally :: IO a                -- ^ computation to run first
-       -> IO b         -- ^ computation to run afterward (even if an exception 
-                       -- was raised)
-       -> IO a         -- returns the value from the first computation
+finally :: IO a         -- ^ computation to run first
+        -> IO b         -- ^ computation to run afterward (even if an exception 
+                        -- was raised)
+        -> IO a         -- returns the value from the first computation
 a `finally` sequel =
   block (do
     r <- catch 
-            (unblock a)
-            (\e -> do { sequel; throw e })
+             (unblock a)
+             (\e -> do { sequel; throw e })
     sequel
     return r
   )
@@ -413,6 +492,21 @@ a `finally` sequel =
 bracket_ :: IO a -> IO b -> IO c -> IO c
 bracket_ before after thing = bracket before (const after) (const thing)
 
+-- | Like bracket, but only performs the final action if there was an 
+-- exception raised by the in-between computation.
+bracketOnError
+        :: IO a         -- ^ computation to run first (\"acquire resource\")
+        -> (a -> IO b)  -- ^ computation to run last (\"release resource\")
+        -> (a -> IO c)  -- ^ computation to run in-between
+        -> IO c         -- returns the value from the in-between computation
+bracketOnError before after thing =
+  block (do
+    a <- before 
+    catch 
+        (unblock (thing a))
+        (\e -> do { after a; throw e })
+ )
+
 -- -----------------------------------------------------------------------------
 -- Asynchronous exceptions
 
@@ -490,24 +584,7 @@ Similar arguments apply for other interruptible operations like
 'System.IO.openFile'.
 -}
 
--- -----------------------------------------------------------------------------
--- Assert
-
-#ifdef __HADDOCK__
--- | If the first argument evaluates to 'True', then the result is the
--- second argument.  Otherwise an 'AssertionFailed' exception is raised,
--- containing a 'String' with the source file and line number of the
--- call to assert.
---
--- Assertions can normally be turned on or off with a compiler flag
--- (for GHC, assertions are normally on unless the @-fignore-asserts@
--- option is give).  When assertions are turned off, the first
--- argument to 'assert' is ignored, and the second argument is
--- returned as the result.
-assert :: Bool -> a -> a
-#endif
-
-#ifndef __GLASGOW_HASKELL__
+#if !(__GLASGOW_HASKELL__ || __NHC__)
 assert :: Bool -> a -> a
 assert True x = x
 assert False _ = throw (AssertionFailed "")
@@ -521,15 +598,19 @@ uncaughtExceptionHandler = unsafePerformIO (newIORef defaultHandler)
    where
       defaultHandler :: Exception -> IO ()
       defaultHandler ex = do
-         (hFlush stdout) `catchException` (\ _ -> return ())
+         (hFlush stdout) `catchAny` (\ _ -> return ())
          let msg = case ex of
                Deadlock    -> "no threads to run:  infinite loop or deadlock?"
                ErrorCall s -> s
-               other       -> showsPrec 0 other "\n"
-         withCStringLen ("Fail: "++msg) $ \(cstr,len) -> writeErrString cstr len
-         
-foreign import ccall unsafe "writeErrString__"
-       writeErrString :: CString -> Int -> IO ()
+               other       -> showsPrec 0 other ""
+         withCString "%s" $ \cfmt ->
+          withCString msg $ \cmsg ->
+            errorBelch cfmt cmsg
+
+-- don't use errorBelch() directly, because we cannot call varargs functions
+-- using the FFI.
+foreign import ccall unsafe "HsBase.h errorBelch2"
+   errorBelch :: CString -> CString -> IO ()
 
 setUncaughtExceptionHandler :: (Exception -> IO ()) -> IO ()
 setUncaughtExceptionHandler = writeIORef uncaughtExceptionHandler