#16197: Strictness is not preserved under -O1 -------------------------------------+------------------------------------- Reporter: alang9 | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: Compiler | Version: 8.4.1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: Type of failure: Incorrect result | Unknown/Multiple at runtime | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Changes (by akio): * cc: akio (added) Comment: I made a smaller test case that I believe exhibits the same issue: {{{#!hs data T = T !Bool data Box a = Box a f :: Int -> T -> Box Bool f n t | n <= 0 = case t of T b -> Box b | otherwise = f (n-2) t f1 :: Int -> Bool -> Box Bool f1 n t | n <= 0 = f (-n) $! T t | otherwise = f1 (n-2) t g :: Int -> Bool g k = if k <= 0 then error "crash" else False {-# NOINLINE g #-} main :: IO () main = case f1 4 (g 0) of Box _ -> return () }}} With ghc-8.6.2, the above program crashes when compiled with -O0 but not when compiled with -O1. It looks like the issue has something to do with strictness analysis. After the first pass of the demand analyzer, `f` gets the following strictness signature: {{{ Str=<S(S),1*U(U)><S(S),1*U(U)>m, }}} This means `f` is deeply strict in the second argument. This is correct in the sense that `f n (T undefined)` is undefined, but it doesn't mean `f` is going to deeply evaluate the second argument (it doesn't). However, it looks like this strong strictness signature is then used in the next simplifier pass to justify dropping the implicit `seq` on `t` (arising from the field strictness of `T`) in `f1`. This `seq` is in fact the only thing in the program that forces the error thunk, so dropping it changes the program behavior. -- Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/16197#comment:4> GHC <http://www.haskell.org/ghc/> The Glasgow Haskell Compiler