DmdAnal: Handling of RULES and unfolding free variables might be broken
While working on !7044, I realised that our current keepAlive*
logic might be broken.
In particular, we have code of this form:
-- See Note [Absence analysis for stable unfoldings and RULES]
rule_fvs = bndrRuleAndUnfoldingIds id
final_ty = body_ty' `plusDmdType` rhs_ty `keepAliveDmdType` rule_fvs
....
-- See Note [Absence analysis for stable unfoldings and RULES]
rhs_fv2 = rhs_fv1 `keepAliveDmdEnv` bndrRuleAndUnfoldingIds id
In dmdAnalBindLetUp
and dmdAnalRhsSig
(called from dmdAnalBindLEtDown
), respectively.
This code fails to unleash demands on free variables in the demand signature of those FVs! E.g., if we could write
foo :: Integer -> Integer
foo a = f a
where
z :: Integer
z = 0
{-# NOINLINE z #-}
g :: Integer -> Integer -> Integer
g x y = x+z
{-# NOINLINE g #-}
f :: Integer -> Integer
f x = x+a
{-# NOINLINE[0] f #-}
{-# RULES "f to g" f 0 = g 0 #-}
(which we can't because RULES on local bindings don't work), then we'd do the following:
- Compute a signature for
g
,<1L>{z->1L>}
. Note that it is strict in its FVx
. - Analyse
f
's RHS. Get DmdType<1L>
. Note that it doesn't mentiong
! - Now we do the
keepAliveDmdEnv
on{g}
, the only FV off
's RULE. This entails simply addingg
to theDmdEnv
off
. We get the final signature<1L>{g->L}
. - Analyse and unleash
f
s signature at the callf a
. - ... Leave scopes of
f
andg
, properly annotating them ... 6.Realise thatz
is absent. Wrong wrong wrong!! This might lead to a crash whenever we rewritef
tog
.
This won't work if lambda-lift f
and g
, because then we'll automatically record many-uses that end up in the lazy_fv
s. In particular, we'd automatically record a usage of z->L
when we add the lazy_fv's of g
; z->1L
would never be part of g
's signature.
It also doesn't work as written, because we can't declare RULES on nested functions.
So I haven't been able to actually reproduce the issue in production. Nevertheless, the danger is there! And !7044 trips over it, because it considers no variable as weak. So it will actually give g
the sig <1L>{z->1L>}
, which we fail to unleash as part of keepAliveDmdEnv
.