Skip to content

exprIsBottom inconsistent with strictness analyser

Because of Note [IO hack in the demand analyser] (which I hate), an expression like

f :: Int -> State# RealWorld -> (# State# RealWorld, Int #)
f x s = case blah of (# s::State# RealWorld, r::() #) ->
        error (show x)

is not reported as a bottoming function by the strictness analyser.

But exprBotStrictness_maybe will say that the RHS is bottoming. That ultimately comes from CoreArity.arityType which has no analogous hack to the demand analyser.

These can't both be right! We could

  • Cripple exprBotStrictness_maybe a bit by adding the hack there too.
  • Weaken the hack so that it propagates divergence.

The demand-analyser hack note says

{- Note [IO hack in the demand analyser]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
There's a hack here for I/O operations.  Consider
     case foo x s of { (# s, r #) -> y }
Is this strict in 'y'?  Normally yes, but what if 'foo' is an I/O
operation that simply terminates the program (not in an erroneous way)?
In that case we should not evaluate 'y' before the call to 'foo'.
Hackish solution: spot the IO-like situation and add a virtual branch,
as if we had
     case foo x s of
        (# s, r #) -> y
        other      -> return ()
So the 'y' isn't necessarily going to be evaluated

A more complete example (Trac #148, #1592) where this shows up is:
     do { let len = <expensive> ;
        ; when (...) (exitWith ExitSuccess)
        ; print len }

I wonder if we could weaken the hack so that it propagated divergence/exception-thowing, while still making mentioned variables lazy. The big reason I'd like to do this is if we have

case f x s of (# s',r #) -> BIG

then I really want to discard the alternative (since f x s is guaranteed to throw an exception) to give

case f x s of {}

This is absolutely kosher; no change in evaluation order or anything.

But weakening the IO hack in this way can change strictness. For example

g A x y z s = x `seq` y `seq` (# s, () #)
g B x y z s = x `seq` case blah2 of (# s', _ #) -> 
              y `seq` (# s', () #)
g C x y z s = case blah of (# s', _ #) ->
                  error (show z)

Currently we treat this as lazy in x,y and z. With the above change, it'd become strict in x but not y or z, which is a little weird.

Trac metadata
Trac field Value
Version 8.0.1
Type Bug
TypeOfFailure OtherFailure
Priority normal
Resolution Unresolved
Component Compiler
Test case
Differential revisions
BlockedBy
Related
Blocking
CC
Operating system
Architecture
Edited by Simon Peyton Jones
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information