DmdAnal: `dmdAnalStar` could yield better usage for trivial args and bindings
Consider
f :: (Bool, Bool) -> (Bool, Bool)
f pr = (pr `seq` True, case pr of (a,b) -> a && b)
{-# NOINLINE f #-}
-- idDmdSig f = LP(1L,1L)
g :: (Bool, Bool) -> ()
g pr = f pr `seq` ()
-- idDmdSig g = L
g simply calls f, yet g puts a worse demand on pr than f; it loses the information that pr's components are only evaluated once.
That is due to dmdAnalStar e (n :* sd), which is used when analysing the argument pr in the call f pr in demand LP(1L,1L). That will
- Call
dmdAnal "pr" "P(1L,1L)", as ifprwas evaluated exactly once in sub-demandP(1L,1L). We get back[pr |-> 1P(1L,1L)](which of course is too optimistic). - Then it worsens the resulting demand type to account for
n=L, by multiplying it withLviamultDmdType. That ultimately callsmultDmd "L"on the demand ofx:multDmd L 1P(1L,1L) === LP(LL,LL) === L.
It's alright to turn the outer 1P(..) into LP(..), but we don't actually need to worsen the inner P(1L,1L), because it is the exact demand that is put on the argument already.
NB: This ticket is specifically concerned with upper bounds. The multDmd "L" situation (with a used more than once upper boudn) only occurs when an arg pr is trivial (or if the RHS of a bindings is trivial). Otherwise, dmdTransformThunkDmd anticipates the memoisation of complex expressions and call oneifyDmd instead. So if we had a call like f (x, True) instead, then we'd get dmdAnalStar "(x, True)" "1P(1L,1L)", e.g., with the oneified demand 1P(..) and all will be well, as we ultimately get a demand of 1L on x.