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.substBind
to 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 thev
binding; in (b) above we'll freshen the secondv
binding.
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)).