Let-bound wildcards are not let-generalised
With -XBangPatterns
, the act of declaring a let-binding without using it might have a side-effect. Consider this example:
{-# LANGUAGE BangPatterns #-}
f :: ()
f = let !x = undefined in ()
g :: ()
g = let !_ = undefined in ()
main = do
print f
print g
Both f
and g
declare a variable in a strict binding that is actually dead. Surprisingly, they lead to different code. Here's the Core (GHC 8.6.5) after optimisation:
main
= >>
(print
@ ()
GHC.Show.$fShow()
(case \ (@ a_a2hW) ->
undefined
@ 'GHC.Types.LiftedRep
@ a_a2hW
(<snip>)
of
{ __DEFAULT ->
GHC.Tuple.()
}))
(print
@ ()
GHC.Show.$fShow()
(case undefined
@ 'GHC.Types.LiftedRep
@ GHC.Types.Any
(<snip>)
of
{ __DEFAULT ->
GHC.Tuple.()
}))
Note the /\a. undefined @LiftedRep @a
in the first scrutinee. This is ultimately due to the fact that x
is let-generalised, whereas _
is not. The first case is on a lambda, so I expect it to perform no forcing at all. The first case evaluates a complete application of undefined
, so should crash. As a result, I'd expect this program to generate the following output:
()
test: Prelude.undefined
CallStack (from HasCallStack):
error, called at libraries/base/GHC/Err.hs:78:14 in base:GHC.Err
undefined, called at test.hs:4:15 in main:Main
Currently (tested on GHC 8.6.5), even the first print f
will crash, so the behavior is actually consistent. I think this is actually a reasonable semantics, but I didn't expect it based on the difference in Core. My understanding of Core semantics might be off here.
So: Is let-generalisation of x
but not of _
expected? If so, why does let !x = undefined in ()
crash?
CC @simonpj @rae since this is a continuation of the discussion in !1954 (comment 229057).