Note [Core let-can-float invariant]
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
- Core letrec invariant GHC.Core
Referenced by 9
- GHC.Core.Opt.Simplify.Utils call site ×2
- GHC.Core call site
- Core letrec invariant GHC.Core
- GHC.Core.Lint call site
- GHC.Core.Make call site
- Guarding against silly shifts GHC.Core.Opt.ConstantFold
- Care with unlifted bindings GHC.Core.Opt.Specialise
- GHC.Core.Utils call site