Normalize `Let` and `LetRec` bindings in `relation`. Mechanically, `action` first renumbers all bindings, erroring if any shadowing is encountered. It then promotes all `Let` and `LetRec` expressions to the roots of their expressions, fusing `Let` bindings into containing `LetRec` bindings, but leaving stacked `LetRec` bindings unfused to each other (for reasons of correctness). It then considers
(
&self,
relation: &mut MirRelationExpr,
features: &OptimizerFeatures,
)
| 96 | /// |
| 97 | /// We then perform a final renumbering. |
| 98 | pub fn action( |
| 99 | &self, |
| 100 | relation: &mut MirRelationExpr, |
| 101 | features: &OptimizerFeatures, |
| 102 | ) -> Result<(), crate::TransformError> { |
| 103 | // Record whether the relation was initially recursive, to confirm that we do not introduce |
| 104 | // recursion to a non-recursive expression. |
| 105 | let was_recursive = relation.is_recursive(); |
| 106 | |
| 107 | // Renumber all bindings to ensure that identifier order matches binding order. |
| 108 | // In particular, as we use `BTreeMap` for binding order, we want to ensure that |
| 109 | // 1. Bindings within a `LetRec` are assigned increasing identifiers, and |
| 110 | // 2. Bindings across `LetRec`s are assigned identifiers in "visibility order", corresponding to an |
| 111 | // in-order traversal. |
| 112 | // TODO: More can and perhaps should be said about "visibility order" and how let promotion is correct. |
| 113 | renumbering::renumber_bindings(relation, &mut IdGen::default())?; |
| 114 | |
| 115 | // Promote all `Let` and `LetRec` AST nodes to the roots. |
| 116 | // After this, all non-`LetRec` nodes contain no further `Let` or `LetRec` nodes, |
| 117 | // placing all `LetRec` nodes around the root, if not always in a single AST node. |
| 118 | let_motion::promote_let_rec(relation); |
| 119 | let_motion::assert_no_lets(relation); |
| 120 | let_motion::assert_letrec_major(relation); |
| 121 | |
| 122 | // Inlining may violate letrec-major form. |
| 123 | inlining::inline_lets(relation, self.inline_mfp)?; |
| 124 | |
| 125 | // Return to letrec-major form to refresh types. |
| 126 | let_motion::promote_let_rec(relation); |
| 127 | support::refresh_types(relation, features)?; |
| 128 | |
| 129 | // Renumber bindings for good measure. |
| 130 | // Ideally we could skip when `action` is a no-op, but hard to thread that through at the moment. |
| 131 | renumbering::renumber_bindings(relation, &mut IdGen::default())?; |
| 132 | |
| 133 | // A final bottom-up traversal to normalize the shape of nested LetRec blocks |
| 134 | relation.try_visit_mut_post(&mut |relation| -> Result<(), RecursionLimitError> { |
| 135 | // Move a non-recursive suffix of bindings from the end of the LetRec |
| 136 | // to the LetRec body. |
| 137 | // This is unsafe when applied to expressions which contain `ArrangeBy`, |
| 138 | // as if the extracted suffixes reference arrangements they will not be |
| 139 | // able to access those arrangements from outside the `LetRec` scope. |
| 140 | // It happens to work at the moment, so we don't touch it but should fix. |
| 141 | let bindings = let_motion::harvest_nonrec_suffix(relation)?; |
| 142 | if let MirRelationExpr::LetRec { |
| 143 | ids: _, |
| 144 | values: _, |
| 145 | limits: _, |
| 146 | body, |
| 147 | } = relation |
| 148 | { |
| 149 | for (id, value) in bindings.into_iter().rev() { |
| 150 | **body = MirRelationExpr::Let { |
| 151 | id, |
| 152 | value: Box::new(value), |
| 153 | body: Box::new(body.take_dangerous()), |
| 154 | }; |
| 155 | } |
no test coverage detected