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.
This commit is contained in:
Michael Schimmel
2026-03-06 11:58:28 +01:00
parent 3ded62d836
commit 1342c7ca8e
3 changed files with 12 additions and 13 deletions
+5 -11
View File
@@ -249,7 +249,7 @@ impl Optimizer {
&& path.enter_lambda(&callee_opt.identity) && path.enter_lambda(&callee_opt.identity)
{ {
path.inlining_depth += 1; path.inlining_depth += 1;
let mut inner_sub = sub.new_inner(); let mut inner_sub = sub.new_for_inlining();
let collapsed = if inliner let collapsed = if inliner
.prepare_beta_reduction(params, &arg_nodes, body, &mut inner_sub) .prepare_beta_reduction(params, &arg_nodes, body, &mut inner_sub)
.is_some() .is_some()
@@ -286,7 +286,7 @@ impl Optimizer {
&& positional_count.is_some() && positional_count.is_some()
&& !lambda_node.ty.is_recursive && !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_stack.insert(*idx);
path.inlining_depth += 1; path.inlining_depth += 1;
let collapsed = if inliner let collapsed = if inliner
@@ -314,7 +314,7 @@ impl Optimizer {
&& !closure.function_node.ty.is_recursive && !closure.function_node.ty.is_recursive
&& path.enter_lambda(&closure.function_node.identity) && 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() { for (i, cell) in closure.upvalues.iter().enumerate() {
closure_sub.add_value( closure_sub.add_value(
Address::Upvalue(UpvalueIdx(i as u32)), Address::Upvalue(UpvalueIdx(i as u32)),
@@ -323,22 +323,16 @@ impl Optimizer {
} }
path.inlining_depth += 1; path.inlining_depth += 1;
let inlined_body = self.visit_node(
closure.function_node.clone(),
&mut closure_sub,
path,
);
let collapsed = if inliner let collapsed = if inliner
.prepare_beta_reduction( .prepare_beta_reduction(
&closure.parameter_node, &closure.parameter_node,
&arg_nodes, &arg_nodes,
&inlined_body, &closure.function_node,
&mut closure_sub, &mut closure_sub,
) )
.is_some() .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 { } else {
None None
}; };
+1 -2
View File
@@ -128,8 +128,7 @@ impl<'a> Inliner<'a> {
} }
if let Address::Local(slot) = addr { if let Address::Local(slot) = addr {
sub.slot_mapping.insert(*slot, *slot); sub.map_slot(*slot);
sub.next_slot = sub.next_slot.max(slot.0 + 1);
} }
*offset += 1; *offset += 1;
} }
@@ -38,6 +38,12 @@ impl SubstitutionMap {
inner 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) { pub fn add_ast_substitution(&mut self, addr: Address, node: AnalyzedNode) {
self.ast_substitutions.insert(addr, Rc::new(node)); self.ast_substitutions.insert(addr, Rc::new(node));
} }