Admin message

Due to a large amount of spam we do not allow new users to create repositories, they are "external" users. If you are a new user and want to create a repository, for example for forking GHC, open a new issue on ghc/ghc using the "get-verified" issue template

Rules can fire in the RHS of other rules in the wrong phase
When simplifying the RHS of a RULE (in `GHC.Core.Opt.Simplify.Iteration.simplRules`), GHC allows other rules to fire, setting the simplifier phase from the activation phase of the rule we are simplifying. The pattern looks like: ```hs simpl_rule rule@(Rule { ru_rhs = rhs }) = do { let rhs_env = updMode (updModeForStableUnfoldings act) env ; rhs' <- simplExprC rhs_env rhs rhs_cont ; ... } ``` where crucially `updModeForStableUnfoldings` sets the simplifier phase from the activation phase of the rule: ```hs phaseFromActivation (ActiveAfter _ n) = Phase n phaseFromActivation _ = InitialPhase ``` This is incorrect when one takes into account rules that stop firing past a certain phase (e.g. a `[~1]` activation). Here's an example that demonstrates the problem: ```hs module T26323 where f :: Int -> Int f x = g x {-# INLINE [1] f #-} g :: Int -> Int g x = 0 {-# NOINLINE g #-} h :: Int -> Int h _ = 1 {-# NOINLINE h #-} {-# RULES "r1" [2] forall x. g x = h x #-} {-# RULES "r2" [~1] forall x. h x = 2 #-} test :: Int test = f 3 ``` Now, how do we optimise `f 3`? The first phase anything can happen is `1`, because the only thing that mentions `f` is its inline pragma which activates in phase 1. So we expect: - Phase 1: inline `f`. `f 3 ===> g 3`. - Phase 1: use rule `r1` (active since phase 2 and forever after). `g 3 ===> h 3`. - Done. Cannot use rule `r2` because it stopped being active in phase 1. No other rule applies. So we expect the final Core to be `h 3`. However, what happens instead is that we optimise the RHS of rule `r1`, setting the simplifier phase to phase 2. Starting with `h x`, we have: - Phase 2: use rule `r2`, which is still active. `h x ==> 2`. So it's as if we had written: ``` {-# RULES "r1" [2] forall x. g x = 2 #-} ``` However, this is invalid because this rule can apply at phase 1, while the rewrite `forall x. h x = 2` is not valid in phase 1. This situation actually arises in practice with GHC's rules for strings: ```hs {-# RULES "unpack" [~1] forall a . unpackCString# a = build (unpackFoldrCString# a) "unpack-append" forall a n . unpackFoldrCString# a (:) n = unpackAppendCString# a n "unpack-append-nil" forall a . unpackAppendCString# a [] = unpackCString# a #-} build :: forall a. (forall b. (a -> b -> b) -> b -> b) -> [a] {-# INLINE [1] mybuild #-} build g = g (:) [] ``` Here, GHC will simplify the RHS of `unpack-append-nil` using the `unpack` rule, even though `unpack-append-nil` is valid in all phases while `unpack` stops being valid from phase 1 onwards. So one can get the following loop of rewrites in simplifier phase 1: ``` unpackAppendCString# x [] ==> Rule: unpack-append-nil, in which one has already applied Rule: unpack to the RHS build (unpackFoldrCString# x) ==> inline build unpackFoldrCString# x (:) [] ==> Rule: unpack-append unpackAppendCString# x [] ```
issue