Cast WW of a NOINLINE function should mark the worker as NOINLINE, not the wrapper
Consider
module Lib where
import Control.Monad.ST
f :: IO ()
f = return ()
{-# NOINLINE f #-}
g :: ST s ()
g = return ()
{-# NOINLINE g #-}
Compiled with -O2
, we get
Lib.g1
:: forall s. GHC.Prim.State# s -> (# GHC.Prim.State# s, () #)
Lib.g1
= \ (@ s_a1oA) (s1_X1qD :: GHC.Prim.State# s_a1oA) ->
(# s1_X1qD, GHC.Tuple.() #)
g [InlPrag=NOINLINE] :: forall s. ST s ()
g = Lib.g1
`cast` (forall (s :: <*>_N). Sym (GHC.ST.N:ST[0] <s>_N <()>_R)
:: (forall s. GHC.ST.STRep s ()) ~R# (forall s. ST s ()))
Lib.f1
:: GHC.Prim.State# RealWorld -> (# GHC.Prim.State# RealWorld, () #)
Lib.f1
= \ (s_a1r4 :: GHC.Prim.State# RealWorld) ->
(# s_a1r4, GHC.Tuple.() #)
f [InlPrag=NOINLINE] :: IO ()
f = Lib.f1
`cast` (Sym (GHC.Types.N:IO[0] <()>_R)
:: (GHC.Prim.State# RealWorld
-> (# GHC.Prim.State# RealWorld, () #))
~R# IO ())
Note that f
and g
(the wrappers) are marked NOINLINE
instead of Lib.f1
and Lib.g1
(the workers wrt. casts/eta expansion), respectively. This might not look harmful in itself, but it inhibits NOINLINE
function in IO
and ST
from exposing wrappers as a result from strictness analysis (which is useful, #13143 (closed)), as the following example demonstrates:
module Lib where
h :: Int -> IO ()
h x = do
x `seq` return ()
print "foo"
print "foo"
print "foo"
{-# NOINLINE h #-}
Lib.$wh
:: Int
-> GHC.Prim.State# RealWorld -> (# GHC.Prim.State# RealWorld, () #)
Lib.$wh = <elided>
Lib.h1
:: Int
-> GHC.Prim.State# RealWorld -> (# GHC.Prim.State# RealWorld, () #)
Lib.h1
= \ (w_s1MP :: Int) (w1_s1MQ :: GHC.Prim.State# RealWorld) ->
case w_s1MP of w2_X1Ni { GHC.Types.I# ipv_s1MX ->
Lib.$wh w2_X1Ni w1_s1MQ
}
h [InlPrag=NOINLINE] :: Int -> IO ()
h = Lib.h1
`cast` (<Int>_R ->_R Sym (GHC.Types.N:IO[0] <()>_R)
:: (Int
-> GHC.Prim.State# RealWorld
-> (# GHC.Prim.State# RealWorld, () #))
~R# (Int -> IO ()))
in #13143 (closed) we decided to mark the worker as NOINLINE, so we should do the same for eta expansion.