Note [The binder-swap substitution]

GHC/Core/Opt/OccurAnal.hs:3252 compiler 2 tickets

The binder-swap is implemented by the occ_bs_env field of OccEnv.
There are two main pieces:

* Given    case x |> co of b { alts }
  we add [x :-> (b, sym co)] to the occ_bs_env environment; this is
  done by addBndrSwap.

* Then, at an occurrence of a variable, we look up in the occ_bs_env
  to perform the swap. This is done by lookupBndrSwap.

Some tricky corners:

(BS1) We do the substitution before gathering occurrence info. So in
      the above example, an occurrence of x turns into an occurrence
      of b, and that's what we gather in the UsageDetails.  It's as
      if the binder-swap occurred before occurrence analysis. See
      the computation of fun_uds in occAnalApp.

(BS2) When doing a lookup in occ_bs_env, we may need to iterate,
      as you can see implemented in lookupBndrSwap.  Why?
      Consider   case x of a { 1# -> e1; DEFAULT ->
                 case x of b { 2# -> e2; DEFAULT ->
                 case x of c { 3# -> e3; DEFAULT -> ..x..a..b.. }}}
      At the first case addBndrSwap will extend occ_bs_env with
          [x :-> a]
      At the second case we occ-anal the scrutinee 'x', which looks up
        'x in occ_bs_env, returning 'a', as it should.
      Then addBndrSwap will add [a :-> b] to occ_bs_env, yielding
         occ_bs_env = [x :-> a, a :-> b]
      At the third case we'll again look up 'x' which returns 'a'.
      But we don't want to stop the lookup there, else we'll end up with
                 case x of a { 1# -> e1; DEFAULT ->
                 case a of b { 2# -> e2; DEFAULT ->
                 case a of c { 3# -> e3; DEFAULT -> ..a..b..c.. }}}
      Instead, we want iterate the lookup in addBndrSwap, to give
                 case x of a { 1# -> e1; DEFAULT ->
                 case a of b { 2# -> e2; DEFAULT ->
                 case b of c { 3# -> e3; DEFAULT -> ..c..c..c.. }}}
      This makes a particular difference for case-merge, which works
      only if the scrutinee is the case-binder of the immediately enclosing
      case (Note [Merge Nested Cases] in GHC.Core.Opt.Simplify.Utils
      See #19581 for the bug report that showed this up.

(BS3) We need care when shadowing.  Suppose [x :-> b] is in occ_bs_env,
      and we encounter:
         (i) \x. blah
             Here we want to delete the x-binding from occ_bs_env

         (ii) \b. blah
              This is harder: we really want to delete all bindings that
              have 'b' free in the range.  That is a bit tiresome to implement,
              so we compromise.  We keep occ_bs_rng, which is the set of
              free vars of rng(occc_bs_env).  If a binder shadows any of these
              variables, we discard all of occ_bs_env.  Safe, if a bit
              brutal.  NB, however: the simplifer de-shadows the code, so the
              next time around this won't happen.

      These checks are implemented in addInScope.
      (i) is needed only for Ids, but (ii) is needed for tyvars too (#22623)
      because if occ_bs_env has [x :-> ...a...] where `a` is a tyvar, we
      must not replace `x` by `...a...` under /\a. ...x..., or similarly
      under a case pattern match that binds `a`.

      An alternative would be for the occurrence analyser to do cloning as
      it goes.  In principle it could do so, but it'd make it a bit more
      complicated and there is no great benefit. The simplifer uses
      cloning to get a no-shadowing situation, the care-when-shadowing
      behaviour above isn't needed for long.

(BS4) The domain of occ_bs_env can include GlobaIds.  Eg
         case M.foo of b { alts }
      We extend occ_bs_env with [M.foo :-> b].  That's fine.

(BS5) We have to apply the occ_bs_env substitution uniformly,
      including to (local) rules and unfoldings.

(BS6) For interest (only),
      see Historical Note [Care with binder-swap on dictionaries]

References 0

This Note does not link to any other.

Referenced by 7