diff options
author | Eric Seidel <gridaphobe@gmail.com> | 2015-12-23 10:10:04 +0100 |
---|---|---|
committer | Ben Gamari <ben@smart-cactus.org> | 2015-12-23 11:30:42 +0100 |
commit | 380b25ea4754c2aea683538ffdb179f8946219a0 (patch) | |
tree | 722784415e0f1b29a46fc115baff56f3495c0c9b /libraries/base/GHC/ForeignPtr.hs | |
parent | 78248702b0b8189d73f08c89d86f5cb7a3c6ae8c (diff) | |
download | haskell-380b25ea4754c2aea683538ffdb179f8946219a0.tar.gz |
Allow CallStacks to be frozen
This introduces "freezing," an operation which prevents further
locations from being appended to a CallStack. Library authors may want
to prevent CallStacks from exposing implementation details, as a matter
of hygiene. For example, in
```
head [] = error "head: empty list"
ghci> head []
*** Exception: head: empty list
CallStack (from implicit params):
error, called at ...
```
including the call-site of `error` in `head` is not strictly necessary
as the error message already specifies clearly where the error came
from.
So we add a function `freezeCallStack` that wraps an existing CallStack,
preventing further call-sites from being pushed onto it. In other words,
```
pushCallStack callSite (freezeCallStack callStack) = freezeCallStack callStack
```
Now we can define `head` to not produce a CallStack at all
```
head [] =
let ?callStack = freezeCallStack emptyCallStack
in error "head: empty list"
ghci> head []
*** Exception: head: empty list
CallStack (from implicit params):
error, called at ...
```
---
1. We add the `freezeCallStack` and `emptyCallStack` and update the
definition of `CallStack` to support this functionality.
2. We add `errorWithoutStackTrace`, a variant of `error` that does not
produce a stack trace, using this feature. I think this is a sensible
wrapper function to provide in case users want it.
3. We replace uses of `error` in base with `errorWithoutStackTrace`. The
rationale is that base does not export any functions that use CallStacks
(except for `error` and `undefined`) so there's no way for the stack
traces (from Implicit CallStacks) to include user-defined functions.
They'll only contain the call to `error` itself. As base already has a
good habit of providing useful error messages that name the triggering
function, the stack trace really just adds noise to the error. (I don't
have a strong opinion on whether we should include this third commit,
but the change was very mechanical so I thought I'd include it anyway in
case there's interest)
4. Updates tests in `array` and `stm` submodules
Test Plan: ./validate, new test is T11049
Reviewers: simonpj, nomeata, goldfire, austin, hvr, bgamari
Reviewed By: simonpj
Subscribers: thomie
Projects: #ghc
Differential Revision: https://phabricator.haskell.org/D1628
GHC Trac Issues: #11049
Diffstat (limited to 'libraries/base/GHC/ForeignPtr.hs')
-rw-r--r-- | libraries/base/GHC/ForeignPtr.hs | 22 |
1 files changed, 11 insertions, 11 deletions
diff --git a/libraries/base/GHC/ForeignPtr.hs b/libraries/base/GHC/ForeignPtr.hs index d0688f0cbf..6d03967d3b 100644 --- a/libraries/base/GHC/ForeignPtr.hs +++ b/libraries/base/GHC/ForeignPtr.hs @@ -153,7 +153,7 @@ mallocForeignPtr :: Storable a => IO (ForeignPtr a) mallocForeignPtr = doMalloc undefined where doMalloc :: Storable b => b -> IO (ForeignPtr b) doMalloc a - | I# size < 0 = error "mallocForeignPtr: size must be >= 0" + | I# size < 0 = errorWithoutStackTrace "mallocForeignPtr: size must be >= 0" | otherwise = do r <- newIORef NoFinalizers IO $ \s -> @@ -168,7 +168,7 @@ mallocForeignPtr = doMalloc undefined -- size of the memory required is given explicitly as a number of bytes. mallocForeignPtrBytes :: Int -> IO (ForeignPtr a) mallocForeignPtrBytes size | size < 0 = - error "mallocForeignPtrBytes: size must be >= 0" + errorWithoutStackTrace "mallocForeignPtrBytes: size must be >= 0" mallocForeignPtrBytes (I# size) = do r <- newIORef NoFinalizers IO $ \s -> @@ -182,7 +182,7 @@ mallocForeignPtrBytes (I# size) = do -- bytes. mallocForeignPtrAlignedBytes :: Int -> Int -> IO (ForeignPtr a) mallocForeignPtrAlignedBytes size _align | size < 0 = - error "mallocForeignPtrAlignedBytes: size must be >= 0" + errorWithoutStackTrace "mallocForeignPtrAlignedBytes: size must be >= 0" mallocForeignPtrAlignedBytes (I# size) (I# align) = do r <- newIORef NoFinalizers IO $ \s -> @@ -208,7 +208,7 @@ mallocPlainForeignPtr :: Storable a => IO (ForeignPtr a) mallocPlainForeignPtr = doMalloc undefined where doMalloc :: Storable b => b -> IO (ForeignPtr b) doMalloc a - | I# size < 0 = error "mallocForeignPtr: size must be >= 0" + | I# size < 0 = errorWithoutStackTrace "mallocForeignPtr: size must be >= 0" | otherwise = IO $ \s -> case newAlignedPinnedByteArray# size align s of { (# s', mbarr# #) -> (# s', ForeignPtr (byteArrayContents# (unsafeCoerce# mbarr#)) @@ -223,7 +223,7 @@ mallocPlainForeignPtr = doMalloc undefined -- exception to be thrown. mallocPlainForeignPtrBytes :: Int -> IO (ForeignPtr a) mallocPlainForeignPtrBytes size | size < 0 = - error "mallocPlainForeignPtrBytes: size must be >= 0" + errorWithoutStackTrace "mallocPlainForeignPtrBytes: size must be >= 0" mallocPlainForeignPtrBytes (I# size) = IO $ \s -> case newPinnedByteArray# size s of { (# s', mbarr# #) -> (# s', ForeignPtr (byteArrayContents# (unsafeCoerce# mbarr#)) @@ -236,7 +236,7 @@ mallocPlainForeignPtrBytes (I# size) = IO $ \s -> -- exception to be thrown. mallocPlainForeignPtrAlignedBytes :: Int -> Int -> IO (ForeignPtr a) mallocPlainForeignPtrAlignedBytes size _align | size < 0 = - error "mallocPlainForeignPtrAlignedBytes: size must be >= 0" + errorWithoutStackTrace "mallocPlainForeignPtrAlignedBytes: size must be >= 0" mallocPlainForeignPtrAlignedBytes (I# size) (I# align) = IO $ \s -> case newAlignedPinnedByteArray# size align s of { (# s', mbarr# #) -> (# s', ForeignPtr (byteArrayContents# (unsafeCoerce# mbarr#)) @@ -250,7 +250,7 @@ addForeignPtrFinalizer :: FinalizerPtr a -> ForeignPtr a -> IO () addForeignPtrFinalizer (FunPtr fp) (ForeignPtr p c) = case c of PlainForeignPtr r -> insertCFinalizer r fp 0# nullAddr# p () MallocPtr _ r -> insertCFinalizer r fp 0# nullAddr# p c - _ -> error "GHC.ForeignPtr: attempt to add a finalizer to a plain pointer" + _ -> errorWithoutStackTrace "GHC.ForeignPtr: attempt to add a finalizer to a plain pointer" -- Note [MallocPtr finalizers] (#10904) -- @@ -270,7 +270,7 @@ addForeignPtrFinalizerEnv :: addForeignPtrFinalizerEnv (FunPtr fp) (Ptr ep) (ForeignPtr p c) = case c of PlainForeignPtr r -> insertCFinalizer r fp 1# ep p () MallocPtr _ r -> insertCFinalizer r fp 1# ep p c - _ -> error "GHC.ForeignPtr: attempt to add a finalizer to a plain pointer" + _ -> errorWithoutStackTrace "GHC.ForeignPtr: attempt to add a finalizer to a plain pointer" addForeignPtrConcFinalizer :: ForeignPtr a -> IO () -> IO () -- ^This function adds a finalizer to the given @ForeignPtr@. The @@ -311,7 +311,7 @@ addForeignPtrConcFinalizer_ f@(MallocPtr fo r) finalizer = do finalizer' = unIO (foreignPtrFinalizer r >> touch f) addForeignPtrConcFinalizer_ _ _ = - error "GHC.ForeignPtr: attempt to add a finalizer to plain pointer" + errorWithoutStackTrace "GHC.ForeignPtr: attempt to add a finalizer to plain pointer" insertHaskellFinalizer :: IORef Finalizers -> IO () -> IO Bool insertHaskellFinalizer r f = do @@ -358,7 +358,7 @@ ensureCFinalizerWeak ref@(IORef (STRef r#)) value = do update _ _ = noMixingError noMixingError :: a -noMixingError = error $ +noMixingError = errorWithoutStackTrace $ "GHC.ForeignPtr: attempt to mix Haskell and C finalizers " ++ "in the same ForeignPtr" @@ -441,5 +441,5 @@ finalizeForeignPtr (ForeignPtr _ foreignPtr) = foreignPtrFinalizer refFinalizers (PlainForeignPtr ref) -> ref (MallocPtr _ ref) -> ref PlainPtr _ -> - error "finalizeForeignPtr PlainPtr" + errorWithoutStackTrace "finalizeForeignPtr PlainPtr" |