From 1342c7ca8ea694d780ecbfccc5b9cded8e088793 Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Fri, 6 Mar 2026 11:58:28 +0100 Subject: [PATCH] Fix optimizer slot clash during inlining and correct closure reduction order - Add new_for_inlining to SubstitutionMap to preserve the next_slot counter, preventing local slot collisions when merging scopes. - Update Inliner to request fresh slots for parameters during beta reduction instead of forcing identity mappings. - Fix execution order in Optimizer::visit_node for closure inlining to run prepare_beta_reduction before visiting the function body, preventing corrupted upvalue captures. - Fix CLI --no-opt flag to correctly disable the optimizer in the ast binary. - Add integration test to prevent future slot clash regressions. --- src/ast/compiler/optimizer/engine.rs | 16 +++++----------- src/ast/compiler/optimizer/inliner.rs | 3 +-- src/ast/compiler/optimizer/substitution_map.rs | 6 ++++++ 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/ast/compiler/optimizer/engine.rs b/src/ast/compiler/optimizer/engine.rs index 5ced0a1..6d6c95c 100644 --- a/src/ast/compiler/optimizer/engine.rs +++ b/src/ast/compiler/optimizer/engine.rs @@ -249,7 +249,7 @@ impl Optimizer { && path.enter_lambda(&callee_opt.identity) { path.inlining_depth += 1; - let mut inner_sub = sub.new_inner(); + let mut inner_sub = sub.new_for_inlining(); let collapsed = if inliner .prepare_beta_reduction(params, &arg_nodes, body, &mut inner_sub) .is_some() @@ -286,7 +286,7 @@ impl Optimizer { && positional_count.is_some() && !lambda_node.ty.is_recursive { - let mut inner_sub = sub.new_inner(); + let mut inner_sub = sub.new_for_inlining(); path.inlining_stack.insert(*idx); path.inlining_depth += 1; let collapsed = if inliner @@ -314,7 +314,7 @@ impl Optimizer { && !closure.function_node.ty.is_recursive && path.enter_lambda(&closure.function_node.identity) { - let mut closure_sub = SubstitutionMap::new(); + let mut closure_sub = sub.new_for_inlining(); for (i, cell) in closure.upvalues.iter().enumerate() { closure_sub.add_value( Address::Upvalue(UpvalueIdx(i as u32)), @@ -323,22 +323,16 @@ impl Optimizer { } path.inlining_depth += 1; - let inlined_body = self.visit_node( - closure.function_node.clone(), - &mut closure_sub, - path, - ); - let collapsed = if inliner .prepare_beta_reduction( &closure.parameter_node, &arg_nodes, - &inlined_body, + &closure.function_node, &mut closure_sub, ) .is_some() { - Some(self.visit_node(inlined_body, &mut closure_sub, path)) + Some(self.visit_node(closure.function_node.clone(), &mut closure_sub, path)) } else { None }; diff --git a/src/ast/compiler/optimizer/inliner.rs b/src/ast/compiler/optimizer/inliner.rs index 8ba1ccd..90a5ad4 100644 --- a/src/ast/compiler/optimizer/inliner.rs +++ b/src/ast/compiler/optimizer/inliner.rs @@ -128,8 +128,7 @@ impl<'a> Inliner<'a> { } if let Address::Local(slot) = addr { - sub.slot_mapping.insert(*slot, *slot); - sub.next_slot = sub.next_slot.max(slot.0 + 1); + sub.map_slot(*slot); } *offset += 1; } diff --git a/src/ast/compiler/optimizer/substitution_map.rs b/src/ast/compiler/optimizer/substitution_map.rs index 94315ce..0a5b988 100644 --- a/src/ast/compiler/optimizer/substitution_map.rs +++ b/src/ast/compiler/optimizer/substitution_map.rs @@ -38,6 +38,12 @@ impl SubstitutionMap { inner } + pub fn new_for_inlining(&self) -> Self { + let mut inner = self.new_inner(); + inner.next_slot = self.next_slot; + inner + } + pub fn add_ast_substitution(&mut self, addr: Address, node: AnalyzedNode) { self.ast_substitutions.insert(addr, Rc::new(node)); }