pub(super) fn inline_lets_core(
    expr: &mut MirRelationExpr,
    inline_mfp: bool
) -> Result<(), TransformError>
Expand description

Considers inlining actions to perform for a sequence of bindings and a following body.

A let binding may be inlined only in subsequent bindings or in the body; other bindings should not “immediately” observe the binding, as that would be a change to the semantics of LetRec. For example, it would not be correct to replace C with A in the definition of B here:

let A = ...;
let B = A - C;
let C = A;

The explanation is that B should always be the difference between the current and previous A, and that the substitution of C would instead make it always zero, changing its definition.

Here a let binding is proposed for inlining if any of the following is true:

  1. It has a single reference across all bindings and the body.
  2. It is a “sufficiently simple” Get, determined in part by the inline_mfp argument.

We don’t need extra checks for limits, because

  • limits is only relevant when a binding is directly used through a back edge (because that is where the rendering puts the limits check);
  • when a binding is directly used through a back edge, it can’t be inlined anyway.
  • Also note that if a LetRec completely disappears at the end of inline_lets_core, then there was no recursion in it.

The case of Constant binding is handled here (as opposed to FoldConstants) in a somewhat limited manner (see #18180). Although a bit weird, constants should also not be inlined into prior bindings as this does change the behavior from one where the collection is initially empty to one where it is always the constant.

Having inlined bindings, many of them may now be dead (with no transitive references from body). These can now be removed. They may not be exactly those bindings that were inlineable, as we may not always be able to apply inlining due to ordering (we cannot inline a binding into one that is not strictly later).