diff options
author | Sebastian Graf <sebastian.graf@kit.edu> | 2022-06-07 11:31:22 +0200 |
---|---|---|
committer | Sebastian Graf <sebastian.graf@kit.edu> | 2022-06-20 09:43:32 +0200 |
commit | 49fb2f9b16ca987648d2ac57eecf1892d49852ec (patch) | |
tree | 5bad0a2814b084d1811ca89ce0e6d70ae837dcbf | |
parent | b570da84b7aad5ca3f90f2d1c1a690c927e99fe9 (diff) | |
download | haskell-49fb2f9b16ca987648d2ac57eecf1892d49852ec.tar.gz |
Simplify: Take care with eta reduction in recursive RHSs (#21652)
Similar to the fix to #20836 in CorePrep, we now track the set of enclosing
recursive binders in the SimplEnv and SimpleOptEnv.
See Note [Eta reduction in recursive RHSs] for details.
I also updated Note [Arity robustness] with the insights Simon and I had in a
call discussing the issue.
Fixes #21652.
Unfortunately, we get a 5% ghc/alloc regression in T16577. That is due to
additional eta reduction in GHC.Read.choose1 and the resulting ANF-isation
of a large list literal at the top-level that didn't happen before (presumably
because it was too interesting to float to the top-level). There's not much we
can do about that.
Metric Increase:
T16577
-rw-r--r-- | compiler/GHC/Core/Opt/Arity.hs | 87 | ||||
-rw-r--r-- | compiler/GHC/Core/Opt/Simplify.hs | 8 | ||||
-rw-r--r-- | compiler/GHC/Core/Opt/Simplify/Env.hs | 52 | ||||
-rw-r--r-- | compiler/GHC/Core/Opt/Simplify/Utils.hs | 3 | ||||
-rw-r--r-- | compiler/GHC/Core/SimpleOpt.hs | 34 | ||||
-rw-r--r-- | testsuite/tests/arityanal/should_compile/Arity03.stderr | 7 | ||||
-rw-r--r-- | testsuite/tests/arityanal/should_run/Makefile | 3 | ||||
-rw-r--r-- | testsuite/tests/arityanal/should_run/T21652.hs | 10 | ||||
-rw-r--r-- | testsuite/tests/arityanal/should_run/T21652.stdout | 1 | ||||
-rw-r--r-- | testsuite/tests/arityanal/should_run/all.T | 2 | ||||
-rw-r--r-- | testsuite/tests/deSugar/should_compile/T19969.stderr | 14 |
11 files changed, 154 insertions, 67 deletions
diff --git a/compiler/GHC/Core/Opt/Arity.hs b/compiler/GHC/Core/Opt/Arity.hs index 5858ff91e0..5edc5e5bb0 100644 --- a/compiler/GHC/Core/Opt/Arity.hs +++ b/compiler/GHC/Core/Opt/Arity.hs @@ -73,6 +73,7 @@ import GHC.Builtin.Types.Prim import GHC.Builtin.Uniques import GHC.Data.FastString +import GHC.Data.Graph.UnVar import GHC.Data.Pair import GHC.Utils.GlobalVars( unsafeHasNoStateHack ) @@ -2043,15 +2044,6 @@ This test is made by `ok_fun` in tryEtaReduce. with both type and dictionary lambdas; hence the slightly ad-hoc (all ok_lam bndrs) -3. (See fun_arity in tryEtaReduce.) We have to hide `f`'s `idArity` in - its own RHS, lest we suffer from the last point of Note [Arity - robustness] in GHC.Core.Opt.Simplify.Env. There we have `f = \x. f x` - and we should not eta-reduce to `f=f`. Which might change a - terminating program (think @f `seq` e@) to a non-terminating one. - So we check for being a loop breaker first. However for GlobalIds - we can look at the arity; and for primops we must, since they have - no unfolding. [SG: Perhaps this is rather a soundness subtlety?] - Of course, eta reduction is not always sound. See Note [Eta reduction soundness] for when it is. @@ -2133,6 +2125,12 @@ case where `e` is trivial): See Note [Eta reduction based on evaluation context] for the implementation details. This criterion is tested extensively in T21261. + R. Note [Eta reduction in recursive RHSs] tells us that we should not + eta-reduce `f` in its own RHS and describes our fix. + There we have `f = \x. f x` and we should not eta-reduce to `f=f`. Which + might change a terminating program (think @f `seq` e@) to a non-terminating + one. + E. (See fun_arity in tryEtaReduce.) As a perhaps special case on the boundary of (A) and (S), when we know that a fun binder `f` is in WHNF, we simply assume it has arity 1 and apply (A). Example: @@ -2276,15 +2274,73 @@ Then this is how the pieces are put together: sub-demands `Cn` and see whether all of the n's (here: `S=C_1N` and `1=C_11`) were strict. And strict they are! Thus, it will eta-reduce `\x y. e x y` to `e`. + +Note [Eta reduction in recursive RHSs] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Consider the following recursive function: + f = \x. ....g (\y. f y).... +The recursive call of f in its own RHS seems like a fine opportunity for +eta-reduction because f has arity 1. And often it is! + +Alas, that is unsound in general if the eta-reduction happens in a tail context. +Making the arity visible in the RHS allows us to eta-reduce + f = \x -> f x +to + f = f +which means we optimise terminating programs like (f `seq` ()) into +non-terminating ones. Nor is this problem just for tail calls. Consider + f = id (\x -> f x) +where we have (for some reason) not yet inlined `id`. We must not eta-reduce to + f = id f +because that will then simplify to `f = f` as before. + +An immediate idea might be to look at whether the called function is a local +loopbreaker and refrain from eta-expanding. But that doesn't work for mutually +recursive function like in #21652: + f = g + g* x = f x +Here, g* is the loopbreaker but f isn't. + +What can we do? + +Fix 1: Zap `idArity` when analysing recursive RHSs and re-attach the info when + entering the let body. + Has the disadvantage that other transformations which make use of arity + (such as dropping of `seq`s when arity > 0) will no longer work in the RHS. + Plus it requires non-trivial refactorings to both the simple optimiser (in + the way `subst_opt_bndr` is used) as well as the Simplifier (in the way + `simplRecBndrs` and `simplRecJoinBndrs` is used), modifying the SimplEnv's + substitution twice in the process. A very complicated stop-gap. + +Fix 2: Pass the set of enclosing recursive binders to `tryEtaReduce`; these are + the ones we should not eta-reduce. All call-site must maintain this set. + Example: + rec { f1 = ....rec { g = ... (\x. g x)...(\y. f2 y)... }... + ; f2 = ...f1... } + when eta-reducing those inner lambdas, we need to know that we are in the + rec group for {f1, f2, g}. + This is very much like the solution in Note [Speculative evaluation] in + GHC.CoreToStg.Prep. + It is a bit tiresome to maintain this info, because it means another field + in SimplEnv and SimpleOptEnv. + +We implement Fix (2) because of it isn't as complicated to maintain as (1). +Plus, it is the correct fix to begin with. After all, the arity is correct, +but doing the transformation isn't. The moving parts are: + * A field `scRecIds` in `SimplEnv` tracks the enclosing recursive binders + * We extend the `scRecIds` set in `GHC.Core.Opt.Simplify.simplRecBind` + * We consult the set in `is_eta_reduction_sound` in `tryEtaReduce` +The situation is very similar to Note [Speculative evaluation] which has the +same fix. -} -- | `tryEtaReduce [x,y,z] e sd` returns `Just e'` if `\x y z -> e` is evaluated -- according to `sd` and can soundly and gainfully be eta-reduced to `e'`. -- See Note [Eta reduction soundness] -- and Note [Eta reduction makes sense] when that is the case. -tryEtaReduce :: [Var] -> CoreExpr -> SubDemand -> Maybe CoreExpr +tryEtaReduce :: UnVarSet -> [Var] -> CoreExpr -> SubDemand -> Maybe CoreExpr -- Return an expression equal to (\bndrs. body) -tryEtaReduce bndrs body eval_sd +tryEtaReduce rec_ids bndrs body eval_sd = go (reverse bndrs) body (mkRepReflCo (exprType body)) where incoming_arity = count isId bndrs -- See Note [Eta reduction makes sense], point (2) @@ -2347,9 +2403,11 @@ tryEtaReduce bndrs body eval_sd --------------- -- See Note [Eta reduction soundness], this is THE place to check soundness! is_eta_reduction_sound fun = + -- Don't eta-reduce in fun in its own recursive RHSs + not (fun `elemUnVarSet` rec_ids) -- criterion (R) -- Check that eta-reduction won't make the program stricter... - (fun_arity fun >= incoming_arity -- criterion (A) and (E) - || all_calls_with_arity incoming_arity) -- criterion (S) + && (fun_arity fun >= incoming_arity -- criterion (A) and (E) + || all_calls_with_arity incoming_arity) -- criterion (S) -- ... and that the function can be eta reduced to arity 0 -- without violating invariants of Core and GHC && canEtaReduceToArity fun 0 0 -- criteria (L), (J), (W), (B) @@ -2358,9 +2416,6 @@ tryEtaReduce bndrs body eval_sd --------------- fun_arity fun - | isLocalId fun - , isStrongLoopBreaker (idOccInfo fun) = 0 - -- See Note [Eta reduction makes sense], point (3) | arity > 0 = arity | isEvaldUnfolding (idUnfolding fun) = 1 -- See Note [Eta reduction soundness], criterion (E) diff --git a/compiler/GHC/Core/Opt/Simplify.hs b/compiler/GHC/Core/Opt/Simplify.hs index c44fd1d62a..b5dac4d385 100644 --- a/compiler/GHC/Core/Opt/Simplify.hs +++ b/compiler/GHC/Core/Opt/Simplify.hs @@ -261,9 +261,11 @@ simplRecBind :: SimplEnv -> BindContext -> [(InId, InExpr)] -> SimplM (SimplFloats, SimplEnv) simplRecBind env0 bind_cxt pairs0 - = do { (env_with_info, triples) <- mapAccumLM add_rules env0 pairs0 - ; (rec_floats, env1) <- go env_with_info triples - ; return (mkRecFloats rec_floats, env1) } + = do { (env1, triples) <- mapAccumLM add_rules env0 pairs0 + ; let new_bndrs = map sndOf3 triples + ; (rec_floats, env2) <- enterRecGroupRHSs env1 new_bndrs $ \env -> + go env triples + ; return (mkRecFloats rec_floats, env2) } where add_rules :: SimplEnv -> (InBndr,InExpr) -> SimplM (SimplEnv, (InBndr, OutBndr, InExpr)) -- Add the (substituted) rules to the binder diff --git a/compiler/GHC/Core/Opt/Simplify/Env.hs b/compiler/GHC/Core/Opt/Simplify/Env.hs index 5defa782e0..30afb9aac2 100644 --- a/compiler/GHC/Core/Opt/Simplify/Env.hs +++ b/compiler/GHC/Core/Opt/Simplify/Env.hs @@ -17,7 +17,7 @@ module GHC.Core.Opt.Simplify.Env ( zapSubstEnv, setSubstEnv, bumpCaseDepth, getInScope, setInScopeFromE, setInScopeFromF, setInScopeSet, modifyInScope, addNewInScopeIds, - getSimplRules, + getSimplRules, enterRecGroupRHSs, -- * Substitution results SimplSR(..), mkContEx, substId, lookupRecBndr, @@ -56,6 +56,7 @@ import GHC.Types.Var import GHC.Types.Var.Env import GHC.Types.Var.Set import GHC.Data.OrdList +import GHC.Data.Graph.UnVar import GHC.Types.Id as Id import GHC.Core.Make ( mkWildValBinder, mkCoreLet ) import GHC.Driver.Session ( DynFlags ) @@ -97,6 +98,10 @@ data SimplEnv , seCvSubst :: CvSubstEnv -- InCoVar |--> OutCoercion , seIdSubst :: SimplIdSubst -- InId |--> OutExpr + -- | Fast OutVarSet tracking which recursive RHSs we are analysing. + -- See Note [Eta reduction in recursive RHSs] in GHC.Core.Opt.Arity. + , seRecIds :: !UnVarSet + ----------- Dynamic part of the environment ----------- -- Dynamic in the sense of describing the setup where -- the expression finally ends up @@ -287,6 +292,7 @@ mkSimplEnv mode , seTvSubst = emptyVarEnv , seCvSubst = emptyVarEnv , seIdSubst = emptyVarEnv + , seRecIds = emptyUnVarSet , seCaseDepth = 0 } -- The top level "enclosing CC" is "SUBSUMED". @@ -392,6 +398,13 @@ modifyInScope :: SimplEnv -> CoreBndr -> SimplEnv modifyInScope env@(SimplEnv {seInScope = in_scope}) v = env {seInScope = extendInScopeSet in_scope v} +enterRecGroupRHSs :: SimplEnv -> [OutBndr] -> (SimplEnv -> SimplM (r, SimplEnv)) + -> SimplM (r, SimplEnv) +enterRecGroupRHSs env bndrs k = do + --pprTraceM "enterRecGroupRHSs" (ppr bndrs) + (r, env'') <- k env{seRecIds = extendUnVarSetList bndrs (seRecIds env)} + return (r, env''{seRecIds = seRecIds env}) + {- Note [Setting the right in-scope set] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Consider @@ -892,31 +905,18 @@ seqIds (id:ids) = seqId id `seq` seqIds ids Note [Arity robustness] ~~~~~~~~~~~~~~~~~~~~~~~ We *do* transfer the arity from the in_id of a let binding to the -out_id. This is important, so that the arity of an Id is visible in -its own RHS. For example: - f = \x. ....g (\y. f y).... -We can eta-reduce the arg to g, because f is a value. But that -needs to be visible. - -This interacts with the 'state hack' too: - f :: Bool -> IO Int - f = \x. case x of - True -> f y - False -> \s -> ... -Can we eta-expand f? Only if we see that f has arity 1, and then we -take advantage of the 'state hack' on the result of -(f y) :: State# -> (State#, Int) to expand the arity one more. - -There is a disadvantage though. Making the arity visible in the RHS -allows us to eta-reduce - f = \x -> f x -to - f = f -which technically is not sound. We take care of that in point (3) of -Note [Eta reduction makes sense]. -Another idea is to ensure that f's arity never decreases; its arity started as -1, and we should never eta-reduce below that. - +out_id so that its arity is visible in its RHS. Examples: + + * f = \x y. let g = \p q. f (p+q) in Just (...g..g...) + Here we want to give `g` arity 3 and eta-expand. `findRhsArity` will have a + hard time figuring that out when `f` only has arity 0 in its own RHS. + * f = \x y. ....(f `seq` blah).... + We want to drop the seq. + * f = \x. g (\y. f y) + You'd think we could eta-reduce `\y. f y` to `f` here. And indeed, that is true. + Unfortunately, it is not sound in general to eta-reduce in f's RHS. + Example: `f = \x. f x`. See Note [Eta reduction in recursive RHSs] for how + we prevent that. Note [Robust OccInfo] ~~~~~~~~~~~~~~~~~~~~~ diff --git a/compiler/GHC/Core/Opt/Simplify/Utils.hs b/compiler/GHC/Core/Opt/Simplify/Utils.hs index d0a7abb84f..f209713de7 100644 --- a/compiler/GHC/Core/Opt/Simplify/Utils.hs +++ b/compiler/GHC/Core/Opt/Simplify/Utils.hs @@ -1662,6 +1662,7 @@ rebuildLam env bndrs body cont ; try_eta dflags bndrs body } where mode = getMode env + rec_ids = seRecIds env in_scope = getInScope env -- Includes 'bndrs' mb_rhs = contIsRhs cont @@ -1678,7 +1679,7 @@ rebuildLam env bndrs body cont try_eta dflags bndrs body | -- Try eta reduction gopt Opt_DoEtaReduction dflags - , Just etad_lam <- tryEtaReduce bndrs body (eval_sd dflags) + , Just etad_lam <- tryEtaReduce rec_ids bndrs body (eval_sd dflags) = do { tick (EtaReduction (head bndrs)) ; return etad_lam } diff --git a/compiler/GHC/Core/SimpleOpt.hs b/compiler/GHC/Core/SimpleOpt.hs index 2db7ee3373..1d604120b9 100644 --- a/compiler/GHC/Core/SimpleOpt.hs +++ b/compiler/GHC/Core/SimpleOpt.hs @@ -52,6 +52,7 @@ import GHC.Utils.Panic import GHC.Utils.Panic.Plain import GHC.Utils.Misc import GHC.Data.Maybe ( orElse ) +import GHC.Data.Graph.UnVar import Data.List (mapAccumL) import qualified Data.ByteString as BS @@ -191,6 +192,10 @@ data SimpleOptEnv , soe_subst :: Subst -- ^ Deals with cloning; includes the InScopeSet + + , soe_rec_ids :: !UnVarSet + -- ^ Fast OutVarSet tracking which recursive RHSs we are analysing. + -- See Note [Eta reduction in recursive RHSs] } instance Outputable SimpleOptEnv where @@ -200,9 +205,10 @@ instance Outputable SimpleOptEnv where <+> text "}" emptyEnv :: SimpleOpts -> SimpleOptEnv -emptyEnv opts = SOE { soe_inl = emptyVarEnv - , soe_subst = emptySubst - , soe_opts = opts } +emptyEnv opts = SOE { soe_inl = emptyVarEnv + , soe_subst = emptySubst + , soe_rec_ids = emptyUnVarSet + , soe_opts = opts } soeZapSubst :: SimpleOptEnv -> SimpleOptEnv soeZapSubst env@(SOE { soe_subst = subst }) @@ -215,6 +221,13 @@ soeSetInScope :: InScopeSet -> SimpleOptEnv -> SimpleOptEnv soeSetInScope in_scope env2@(SOE { soe_subst = subst2 }) = env2 { soe_subst = setInScope subst2 in_scope } +enterRecGroupRHSs :: SimpleOptEnv -> [OutBndr] -> (SimpleOptEnv -> (SimpleOptEnv, r)) + -> (SimpleOptEnv, r) +enterRecGroupRHSs env bndrs k + = (env'{soe_rec_ids = soe_rec_ids env}, r) + where + (env', r) = k env{soe_rec_ids = extendUnVarSetList bndrs (soe_rec_ids env)} + --------------- simple_opt_clo :: InScopeSet -> SimpleClo @@ -226,6 +239,7 @@ simple_opt_expr :: HasCallStack => SimpleOptEnv -> InExpr -> OutExpr simple_opt_expr env expr = go expr where + rec_ids = soe_rec_ids env subst = soe_subst env in_scope = substInScope subst in_scope_env = (in_scope, simpleUnfoldingFun) @@ -288,14 +302,17 @@ simple_opt_expr env expr ---------------------- -- go_lam tries eta reduction + -- It is quite important that it does so. I tried removing this code and + -- got a lot of regressions, e.g., +11% ghc/alloc in T18223 and many + -- run/alloc increases. Presumably RULEs are affected. go_lam env bs' (Lam b e) = go_lam env' (b':bs') e where (env', b') = subst_opt_bndr env b go_lam env bs' e | so_eta_red (soe_opts env) - , Just etad_e <- tryEtaReduce bs e' topSubDmd = etad_e - | otherwise = mkLams bs e' + , Just etad_e <- tryEtaReduce rec_ids bs e' topSubDmd = etad_e + | otherwise = mkLams bs e' where bs = reverse bs' e' = simple_opt_expr env e @@ -408,12 +425,13 @@ simple_opt_bind env (NonRec b r) top_level (env', mb_pr) = simple_bind_pair env b' Nothing (env,r') top_level simple_opt_bind env (Rec prs) top_level - = (env'', res_bind) + = (env2, res_bind) where res_bind = Just (Rec (reverse rev_prs')) prs' = joinPointBindings_maybe prs `orElse` prs - (env', bndrs') = subst_opt_bndrs env (map fst prs') - (env'', rev_prs') = foldl' do_pr (env', []) (prs' `zip` bndrs') + (env1, bndrs') = subst_opt_bndrs env (map fst prs') + (env2, rev_prs') = enterRecGroupRHSs env1 bndrs' $ \env -> + foldl' do_pr (env, []) (prs' `zip` bndrs') do_pr (env, prs) ((b,r), b') = (env', case mb_pr of Just pr -> pr : prs diff --git a/testsuite/tests/arityanal/should_compile/Arity03.stderr b/testsuite/tests/arityanal/should_compile/Arity03.stderr index f41fc1552c..652fcde173 100644 --- a/testsuite/tests/arityanal/should_compile/Arity03.stderr +++ b/testsuite/tests/arityanal/should_compile/Arity03.stderr @@ -26,12 +26,7 @@ fac = \ (x :: Int) -> case x of { GHC.Types.I# ww -> case F3.$wfac ww of ww1 { _ -- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0} f3 :: Int -> Int -[GblId, - Arity=1, - Str=<1!P(1L)>, - Cpr=1, - Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=True) - Tmpl= fac}] +[GblId, Arity=1, Str=<1!P(1L)>, Cpr=1, 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)}] f3 = fac diff --git a/testsuite/tests/arityanal/should_run/Makefile b/testsuite/tests/arityanal/should_run/Makefile new file mode 100644 index 0000000000..9101fbd40a --- /dev/null +++ b/testsuite/tests/arityanal/should_run/Makefile @@ -0,0 +1,3 @@ +TOP=../../.. +include $(TOP)/mk/boilerplate.mk +include $(TOP)/mk/test.mk diff --git a/testsuite/tests/arityanal/should_run/T21652.hs b/testsuite/tests/arityanal/should_run/T21652.hs new file mode 100644 index 0000000000..16fda0aeaa --- /dev/null +++ b/testsuite/tests/arityanal/should_run/T21652.hs @@ -0,0 +1,10 @@ +import GHC.Exts + +f, g :: a -> a +f = g +g x = f x +{-# NOINLINE f #-} +{-# NOINLINE g #-} + +-- should print done, not <<loop>> +main = lazy g `seq` putStrLn "done" diff --git a/testsuite/tests/arityanal/should_run/T21652.stdout b/testsuite/tests/arityanal/should_run/T21652.stdout new file mode 100644 index 0000000000..19f86f493a --- /dev/null +++ b/testsuite/tests/arityanal/should_run/T21652.stdout @@ -0,0 +1 @@ +done diff --git a/testsuite/tests/arityanal/should_run/all.T b/testsuite/tests/arityanal/should_run/all.T new file mode 100644 index 0000000000..a6b06fbb15 --- /dev/null +++ b/testsuite/tests/arityanal/should_run/all.T @@ -0,0 +1,2 @@ +# Regression tests +test('T21652', [ only_ways(['optasm']) ], compile_and_run, ['']) diff --git a/testsuite/tests/deSugar/should_compile/T19969.stderr b/testsuite/tests/deSugar/should_compile/T19969.stderr index 5e23785472..3ded6f27a4 100644 --- a/testsuite/tests/deSugar/should_compile/T19969.stderr +++ b/testsuite/tests/deSugar/should_compile/T19969.stderr @@ -1,7 +1,7 @@ ==================== Tidy Core ==================== Result size of Tidy Core - = {terms: 12, types: 18, coercions: 0, joins: 0/0} + = {terms: 8, types: 14, coercions: 0, joins: 0/0} Rec { -- RHS size: {terms: 3, types: 2, coercions: 0, joins: 0/0} @@ -10,7 +10,7 @@ f [Occ=LoopBreaker] :: [Int] -> [Int] f = \ (x :: [Int]) -> f x end Rec } --- RHS size: {terms: 3, types: 2, coercions: 0, joins: 0/0} +-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0} g [InlPrag=INLINE (sat-args=1)] :: [Int] -> [Int] [GblId, Arity=1, @@ -19,10 +19,10 @@ g [InlPrag=INLINE (sat-args=1)] :: [Int] -> [Int] Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=ALWAYS_IF(arity=1,unsat_ok=False,boring_ok=True) - Tmpl= \ (x [Occ=Once1] :: [Int]) -> f x}] -g = \ (x :: [Int]) -> f x + Tmpl= f}] +g = f --- RHS size: {terms: 3, types: 2, coercions: 0, joins: 0/0} +-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0} h [InlPrag=INLINE (sat-args=1)] :: [Int] -> [Int] [GblId, Arity=1, @@ -31,8 +31,8 @@ h [InlPrag=INLINE (sat-args=1)] :: [Int] -> [Int] Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=ALWAYS_IF(arity=1,unsat_ok=False,boring_ok=True) - Tmpl= \ (x [Occ=Once1] :: [Int]) -> f x}] -h = \ (x :: [Int]) -> f x + Tmpl= f}] +h = f |