INLINE-unfoldings are not the precise RHS.
Consider Bug.hs
:
module Bug where
import Data.ByteString (ByteString)
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
countChars :: ByteString -> Int
countChars = T.length . T.toUpper . TE.decodeUtf8
{-# INLINE countChars #-}
If I compile this module with GHC-9.8.2 and print the resulting interface
% ghc-9.8.2 -O -fforce-recomp -c Bug.hs
% ghc-9.8.2 --show-iface Bug.hi
I see something like:
countChars ::
Data.ByteString.Internal.Type.ByteString -> GHC.Types.Int
[LambdaFormInfo: LFReEntrant 1, Arity: 1, Strictness: <1L>, CPR: 1,
Inline: (sat-args=0),
Unfolding: Core: StableUser <0,FalseTrue>
\ (eta['GHC.Types.Many] :: Data.ByteString.Internal.Type.ByteString) ->
let {
t :: Data.Text.Internal.Text [] = Data.Text.Encoding.decodeUtf8 eta
} in
case Data.Text.null t of wild {
GHC.Types.False
-> case t of wild1 { Data.Text.Internal.Text bx bx1 bx2 ->
letrec {
....
here, the T.toUpper
(which is marked as INLINE
) is inlined into the unfolding.
The documention says
So GHC guarantees to inline precisely the code that you wrote, no more and no less. It does this by capturing a copy of the definition of the function to use for inlining (we call this the “inline-RHS”), which it leaves untouched, while optimising the ordinarily RHS as usual. For externally-visible functions the inline-RHS (not the optimised RHS) is recorded in the interface file.
I find the documentation and reality to be different.
I understand that inlining INLINE
-marked stuff into inline-RHS makes sense (as those would be inlined), but then the documentation should have a remark. In this case this bloats interface files. T.toUpper
is "big".
I don't like that toUpper
inner-loop is forcefully inlined (it results in code bloat), but it's another (text
's) issue.