## DmdAnal: Don't reduce trivial product demands to polymorphic counterparts

Consider (from #18231)

```
inc :: State Int ()
inc = modify' (+1)
m :: State Int ()
m = forever inc
```

Today, we'll get the following Core after the first round of strictness analysis:

```
Rec {
-- RHS size: {terms: 6, types: 1, coercions: 0, joins: 0/0}
lvl_sLL
:: GHC.Prim.Int# -> Data.Functor.Identity.Identity ((), Int)
[LclId,
Arity=1,
Str=<B>b,
Cpr=b,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True, Guidance=NEVER}]
lvl_sLL
= \ (x_aLn [Dmd=B] :: GHC.Prim.Int#) ->
a'_sLD (GHC.Types.I# (GHC.Prim.+# x_aLn 1#))
-- RHS size: {terms: 6, types: 3, coercions: 0, joins: 0/0}
a'_sLD [Occ=LoopBreaker]
:: Int -> Data.Functor.Identity.Identity ((), Int)
[LclId,
Arity=1,
Str=<SB>b,
Cpr=b,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True, Guidance=NEVER}]
a'_sLD
= \ (eta2_aL7 [Dmd=SB] :: Int) ->
case eta2_aL7 of { GHC.Types.I# x_aLn [Dmd=B] -> lvl_sLL x_aLn }
end Rec }
-- RHS size: {terms: 1, types: 0, coercions: 5, joins: 0/0}
m :: State Int ()
[LclIdX,
Arity=1,
Str=<SB>b,
Cpr=b,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=0,unsat_ok=True,boring_ok=True)}]
m = a'_sLI
`cast` (Sym (Control.Monad.Trans.State.Strict.N:StateT[0]
<Int>_N <Data.Functor.Identity.Identity>_R <()>_N)
:: (Int -> Data.Functor.Identity.Identity ((), Int))
~R# StateT Int Data.Functor.Identity.Identity ())
```

Note the demand `SB`

on the argument to `a'`

. Since it is not a product demand, we will not unbox it and go down a strange path of subsequent transformations, outlined here, that ultimately leads to the following simplified Core:

```
Rec {
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
lvl_rJM :: Data.Functor.Identity.Identity ((), Int)
[GblId, Str=b, Cpr=b]
lvl_rJM = lvl_rJM
end Rec }
-- RHS size: {terms: 5, types: 3, coercions: 0, joins: 0/0}
T18231.m1 :: Int -> Data.Functor.Identity.Identity ((), Int)
[GblId, Arity=1, Str=<SB>b, Cpr=b, Unf=OtherCon []]
T18231.m1
= \ (eta2_aLc :: Int) ->
case eta2_aLc of { GHC.Types.I# x_aLs -> lvl_rJM }
```

The endless loop has been replaced by a looping thunk that will crash rather than loop. It's not devastating, because it's just trading one imprecise bottom (divergence) for another (imprecise exception).

But if we instead said `PS(B)`

for the demand on `a'`

's argument above (so, a product demand), we'd unbox the integer (remember that product demands convey boxity information, e.g. that the thing is used unboxed) and will preserve the endless loop until code gen and get something like

```
$wa'_sLj = \ (eta2_aKN :: (# #)) -> $wa'_sLj eta2_aKN
a'_sLj
= \ (eta2_aKN [Dmd=<SP(B)>] :: Int) ->
case eta2_aKN of { GHC.Types.I# x_aL3 [Dmd=B] ->
$wa'_sLj (# #)
}
```

As I said above, this is not yet enough to make a case. But why don't we give `a'`

's argument the more natural product demand in the first place?

The reason is the use of `mkProd`

in the `Case`

case of demand analysis. It performs rewrites along the "semantic equality" `P(d,d,...) === d`

for any polymorphic demand `d`

. It's the single remaining call site of `mkProd`

. The thing is, since `P`

also conveys boxity information, that semantic equality actually isn't a semantic equality anymore! That's also encoded in `Note [Don't optimise UP(U,U,...) to U]`

and `mkProd`

will actually only perform the rewrite if `d`

is `B`

or `A`

.

I argue we drop that function completely and just unbox `P(B,B,...)`

as well as `P(A,A,...)`

. It's worth it just for being able to delete `mkProd`

(and adjust `Note [Don't optimise UP(U,U,...) to U]`

so that it says why `P(d,d,...) === d`

*does not* hold).

Edit: I just realise that we have this special little function that we call in `GHC.Core.Opt.WorkWrap.Utils.wantToUnbox`

:

```
split_prod_dmd_arity dmd arity
-- For seqDmd, it should behave like <S(AAAA)>, for some
-- suitable arity
| isSeqDmd dmd = Just (replicate arity absDmd)
| _ :* Prod ds <- dmd = Just ds
| otherwise = Nothing
```

Note how it painstakingly *reverses* the collapsing of `S(AAAA)`

into `SA`

introduced in `mkProd`

! So really we are only talking about whether or not we want to collapse `S(BBBB)`

into `SB`

.