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:
@@ -691,7 +691,8 @@ impl Optimizer {
|
|||||||
fn purity_of(&self, node: &TypedNode) -> Purity {
|
fn purity_of(&self, node: &TypedNode) -> Purity {
|
||||||
match &node.kind {
|
match &node.kind {
|
||||||
BoundKind::Constant(_) | BoundKind::Parameter { .. } | BoundKind::Nop => Purity::Pure,
|
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 {
|
BoundKind::Get { addr, .. } => match addr {
|
||||||
Address::Global(idx) => {
|
Address::Global(idx) => {
|
||||||
if let Some(purity_rc) = &self.global_purity {
|
if let Some(purity_rc) = &self.global_purity {
|
||||||
@@ -734,6 +735,7 @@ impl Optimizer {
|
|||||||
BoundKind::Set { .. } => Purity::Impure,
|
BoundKind::Set { .. } => Purity::Impure,
|
||||||
BoundKind::Call { callee, args } => {
|
BoundKind::Call { callee, args } => {
|
||||||
let callee_purity = match &callee.kind {
|
let callee_purity = match &callee.kind {
|
||||||
|
BoundKind::Lambda { body, .. } => self.purity_of(body),
|
||||||
BoundKind::Get {
|
BoundKind::Get {
|
||||||
addr: Address::Global(idx),
|
addr: Address::Global(idx),
|
||||||
..
|
..
|
||||||
@@ -1365,4 +1367,19 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert!(dump.contains("Constant: 42"));
|
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"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user