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: ```ignore let A = ...; let B =
(
expr: &mut MirRelationExpr,
inline_mfp: bool,
)
| 745 | /// be able to apply inlining due to ordering (we cannot inline a binding |
| 746 | /// into one that is not strictly later). |
| 747 | pub(super) fn inline_lets_core( |
| 748 | expr: &mut MirRelationExpr, |
| 749 | inline_mfp: bool, |
| 750 | ) -> Result<(), crate::TransformError> { |
| 751 | if let MirRelationExpr::LetRec { |
| 752 | ids, |
| 753 | values, |
| 754 | limits, |
| 755 | body, |
| 756 | } = expr |
| 757 | { |
| 758 | // Count the number of uses of each local id across all expressions. |
| 759 | let mut counts = BTreeMap::new(); |
| 760 | for value in values.iter() { |
| 761 | super::support::count_local_id_uses(value, &mut counts); |
| 762 | } |
| 763 | super::support::count_local_id_uses(body, &mut counts); |
| 764 | |
| 765 | // Each binding can reach one of three positions on its inlineability: |
| 766 | // 1. The binding is used once and is available to be directly taken. |
| 767 | // 2. The binding is simple enough that it can just be cloned. |
| 768 | // 3. The binding is not available for inlining. |
| 769 | let mut inline_offers = BTreeMap::new(); |
| 770 | |
| 771 | // Each binding may require the expiration of prior inlining offers. |
| 772 | // This occurs when an inlined body references the prior iterate of a binding, |
| 773 | // and inlining it would change the meaning to be the current iterate. |
| 774 | // Roughly, all inlining offers expire just after the binding of the least |
| 775 | // identifier they contain that is greater than the bound identifier itself. |
| 776 | let mut expire_offers = BTreeMap::new(); |
| 777 | let mut expired_offers = Vec::new(); |
| 778 | |
| 779 | // For each binding, inline `Get`s and then determine if *it* should be inlined. |
| 780 | // It is important that we do the substitution in-order and before reasoning |
| 781 | // about the inlineability of each binding, to ensure that our conclusion about |
| 782 | // the inlineability of a binding stays put. Specifically, |
| 783 | // 1. by going in order no substitution will increase the `Get`-count of an |
| 784 | // identifier beyond one, as all in values with strictly greater identifiers. |
| 785 | // 2. by performing the substitution before reasoning, the structure of the value |
| 786 | // as it would be substituted is fixed. |
| 787 | for ((id, mut expr), max_iter) in ids |
| 788 | .drain(..) |
| 789 | .zip_eq(values.drain(..)) |
| 790 | .zip_eq(limits.drain(..)) |
| 791 | { |
| 792 | // Substitute any appropriate prior let bindings. |
| 793 | inline_lets_helper(&mut expr, &mut inline_offers)?; |
| 794 | |
| 795 | // Determine the first `id'` at which any inlining offer must expire. |
| 796 | // An inlining offer expires because it references an `id'` that is not yet bound, |
| 797 | // indicating a reference to the *prior* iterate of that identifier. Inlining the |
| 798 | // expression once `id'` becomes bound would advance the reference to be the |
| 799 | // *current* iterate of the identifier. |
| 800 | MirRelationExpr::collect_expirations(id, &expr, &mut expire_offers); |
| 801 | |
| 802 | // Gets for `id` only occur in later expressions, so this should still be correct. |
| 803 | let num_gets = counts.get(&id).map(|x| *x).unwrap_or(0); |
| 804 | // Counts of zero or one lead to substitution; otherwise certain simple structures |
no test coverage detected