Case-of-known-constructor is broken in GHC 8.10 for floated DataCon wrappers
As the issue title states, case-of-known-constructor does *not* fire if a `case` expression scrutinizes a floated-out value built from a DataCon wrapper. This program reproduces the issue:
```haskell
module KnownCon where
data T = D !Bool
f :: T -> T -> T
f (D a) (D b) = D (a && b)
{-# INLINE [100] f #-} -- so it isn’t inlined before FloatOut
g :: Bool -> T
g x = f (D True) (D x)
```
To see the bad behavior, we have to compile with `-dverbose-core2core`, since we need to see the output of simplifier phase 2 (or phase 1). Inspecting it shows that `D True` is floated out of `g` by FloatOut, and even though `f` is inlined, case-of-known-constructor does *not* fire!
```haskell
==================== Simplifier ====================
Max iterations = 4
SimplMode {Phase = 2 [main],
inline,
rules,
eta-expand,
case-of-case}
lvl_s1gm :: T
[LclId,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=False, ConLike=False,
WorkFree=False, Expandable=False, Guidance=IF_ARGS [] 20 0}]
lvl_s1gm = $WD True
g :: Bool -> T
[LclIdX,
Arity=1,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True, Guidance=IF_ARGS [20] 80 0}]
g = \ (x_a1eV :: Bool) ->
case lvl_s1gm of { D a_atX ->
case x_a1eV of dt_X0 { __DEFAULT ->
case a_atX of {
False -> $WD False;
True -> $WD dt_X0
}
}
}
```
This seems quite bad to me. It is a regression from GHC 8.8, since GHC 8.8 inlines DataCon wrappers much more aggressively.
---
The source of this issue appears to be the `Expandable=False` in the floated-out binding’s unfolding. The root cause is `isExpandableApp`, which does not classify applications of DataCon wrappers as expandable:
```haskell
isExpandableApp :: CheapAppFun
isExpandableApp fn n_val_args
| isWorkFreeApp fn n_val_args = True
| otherwise
= case idDetails fn of
DataConWorkId {} -> True -- Actually handled by isWorkFreeApp
RecSelId {} -> n_val_args == 1 -- See Note [Record selection]
ClassOpId {} -> n_val_args == 1
PrimOpId {} -> False
_ | isBottomingId fn -> False
-- See Note [isExpandableApp: bottoming functions]
| isConLike (idRuleMatchInfo fn) -> True
| all_args_are_preds -> True
| otherwise -> False
```
One point of note is that DataCon wrappers do not appear to be considered “conlike”; `isConLikeId` returns `True` on DataCon workers and ids with user-defined `CONLIKE` pragmas, but it returns `False` on DataCon wrappers. I suppose it is possible to argue in favor of this behavior, as it’s true that a DataCon wrapper is not work-free! But it is not an especially large amount of work, and the purpose of `CONLIKE` is to annotate something as worth duplicating *if and only if it exposes further optimizations*. From that perspective, DataCon wrappers certainly seem conlike to me.
In any case, DataCon wrappers definitely ought to be *expandable*, even if they are not conlike. In fact, `Note [exprIsConApp_maybe on data constructors with wrappers]` assumes they are:
```
1. Inline $WMkT on-the-fly. That's why data-constructor wrappers are marked
as expandable. (See GHC.Core.Utils.isExpandableApp.) Now we have
```
So this seems clearly a mistake to me.
issue