[Git][ghc/ghc][wip/T24625] Improve efficiency of `assertError` (#24625)
Sebastian Graf (@sgraf812)
gitlab at gitlab.haskell.org
Sun Aug 18 10:26:00 UTC 2024
Sebastian Graf pushed to branch wip/T24625 at Glasgow Haskell Compiler / GHC
Commits:
d11c734e by Sebastian Graf at 2024-08-18T12:25:50+02:00
Improve efficiency of `assertError` (#24625)
... by moving `lazy` to the exception-throwing branch.
It's all documented in `Note [Strictness of assertError]`.
- - - - -
3 changed files:
- libraries/ghc-internal/src/GHC/Internal/IO/Exception.hs
- + testsuite/tests/simplCore/should_compile/T24625.hs
- testsuite/tests/simplCore/should_compile/all.T
Changes:
=====================================
libraries/ghc-internal/src/GHC/Internal/IO/Exception.hs
=====================================
@@ -438,13 +438,10 @@ instance Show IOException where
"" -> 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 :: (?callStack :: CallStack) => Bool -> a -> a
assertError predicate v
- | predicate = lazy v
- | otherwise = unsafeDupablePerformIO $ do
+ | predicate = v
+ | otherwise = lazy $ unsafeDupablePerformIO $ do -- lazy: See Note [Strictness of assertError]
ccsStack <- currentCallStack
let
implicitParamCallStack = prettyCallStackLines ?callStack
@@ -452,6 +449,44 @@ assertError predicate v
stack = intercalate "\n" $ implicitParamCallStack ++ ccsCallStack
throwIO (AssertionFailed ("Assertion failed\n" ++ stack))
+{- Note [Strictness of assertError]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+It is vital that Demand Analysis does not see `assertError p e` as strict in e.
+#5561 details what happens otherwise, tested by libraries/base/tests/assert.hs:
+
+ let e1 i = throw Overflow
+ in assertError False (e1 5)
+
+This should *not* throw the Overflow exception; rather it should throw an
+AssertionError.
+Hence we use GHC.Exts.lazy to make assertError appear lazy in e, so that it
+is not called by-value.
+(Note that the reason we need `lazy` in the first place is that error has a
+bottoming result, which is strict in all free variables.)
+The way we achieve this is a bit subtle; before #24625 we defined it as
+
+ assertError p e | p = lazy e
+ | otherwise = error "assertion"
+
+but this means that in the following example (full code in T24625) we cannot
+cancel away the allocation of `Just x` because of the intervening `lazy`:
+
+ case assertError False (Just x) of Just y -> y
+ ==> { simplify }
+ case lazy (Just x) of Just y -> y
+
+Instead, we put `lazy` in the otherwise branch, thus
+
+ assertError p e | p = e
+ | otherwise = lazy $ error "assertion"
+
+The effect on #5561 is the same: since the otherwise branch appears lazy in e,
+the overall demand on `e` must be lazy as well.
+Furthermore, since there is no intervening `lazy` on the expected code path,
+the Simplifier may perform case-of-case on e and simplify the `Just x` example
+to `x`.
+-}
+
unsupportedOperation :: IOError
unsupportedOperation =
(IOError Nothing UnsupportedOperation ""
@@ -480,4 +515,3 @@ untangle coded message
_ -> (loc, "")
}
not_bar c = c /= '|'
-
=====================================
testsuite/tests/simplCore/should_compile/T24625.hs
=====================================
@@ -0,0 +1,14 @@
+module T24625 where
+
+import GHC.IO.Exception
+import GHC.Exts
+
+data Foo = Foo !Int !Int String
+
+true :: Bool
+true = True
+{-# NOINLINE true #-}
+
+function :: Int -> Int -> String -> Int
+function !a !b c = case assertError true (Foo a b c) of
+ Foo a b c -> a + b
=====================================
testsuite/tests/simplCore/should_compile/all.T
=====================================
@@ -526,5 +526,6 @@ test('T24808', [ grep_errmsg(r'myFunction') ], compile, ['-O -ddump-simpl'])
# T24944 needs -O2 because it's about SpecConstr
test('T24944', [extra_files(['T24944a.hs'])], multimod_compile, ['T24944', '-v0 -O2'])
+test('T24625', [ grep_errmsg(r'case lazy') ], compile, ['-O -ddump-simpl -dsuppress-uniques'])
test('T24725a', [ grep_errmsg(r'testedRule')], compile, ['-O -ddump-rule-firings'])
test('T25033', normal, compile, ['-O'])
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/d11c734ecb42d391d51e30bb403b662e14a3b30a
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/d11c734ecb42d391d51e30bb403b662e14a3b30a
You're receiving this email because of your account on gitlab.haskell.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-commits/attachments/20240818/b4f561ab/attachment-0001.html>
More information about the ghc-commits
mailing list