Refactor optimizer to track upvalue usage

The `UsageInfo` struct has been updated to include tracking for
`used_upvalues` and `assigned_upvalues`. The `collect_usage` function
has been modified to handle these new fields when encountering
`Address::Upvalue`.

The `map_params_to_args` function now takes `body_usage` as an argument
to ensure that parameters assigned to or used in the body are correctly
substituted. A new helper function, `collect_parameter_slots_set`, has
been added to gather all parameter slots within a pattern.

The inlining logic in `Optimizer::inline_call` has been enhanced to
prevent inlining if a parameter is used or assigned in the function body
but cannot be substituted. This addresses a bug where aggressive
inlining could lead to incorrect code generation when parameters were
reassigned.

A new integration test, `test_closure_reassignment_optimization_bug`,
has been added to specifically target and verify the fix for this
inlining issue.
This commit is contained in:
Michael Schimmel
2026-02-24 12:53:06 +01:00
parent 2c652e0140
commit e07acc0208
4 changed files with 121 additions and 39 deletions
+11
View File
@@ -390,4 +390,15 @@ mod tests {
"[\"Symbol:\" \"btc\" \"field:\" :close \"id:\" \"cls\"]"
);
}
#[test]
fn test_closure_reassignment_optimization_bug() {
let env = Environment::new();
// This test case reproduces a bug where the optimizer aggressively inlined a function
// ('f') even though its parameters ('x') were being assigned to in the body (by inner lambda).
// The fix ensures that such functions are NOT inlined.
let source = "(do (def f (fn [[x y]] (fn [] (assign x (+ x y))))) ((f [1 2])))";
let res = env.run_script(source);
assert_eq!(format!("{}", res.unwrap()), "3");
}
}