Add destructuring support to dead-def elimination
Introduce `collect_pattern_addrs` to gather all bound addresses from a destructuring pattern. This enables the optimizer to remove unused destructuring definitions, similar to how simple unused definitions are handled. Added integration tests to verify this behavior and other optimizer robustness scenarios.
This commit is contained in:
@@ -10,7 +10,7 @@ use std::rc::Rc;
|
||||
use super::folder::Folder;
|
||||
use super::inliner::Inliner;
|
||||
use super::substitution_map::SubstitutionMap;
|
||||
use super::utils::{PathTracker, UsageInfo};
|
||||
use super::utils::{collect_pattern_addrs, PathTracker, UsageInfo};
|
||||
|
||||
pub struct Optimizer {
|
||||
pub enabled: bool,
|
||||
@@ -500,7 +500,7 @@ impl Optimizer {
|
||||
let is_last = i == last_idx;
|
||||
if self.enabled && !is_last {
|
||||
let removable = match &e.kind {
|
||||
NodeKind::Def { pattern, value, .. } => {
|
||||
NodeKind::Def { pattern, value, info: def_info } => {
|
||||
let addr = Self::extract_def_addr(pattern);
|
||||
if let Some(addr) = addr {
|
||||
!info.is_used(&addr)
|
||||
@@ -511,8 +511,23 @@ impl Optimizer {
|
||||
})
|
||||
&& (value.ty.purity >= Purity::SideEffectFree
|
||||
|| matches!(value.kind, NodeKind::Lambda { .. }))
|
||||
} else if def_info.captured_by.is_empty() {
|
||||
// Destructuring def: safe to remove only when no closure
|
||||
// captures any binding and every bound slot is unused.
|
||||
let mut addrs = Vec::new();
|
||||
collect_pattern_addrs(pattern, &mut addrs);
|
||||
!addrs.is_empty()
|
||||
&& addrs.iter().all(|a| {
|
||||
!info.is_used(a)
|
||||
&& if let Address::Local(slot) = a {
|
||||
!sub.captured_slots.contains(slot)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
&& (value.ty.purity >= Purity::SideEffectFree
|
||||
|| matches!(value.kind, NodeKind::Lambda { .. }))
|
||||
} else {
|
||||
// Destructuring def without single addr - not removable
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,7 +199,7 @@ impl UsageInfo {
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_assigned_from_target(&mut self, node: &AnalyzedNode) {
|
||||
pub fn collect_assigned_from_target(&mut self, node: &AnalyzedNode) {
|
||||
match &node.kind {
|
||||
NodeKind::Identifier { binding, .. } => {
|
||||
let addr = match binding {
|
||||
@@ -217,3 +217,28 @@ impl UsageInfo {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Collects all addresses declared in a binding pattern (Identifier Declaration or nested Tuple).
|
||||
///
|
||||
/// Used by the optimizer to enumerate all slots bound by a destructuring `def` pattern,
|
||||
/// enabling dead-def elimination for patterns that `extract_def_addr` cannot handle
|
||||
/// (i.e. anything other than a plain `Identifier`).
|
||||
pub fn collect_pattern_addrs(pattern: &AnalyzedNode, out: &mut Vec<Address<VirtualId>>) {
|
||||
match &pattern.kind {
|
||||
NodeKind::Identifier {
|
||||
binding: IdentifierBinding::Declaration { addr, .. },
|
||||
..
|
||||
} => {
|
||||
out.push(*addr);
|
||||
}
|
||||
NodeKind::Def { pattern: inner, .. } => {
|
||||
collect_pattern_addrs(inner, out);
|
||||
}
|
||||
NodeKind::Tuple { elements } => {
|
||||
for el in elements {
|
||||
collect_pattern_addrs(el, out);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user