From f980d9befc8765e2e9a00da986ed33b7ae13c640 Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Sat, 21 Feb 2026 20:01:58 +0100 Subject: [PATCH] Add PartialEq to BoundKind and Value Implement PartialEq for BoundKind to allow for structural equality checks during optimization. This enables the optimizer to terminate early when a node no longer changes. Also, implement PartialEq for Value to facilitate comparisons between different Value variants. --- src/ast/compiler/bound_nodes.rs | 56 +++++++++++++++++++++++++++++++++ src/ast/compiler/optimizer.rs | 4 ++- src/ast/nodes.rs | 2 +- src/ast/types.rs | 25 ++++++++++++++- 4 files changed, 84 insertions(+), 3 deletions(-) diff --git a/src/ast/compiler/bound_nodes.rs b/src/ast/compiler/bound_nodes.rs index f72a473..54fe4af 100644 --- a/src/ast/compiler/bound_nodes.rs +++ b/src/ast/compiler/bound_nodes.rs @@ -114,6 +114,62 @@ pub enum BoundKind { Extension(Box>), } +impl PartialEq for BoundKind +where T: PartialEq { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (BoundKind::Nop, BoundKind::Nop) => true, + (BoundKind::Constant(a), BoundKind::Constant(b)) => a == b, + (BoundKind::Parameter { name: na, slot: sa }, BoundKind::Parameter { name: nb, slot: sb }) => { + na == nb && sa == sb + } + (BoundKind::Get { addr: aa, name: na }, BoundKind::Get { addr: ab, name: nb }) => { + aa == ab && na == nb + } + (BoundKind::Set { addr: aa, value: va }, BoundKind::Set { addr: ab, value: vb }) => { + aa == ab && va == vb + } + (BoundKind::DefLocal { name: na, slot: sa, value: va, captured_by: ca }, + BoundKind::DefLocal { name: nb, slot: sb, value: vb, captured_by: cb }) => { + na == nb && sa == sb && va == vb && ca == cb + } + (BoundKind::If { cond: ca, then_br: ta, else_br: ea }, + BoundKind::If { cond: cb, then_br: tb, else_br: eb }) => { + ca == cb && ta == tb && ea == eb + } + (BoundKind::DefGlobal { name: na, global_index: ga, value: va }, + BoundKind::DefGlobal { name: nb, global_index: gb, value: vb }) => { + na == nb && ga == gb && va == vb + } + (BoundKind::Lambda { params: pa, upvalues: ua, body: ba, positional_count: pca }, + BoundKind::Lambda { params: pb, upvalues: ub, body: bb, positional_count: pcb }) => { + pa == pb && ua == ub && ba == bb && pca == pcb + } + (BoundKind::Call { callee: ca, args: aa }, BoundKind::Call { callee: cb, args: ab }) => { + ca == cb && aa == ab + } + (BoundKind::TailCall { callee: ca, args: aa }, BoundKind::TailCall { callee: cb, args: ab }) => { + ca == cb && aa == ab + } + (BoundKind::Block { exprs: ea }, BoundKind::Block { exprs: eb }) => { + ea == eb + } + (BoundKind::Tuple { elements: ea }, BoundKind::Tuple { elements: eb }) => { + ea == eb + } + (BoundKind::Record { fields: fa }, BoundKind::Record { fields: fb }) => { + fa == fb + } + (BoundKind::Expansion { original_call: oa, bound_expanded: ba }, + BoundKind::Expansion { original_call: ob, bound_expanded: bb }) => { + Rc::ptr_eq(oa, ob) && ba == bb + } + (BoundKind::Extension(_), BoundKind::Extension(_)) => false, // Currently no equality for extensions + _ => false, + } + } +} + /// A single field in a Record literal (Key-Value pair) pub type RecordField = (BoundNode, BoundNode); diff --git a/src/ast/compiler/optimizer.rs b/src/ast/compiler/optimizer.rs index 52d86d1..61c997f 100644 --- a/src/ast/compiler/optimizer.rs +++ b/src/ast/compiler/optimizer.rs @@ -24,7 +24,9 @@ impl Optimizer { let mut current = node; for _ in 0..self.max_passes { let next = self.visit_node(current.clone()); - // TODO: Structural equality check to stop early + if next == current { + break; + } current = next; } current diff --git a/src/ast/nodes.rs b/src/ast/nodes.rs index 3db5ccd..8fe5680 100644 --- a/src/ast/nodes.rs +++ b/src/ast/nodes.rs @@ -25,7 +25,7 @@ impl From<&str> for Symbol { } /// A generic AST Node wrapper to preserve identity and metadata -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Node { pub identity: Identity, pub kind: K, diff --git a/src/ast/types.rs b/src/ast/types.rs index 65b760f..f41ba79 100644 --- a/src/ast/types.rs +++ b/src/ast/types.rs @@ -59,7 +59,7 @@ pub trait Object: fmt::Debug { pub type ValueList = Rc>; /// Internal storage for Records to allow sharing schema (keys) between instances. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct RecordData { /// Names for slots. pub keys: Rc>, @@ -85,6 +85,29 @@ pub enum Value { TailCallRequest(Box<(Rc, Vec)>), // Internal: For TCO (Boxed to keep Value small) } +impl PartialEq for Value { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Value::Void, Value::Void) => true, + (Value::Bool(a), Value::Bool(b)) => a == b, + (Value::Int(a), Value::Int(b)) => a == b, + (Value::Float(a), Value::Float(b)) => a == b, + (Value::DateTime(a), Value::DateTime(b)) => a == b, + (Value::Text(a), Value::Text(b)) => a == b, + (Value::Keyword(a), Value::Keyword(b)) => a == b, + (Value::Tuple(a), Value::Tuple(b)) => a == b, + (Value::Record(a), Value::Record(b)) => a == b, + (Value::Function(a), Value::Function(b)) => Rc::ptr_eq(a, b), + (Value::Object(a), Value::Object(b)) => Rc::ptr_eq(a, b), + (Value::Cell(a), Value::Cell(b)) => Rc::ptr_eq(a, b), + (Value::TailCallRequest(a), Value::TailCallRequest(b)) => { + Rc::ptr_eq(&a.0, &b.0) && a.1 == b.1 + } + _ => false, + } + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Signature { pub params: StaticType,