Skip to content

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.

Edited by Sebastian Graf
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information