Strictness signatures lie about boxing
Consider these two definitions
-- Str=<1L><1!P(L,L,L,A,A)><L><L><L><L><L><L><L><L>,
f True (x,y,z,_,_) b1 b2 b3 b4 b5 b6 b7 b8 = (x,y,z, b1, b2, b3, b4, b5, b6, b7, b8)
f False at b1 b2 b3 b4 b5 b6 b7 b8 = f True at b1 b2 b3 b4 b5 b6 b7 b8
-- Str=<1L><1!P(L,L,L,A,A)><L><L><L>,
g True (x,y,z,_,_) b1 b2 b3 = (x,y,z, b1, b2, b3)
g False at b1 b2 b3 = g True at b1 b2 b3
Both strictness signatures claim to unbox the second argument. But in fact f
has too many
arguments to worker/wrapper, so we get no worker/wrapper for f
at all. But we do for g
.
TL;DR: f's strictness signature lies about boxing.
It turns out that this bug means that fixing #20746 (closed) is not enough to fix boxing
in GHC.IO.Handle.Text.hGetBuf
. Why? Because demand analysis sees:
let {
act_s4jT [Dmd=LCL(C1(L))]
:: Handle__ -> State# RealWorld -> (# State# RealWorld, Int #)
[LclId,
Arity=2,
Str=<1!P(LP(LCL(C1(C1(C1(C1(P(L,1P(1L))))))),A,A,A),A,LP(A,LCL(C1(C1(P(L,1P(1P(1L),L))))),A,A,A,A),A,L,A,L,A,L,L,A,A,ML,A,A,A,A)><L> {a3Ym->M!L},
Unf=Unf{Src=<vanilla>, TopLvl=False, Value=True, ConLike=True,
WorkFree=True, Expandable=True, Guidance=IF_ARGS [20 0] 222 0}]
act_s4jT
= \ (h__a1Zv [Dmd=1!P(LP(LCL(C1(C1(C1(C1(P(L,1P(1L))))))),A,A,A),A,LP(A,LCL(C1(C1(P(L,1P(1P(1L),L))))),A,A,A,A),A,L,A,L,A,L,L,A,A,ML,A,A,A,A)]
:: Handle__)
(eta_X3 [OS=OneShot] :: State# RealWorld) ->
case h__a1Zv
of wild_X4 [Dmd=M!P(L!P(LCL(C1(C1(C1(C1(P(L,1P(1L))))))),A,A,A),A,L!P(A,LCL(C1(C1(P(L,1P(1P(1L),L))))),A,A,A,A),A,L!L,A,L!L,A,A,A,A,A,A,A,A,A,A)]
{ Handle__ @dev_a2Ph @enc_state_a2Pi @dec_state_a2Pj
$dRawIO_a2Pk [Dmd=L!P(LCL(C1(C1(C1(C1(P(L,1P(1L))))))),A,A,A)]
$dIODevice_a2Pl [Dmd=A]
$dBufferedIO_a2Pm [Dmd=L!P(A,LCL(C1(C1(P(L,1P(1P(1L),L))))),A,A,A,A)]
$dTypeable_a2Pn [Dmd=A] ds_d3ri [Dmd=L!L] ds_d3rj [Dmd=A] bx_d3J4
ds_d3rl [Dmd=A] bx_d3J5 bx_d3J6 bx_d3J7 [Dmd=A] ds_d3rp [Dmd=A]
ds_d3rq [Dmd=ML] ds_d3rr [Dmd=A] ds_d3rs [Dmd=A] ds_d3rt [Dmd=A]
ds_d3ru [Dmd=A] ->
case GHC.IO.Handle.Internals.$wflushCharReadBuffer
@dev_a2Ph
@enc_state_a2Pi
@dec_state_a2Pj
bx_d3J4
bx_d3J5
bx_d3J6
ds_d3rq
eta_X3
of ww17_a4f0
{ __DEFAULT ->
case readMutVar# @RealWorld @(Buffer Word8) bx_d3J4 ww17_a4f0 of
{ (# ipv_a3WM, ipv1_a3WN [Dmd=1!L] #) ->
case ipv1_a3WN of wild_X5 [Dmd=1!L]
{ Buffer bx_d3J8 bx_d3J9 ds_d3rd bx_d3Ja bx_d3Jb bx_d3Jc bx_d3Jd ->
case ==# bx_d3Jc bx_d3Jd of {
__DEFAULT -> bufReadNonEmpty_s4fR wild_X4 ...blah...
1# -> bufReadEmpty_s4fD wild_X4 ...blah...
} }}}}
and those bufReadNonEmpty
and bufReadEmpty
calls are precisely like f
: they claim to unbox wild_X4
but in fact do not do so
becuase of too many arguments.
Result: hGetBuf
allocates a lot more, for reasons described in #20746 (closed).