FloatOut reverts eta expansion of thunks
Call Arity works hard to identify thunks that can be eta expanded, because they are only called once, with arity > 0. As it does for lvl
this example
{-# OPTIONS_GHC -O2 -fforce-recomp #-}
{-# LANGUAGE BangPatterns #-}
module Lib (main) where
expensive :: a -> a
expensive x = x
{-# NOINLINE expensive #-}
lvl :: Int -> Int
lvl = expensive (+ 42)
{-# NOINLINE lvl #-}
go :: Int -> Int -> Int
go 0 x = lvl x
go 1 x = lvl (x+1)
go n x = go (n-1) (x+1)
res :: Int
res = go 15 25
main :: IO ()
main = print res
Before SetLevels (after Call Arity), indeed there is
lvl
= \ (eta_B1 :: Int) ->
expensive
@ (Int -> Int)
(\ (ds_d2K7 :: Int) ->
case ds_d2K7 of { GHC.Types.I# x_a2KB [Dmd=<S,U>] ->
GHC.Types.I# (GHC.Prim.+# x_a2KB 42#)
})
eta_B1
lvl
got eta expanded. But if you look at the output of -ddump-simpl
, you'll see
lvl2_r3jd = expensive_rqN @ (Int -> Int) lvl1_r3dx
which is an un-eta-expanded form of lvl
above. lvl2
is due to FloatOut, which identifies expensive (+ 42)
as a MFE of lvl = \eta. expensive (+ 42) eta
.
I'm proposing that lvl
should stay eta expanded. Here's how to ensure that:
That \eta.
should have a one-shot annotation. The only world in which it is OK to eta-expand a thunk is when the new manifest lambdas are one-shot (and that the thunk is not otherwise seq
ed, of course). And then we have to make sure that we preserve that one-shot-ness (#18355), so that FloatIn and the Simplifier will be able to float it back in.
Preserving that one-shot-ness is the hard part, but fortunately that seems to have been solved by Kinds are Calling Conventions.