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.
This commit is contained in:
Michael Schimmel
2026-02-21 20:01:58 +01:00
parent 56b8e8389b
commit f980d9befc
4 changed files with 84 additions and 3 deletions
+56
View File
@@ -114,6 +114,62 @@ pub enum BoundKind<T = ()> {
Extension(Box<dyn BoundExtension<T>>),
}
impl<T> PartialEq for BoundKind<T>
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<T> = (BoundNode<T>, BoundNode<T>);
+3 -1
View File
@@ -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
+1 -1
View File
@@ -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<K, T = ()> {
pub identity: Identity,
pub kind: K,
+24 -1
View File
@@ -59,7 +59,7 @@ pub trait Object: fmt::Debug {
pub type ValueList = Rc<Vec<Value>>;
/// 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<Vec<Keyword>>,
@@ -85,6 +85,29 @@ pub enum Value {
TailCallRequest(Box<(Rc<dyn Object>, Vec<Value>)>), // 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,