[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