Fix: Correctly determine lambda purity

The purity of a lambda definition should always be `Pure`. The purity of
the lambda's body is only relevant when the lambda is called. This
change ensures that defining a lambda does not incorrectly mark the
parent scope as impure.
This commit is contained in:
Michael Schimmel
2026-02-22 09:20:44 +01:00
parent 6605f56756
commit e3bfc01027
+18 -1
View File
@@ -691,7 +691,8 @@ impl Optimizer {
fn purity_of(&self, node: &TypedNode) -> Purity {
match &node.kind {
BoundKind::Constant(_) | BoundKind::Parameter { .. } | BoundKind::Nop => Purity::Pure,
BoundKind::Lambda { body, .. } => self.purity_of(body),
// Defining a lambda is pure; only calling it may have side effects.
BoundKind::Lambda { .. } => Purity::Pure,
BoundKind::Get { addr, .. } => match addr {
Address::Global(idx) => {
if let Some(purity_rc) = &self.global_purity {
@@ -734,6 +735,7 @@ impl Optimizer {
BoundKind::Set { .. } => Purity::Impure,
BoundKind::Call { callee, args } => {
let callee_purity = match &callee.kind {
BoundKind::Lambda { body, .. } => self.purity_of(body),
BoundKind::Get {
addr: Address::Global(idx),
..
@@ -1365,4 +1367,19 @@ mod tests {
);
assert!(dump.contains("Constant: 42"));
}
#[test]
fn test_opt_unused_lambda_with_side_effects_dce() {
// Die Lambda-Definition selbst ist Pure, auch wenn ihr Body Impure ist (assign).
// Daher muss sie gelöscht werden, wenn sie nicht gerufen wird.
// 'g' muss definiert sein, damit der Binder nicht abbricht.
let source = "(fn [] (do (def g 0) (def f (fn [x] (assign g 1))) 42))";
let dump = get_optimized_dump(source, true);
assert!(
!dump.contains("DefLocal (Name: 'f'"),
"Unused lambda with side effects should be removed by DCE. Dump: \n{}",
dump
);
assert!(dump.contains("Constant: 42"));
}
}