Rule let-matching logic fails to extend in-scope set correctly
Note [Matching lets] in GHC.Core.Rules claims the following:
We use
GHC.Core.Subst.substBindto freshen the binding, using an in-scope set that is the original in-scope variables plus thers_bndrs(currently floated let-bindings). So in (a) above we'll freshen thevbinding; in (b) above we'll freshen the secondvbinding.
However, this isn't what match actually does. Specifically, it fails to extend the in-scope set of the substitution given to substBind with rs_bndrs:
match renv subst e1 (Let bind e2) mco
| -- pprTrace "match:Let" (vcat [ppr bind, ppr $ okToFloat (rv_lcl renv) (bindFreeVars bind)]) $
not (isJoinBind bind) -- can't float join point out of argument position
, okToFloat (rv_lcl renv) (bindFreeVars bind) -- See Note [Matching lets]
= match (renv { rv_fltR = flt_subst'
, rv_lcl = rv_lcl renv `extendRnInScopeSetList` new_bndrs })
-- We are floating the let-binding out, as if it had enclosed
-- the entire target from Day 1. So we must add its binders to
-- the in-scope set (#20200)
(subst { rs_binds = rs_binds subst . Let bind'
, rs_bndrs = new_bndrs ++ rs_bndrs subst })
e1 e2 mco
| otherwise
= Nothing
where
(flt_subst', bind') = substBind (rv_fltR renv) bind
new_bndrs = bindersOf bind'
Now, one might be tempted to think that this is okay; afterall, match does extend rv_fltR of the RuleMatchEnv. However, match_exprs does not propagate RuleMatchEnv across matched arguments; only the RuleSubst will be carried over when we match the next argument.
I don't yet know why we haven't observed fallout from this master, but this seems like a bug which resulted in Core Lint failures while building Data.Binary.Class on the 9.2 branch (see !7602 (merged)).