Arity decrease through eta reduction
While working on !8277 (merged)/#20836 (closed) I came up with the following program:
import GHC.Exts
f, g :: a -> a
f = g
g x = f x
{-# NOINLINE f #-}
{-# NOINLINE g #-}
main = lazy g `seq` putStrLn "done"
(The lazy
is just so that we don't discard the seq
.)
The program should print done
and return rapidly. Yet if you compile it with optimisations, you'll see <<loop>>
instead.
The reason is that GHC eta-reduces \x -> f x
to f
, because f
has arity 1 (just like g
). But in doing so, it reduces the arity of g
and consequently of f
to 0! Immediately after in the next
Rec {
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
f [InlPrag=NOINLINE] :: forall a. a -> a
[LclId,
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)}]
f = g
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
g [InlPrag=NOINLINE, Occ=LoopBreaker] :: forall a. a -> a
[LclId,
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)}]
g = f
end Rec }
And that of course lets us fall into a black hole if we evaluate g
, when before it would stop at the lambda.
(Not only has arity decreased to 0, we also keep saying Value=True
for a very long time, hence the seq
won't survive even after we eta-expanded. That's probably an issue that wouldn't arise if we didn't eta-reduce in the first place.)
In !8277 (comment 432209), @simonpj suggests
A plausible solution is to retreat on
Note [Arity robustness]
and zap arity info inzapFragileIdInfo
; test for perf regressions (incl nofib).
We should do that.