Note [Core let-can-float invariant]

GHC/Core.hs:430 compiler 1 ticket

The let-can-float invariant:

    The right hand side of a /non-top-level/, /non-recursive/ binding
    may be of unlifted type, but only if
    the expression is ok-for-speculation
    or the 'Let' is for a join point.

    (For top-level or recursive lets see Note [Core letrec invariant].)

This means that the let can be floated around
without difficulty. For example, this is OK:

   y::Int# = x +# 1#

But this is not, as it may affect termination if the
expression is floated out:

   y::Int# = fac 4#

In this situation you should use @case@ rather than a @let@. The function
'GHC.Core.Utils.needsCaseBinding' can help you determine which to generate, or
alternatively use 'GHC.Core.Make.mkCoreLet' rather than this constructor directly,
which will generate a @case@ if necessary

The let-can-float invariant is initially enforced by mkCoreLet in GHC.Core.Make.

Historical Note [The let/app invariant]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Before 2022 GHC used the "let/app invariant", which applied the let-can-float rules
to the argument of an application, as well as to the RHS of a let.  This made some
kind of sense, because 'let' can always be encoded as application:
   let x=rhs in b   =    (\x.b) rhs

But the let/app invariant got in the way of RULES; see #19313.  For example
  up :: Int# -> Int#
  {-# RULES "up/down" forall x. up (down x) = x #

References 1

Referenced by 9