[GHC] #5129: "evaluate" optimized away
GHC
cvs-ghc at haskell.org
Sat Apr 30 17:53:45 CEST 2011
#5129: "evaluate" optimized away
---------------------------------+------------------------------------------
Reporter: dons | Owner:
Type: bug | Status: new
Priority: normal | Milestone:
Component: Compiler | Version: 7.0.3
Keywords: seq, evaluate | Testcase:
Blockedby: | Difficulty:
Os: Unknown/Multiple | Blocking:
Architecture: Unknown/Multiple | Failure: Incorrect result at runtime
---------------------------------+------------------------------------------
Comment(by simonpj):
Actually this ticket is a bit different to #2273. Here is what is
happening.
Here are the background definitions:
{{{
evaluate :: a -> IO ()
-- Defined in GHC.IO.Base
evaluate x = x `seq` return ()
poss_err :: String
-- Assume this doesn't get constant folded away
-- So poss_err isn't definitely bottom.
-- (You had a NOINLINE on throwIfNegative)
poss_err = if (-1) < 0 then error "urk" else "foo"
assertFailure :: String -> IO ()
-- Defined in Test.HUnit.Lang
assertFailure s = throw (userError "blah")
}}}
Now consider the term
{{{
...catch (evaluate poss_err >> assertFailure "foo")...
}}}
By the time we've inlined `(>>)` and `evaluate`, we get something like
this
{{{
...catch (\s. case poss_err of _ -> assertFailure "foo" s)...
}}}
Now if GHC sees `(case x of y { ...blah... })`, and `y` is used strictly
in `...blah...`, it discards the case expression. After all, we reason
that if `x` turned out to throw an exception, we'll get the exception
later instead. And indeed, since `...blah...` diverges (well, throws a
different error), `y` is used strictly.
It's like the case in our imprecise-exception paper where we argue that we
should not specify which of the two errors is thrown by `(error "urk" +
error "bok")`. It's imprecise.
However, this is the IO monad, where the order of evaluation ''is''
defined, and the exceptions raise are too. There are two mistakes.
* In `GHC.IO.Base`, the function `evaluate` is small and hence can be
inlined. That exposes the fact that the state token is not affected by
evaluating the argument. If we didn't inline `evaluate` we'd end up with
something like
{{{
\s. case (evaluate poss_err s) of (# s1, _ #) -> assertFailure "blah"
s1
}}}
Now we can't drop the case because we need the state token `s1`. If we
just put a `{-# NOINLNE #-}` on `evaluate` we'll hide this from GHC so it
won't discard such cases.
* In `Test.HUnit.Lang`, the function `assertFailure` has an IO type, but
is defined using `throw`. It should be define using `throwIO`. The whole
point of `throwIO` is that it consumes a state token, and that's what
sequences it relative to earlier producers of the state token.
I'll fix `evaluate`, but someone else had better deal with `HUnit`.
Simon
--
Ticket URL: <http://hackage.haskell.org/trac/ghc/ticket/5129#comment:2>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
More information about the Glasgow-haskell-bugs
mailing list