Refactor Destructure to handle assignments

Renames `DefDestructure` to `Destructure` to better reflect its use in
both definitions and assignments.
Introduces `bind_assign_pattern` to handle assignment destructuring in
the binder.
Adds `test_assign_destructuring` to verify assignment destructuring
functionality.
This commit is contained in:
Michael Schimmel
2026-02-24 08:51:24 +01:00
parent 2b0e7f49d7
commit 51d83562de
9 changed files with 162 additions and 16 deletions
+2 -2
View File
@@ -178,11 +178,11 @@ impl<'a> Analyzer<'a> {
)
}
BoundKind::DefDestructure { pattern, value } => {
BoundKind::Destructure { pattern, value } => {
let pat_m = self.visit(Rc::new((**pattern).clone()));
let val_m = self.visit(Rc::new((**value).clone()));
(
BoundKind::DefDestructure {
BoundKind::Destructure {
pattern: Box::new(pat_m),
value: Box::new(val_m),
},
+37 -2
View File
@@ -209,7 +209,7 @@ impl Binder {
Ok(self.make_node(
node.identity.clone(),
BoundKind::DefDestructure {
BoundKind::Destructure {
pattern: Box::new(target_node),
value: Box::new(val_node),
},
@@ -230,7 +230,14 @@ impl Binder {
},
))
} else {
Err("Assignment target must be an identifier".to_string())
let target_node = self.bind_assign_pattern(target)?;
Ok(self.make_node(
node.identity.clone(),
BoundKind::Destructure {
pattern: Box::new(target_node),
value: Box::new(val_node),
},
))
}
}
@@ -456,6 +463,34 @@ impl Binder {
}
}
fn bind_assign_pattern(&mut self, node: &Node<UntypedKind>) -> Result<BoundNode, String> {
match &node.kind {
UntypedKind::Identifier(sym) => {
let addr = self.resolve_variable(sym)?;
Ok(self.make_node(
node.identity.clone(),
BoundKind::Set {
addr,
value: Box::new(self.make_node(node.identity.clone(), BoundKind::Nop)),
},
))
}
UntypedKind::Tuple { elements } => {
let mut bound_elems = Vec::new();
for e in elements {
bound_elems.push(self.bind_assign_pattern(e)?);
}
Ok(self.make_node(
node.identity.clone(),
BoundKind::Tuple {
elements: bound_elems,
},
))
}
_ => Err(format!("Invalid node in assignment pattern: {:?}", node.kind)),
}
}
fn make_node(&self, identity: Identity, kind: BoundKind<()>) -> BoundNode {
Node {
identity,
+5 -5
View File
@@ -82,8 +82,8 @@ pub enum BoundKind<T = ()> {
value: Box<BoundNode<T>>,
},
/// A destructuring definition (can be local or global depending on the pattern)
DefDestructure {
/// A destructuring operation (can be a definition or an assignment depending on the pattern)
Destructure {
pattern: Box<BoundNode<T>>,
value: Box<BoundNode<T>>,
},
@@ -194,11 +194,11 @@ where
},
) => na == nb && ga == gb && va == vb,
(
BoundKind::DefDestructure {
BoundKind::Destructure {
pattern: pa,
value: va,
},
BoundKind::DefDestructure {
BoundKind::Destructure {
pattern: pb,
value: vb,
},
@@ -265,7 +265,7 @@ impl<T> BoundKind<T> {
BoundKind::DefGlobal {
name, global_index, ..
} => format!("DEF_GLOBAL({}, Idx:{})", name.name, global_index),
BoundKind::DefDestructure { .. } => "DEF_DESTRUCTURE".to_string(),
BoundKind::Destructure { .. } => "DESTRUCTURE".to_string(),
BoundKind::Lambda {
params, upvalues, ..
} => {
+2 -2
View File
@@ -120,8 +120,8 @@ impl Dumper {
self.indent -= 1;
}
BoundKind::DefDestructure { pattern, value } => {
self.log("DefDestructure", node);
BoundKind::Destructure { pattern, value } => {
self.log("Destructure", node);
self.indent += 1;
self.write_indent();
self.output.push_str("Pattern:\n");
+57
View File
@@ -95,6 +95,34 @@ impl Optimizer {
current
}
fn collect_pattern_usage(&self, node: &AnalyzedNode, info: &mut UsageInfo) {
match &node.kind {
BoundKind::Parameter { slot, .. } => {
info.assigned_locals.insert(*slot);
}
BoundKind::DefGlobal { global_index, .. } => {
info.assigned_globals.insert(*global_index);
}
BoundKind::Set { addr, .. } => {
match addr {
Address::Local(slot) => {
info.assigned_locals.insert(*slot);
}
Address::Global(idx) => {
info.assigned_globals.insert(*idx);
}
_ => {}
}
}
BoundKind::Tuple { elements } => {
for el in elements {
self.collect_pattern_usage(el, info);
}
}
_ => {}
}
}
fn visit_node(
&self,
node: AnalyzedNode,
@@ -561,6 +589,31 @@ impl Optimizer {
)
}
BoundKind::Destructure {
ref pattern,
ref value,
} => {
let val_opt = Box::new(self.visit_node((**value).clone(), sub, path));
let pat_opt = Box::new(self.visit_node((**pattern).clone(), sub, path));
let mut info = UsageInfo::default();
self.collect_pattern_usage(&pat_opt, &mut info);
for slot in info.assigned_locals {
sub.locals.remove(&slot);
}
for idx in info.assigned_globals {
sub.globals.remove(&idx);
}
(
BoundKind::Destructure {
pattern: pat_opt,
value: val_opt,
},
node.ty.clone(),
)
}
BoundKind::Tuple { elements } => {
let elements = elements
.into_iter()
@@ -808,6 +861,10 @@ impl Optimizer {
fn collect_usage(&self, node: &AnalyzedNode, info: &mut UsageInfo) {
match &node.kind {
BoundKind::Destructure { pattern, value } => {
self.collect_pattern_usage(pattern, info);
self.collect_usage(value, info);
}
BoundKind::Constant(v) => {
if let Value::Object(obj) = v
&& let Some(closure) = obj.as_any().downcast_ref::<Closure>()
+1 -1
View File
@@ -118,7 +118,7 @@ impl TCO {
global_index: *global_index,
value: Box::new(Self::transform(Rc::new((**value).clone()), false)),
},
BoundKind::DefDestructure { pattern, value } => BoundKind::DefDestructure {
BoundKind::Destructure { pattern, value } => BoundKind::Destructure {
pattern: Box::new(Self::transform(Rc::new((**pattern).clone()), false)),
value: Box::new(Self::transform(Rc::new((**value).clone()), false)),
},
+19 -2
View File
@@ -163,6 +163,23 @@ impl TypeChecker {
specialized_ty.clone(),
)
}
BoundKind::Set { addr, value: _value } => {
// In an assignment pattern, 'value' is just a Nop placeholder from the Binder.
// We update the type of the address (if it's a local or global) to match the specialized type.
// Note: For now, we assume assignments are compatible if types were already inferred,
// or we could add a check here against existing type.
(
BoundKind::Set {
addr,
value: Box::new(Node {
identity: node.identity.clone(),
kind: BoundKind::Nop,
ty: specialized_ty.clone(),
}),
},
specialized_ty.clone(),
)
}
BoundKind::Tuple { elements } => {
let mut typed_elements = Vec::new();
let mut elem_types = Vec::new();
@@ -282,12 +299,12 @@ impl TypeChecker {
)
}
BoundKind::DefDestructure { pattern, value } => {
BoundKind::Destructure { pattern, value } => {
let val_typed = self.check_node(*value, ctx)?;
let pat_typed = self.check_params(*pattern, &val_typed.ty, ctx)?;
let ty = val_typed.ty.clone();
(
BoundKind::DefDestructure {
BoundKind::Destructure {
pattern: Box::new(pat_typed),
value: Box::new(val_typed),
},