Note [Desugar Strict binds]

GHC/HsToCore/Binds.hs:629 compiler

See https://gitlab.haskell.org/ghc/ghc/wikis/strict-pragma

Desugaring strict variable bindings looks as follows (core below ==>)

  let !x = rhs
  in  body
==>
  let x = rhs
  in x `seq` body -- seq the variable

and if it is a pattern binding the desugaring looks like

  let !pat = rhs
  in body
==>
  let x = rhs -- bind the rhs to a new variable
      pat = x
  in x `seq` body -- seq the new variable

if there is no variable in the pattern desugaring looks like

  let False = rhs
  in body
==>
  let x = case rhs of {False -> (); _ -> error "Match failed"}
  in x `seq` body

In order to force the Ids in the binding group they are passed around
in the dsHsBind family of functions, and later seq'ed in GHC.HsToCore.Expr.ds_val_bind.

Consider a recursive group like this

  letrec
     f : g = rhs[f,g]
  in <body>

Without `Strict`, we get a translation like this:

  let t = /\a. letrec tm = rhs[fm,gm]
                      fm = case t of fm:_ -> fm
                      gm = case t of _:gm -> gm
                in
                (fm,gm)

  in let f = /\a. case t a of (fm,_) -> fm
  in let g = /\a. case t a of (_,gm) -> gm
  in <body>

Here `tm` is the monomorphic binding for `rhs`.

With `Strict`, we want to force `tm`, but NOT `fm` or `gm`.
Alas, `tm` isn't in scope in the `in <body>` part.

The simplest thing is to return it in the polymorphic
tuple `t`, thus:

  let t = /\a. letrec tm = rhs[fm,gm]
                      fm = case t of fm:_ -> fm
                      gm = case t of _:gm -> gm
                in
                (tm, fm, gm)

  in let f = /\a. case t a of (_,fm,_) -> fm
  in let g = /\a. case t a of (_,_,gm) -> gm
  in let tm = /\a. case t a of (tm,_,_) -> tm
  in tm `seq` <body>


See https://gitlab.haskell.org/ghc/ghc/wikis/strict-pragma for a more
detailed explanation of the desugaring of strict bindings.

Wrinkle 1: forcing linear variables

Consider

  let %1 !x = rhs in <body>
==>
  let x = rhs in x `seq` <body>

In the desugared version x is used in both arguments of seq. This isn't
recognised a linear. So we can't strictly speaking use seq. Instead, the code is
really desugared as

  let x = rhs in case x of x { _ -> <body> }

The shadowing with the case-binder is crucial. The linear linter (see
Note [Linting linearity] in GHC.Core.Lint) understands this as linear. This is
what the seqVar function does.

To be more precise, suppose x has multiplicity p, the fully annotated seqVar (in
Core, p is really stored inside x) is

  case x of %p x { _ -> <body> }

In linear Core, case u of %p y { _ -> v } consumes u with multiplicity p, and
makes y available with multiplicity p in v. Which is exactly what we want.

Wrinkle 2: linear patterns

Consider the following linear binding (linear lets are always non-recursive):

  let
     %1 f : g = rhs
  in <body>

The general case would desugar it to

  let t = let tm = rhs
              fm = case tm of fm:_ -> fm
              gm = case tm of _:gm -> gm
           in
           (tm, fm, gm)

  in let f = case t a of (_,fm,_) -> fm
  in let g = case t a of (_,_,gm) -> gm
  in let tm = case t a of (tm,_,_) -> tm
  in tm `seq` <body>

But all the case expression drop variables, which is prohibited by
linearity. But because this is a non-recursive let (in particular we're
desugaring a single binding), we can (and do) desugar the binding as a simple
case-expression instead:

  case rhs of {
    (f:g) -> <body>
  }

This is handled by the special case: a non-recursive PatBind in
GHC.HsToCore.Expr.ds_val_bind.

References 1

Referenced by 7