From 472771f3986a618ff1001c8244fc48a6b034fea3 Mon Sep 17 00:00:00 2001 From: Arthur Paulino Date: Wed, 1 Nov 2023 10:53:05 -0300 Subject: [PATCH] several minor API improvements, new docstrings, and helper gadgets for coprocessors authors --- src/cli/commitment.rs | 6 +- src/cli/repl.rs | 16 +-- src/cli/zstore.rs | 82 ++++++------ src/coprocessor/circom.rs | 4 +- src/coprocessor/gadgets.rs | 241 ++++++++++++++++++++++++++++++++++++ src/coprocessor/mod.rs | 6 +- src/coprocessor/trie/mod.rs | 4 +- src/eval/tests/mod.rs | 6 +- src/lem/circuit.rs | 100 +++++++-------- src/lem/eval.rs | 66 +++++----- src/lem/pointers.rs | 11 -- src/lem/store.rs | 62 ++++++++-- src/lem/tests/eval_tests.rs | 4 +- src/store.rs | 4 +- src/z_data/z_ptr.rs | 4 +- 15 files changed, 438 insertions(+), 178 deletions(-) create mode 100644 src/coprocessor/gadgets.rs diff --git a/src/cli/commitment.rs b/src/cli/commitment.rs index 2f6144f9c5..ac727efbad 100644 --- a/src/cli/commitment.rs +++ b/src/cli/commitment.rs @@ -33,13 +33,13 @@ impl HasFieldModulus for Commitment { } impl Commitment { - pub(crate) fn new(secret: Option, payload: Ptr, store: &Store) -> Result { + pub(crate) fn new(secret: Option, payload: Ptr, store: &Store) -> Self { let secret = secret.unwrap_or(F::NON_HIDING_COMMITMENT_SECRET); let (hash, z_payload) = store.hide_and_return_z_payload(secret, payload); let mut z_store = ZStore::::default(); - populate_z_store(&mut z_store, &payload, store, &mut HashMap::default())?; + populate_z_store(&mut z_store, &payload, store, &mut HashMap::default()); z_store.add_comm(hash, secret, z_payload); - Ok(Self { hash, z_store }) + Self { hash, z_store } } #[inline] diff --git a/src/cli/repl.rs b/src/cli/repl.rs index 63bfd93e37..5cb1e6cbff 100644 --- a/src/cli/repl.rs +++ b/src/cli/repl.rs @@ -213,15 +213,15 @@ impl Repl { let output = &frames[n_frames - 1].output; let mut z_store = ZStore::::default(); let mut cache = HashMap::default(); - let expr = populate_z_store(&mut z_store, &input[0], &self.store, &mut cache)?; - let env = populate_z_store(&mut z_store, &input[1], &self.store, &mut cache)?; - let cont = populate_z_store(&mut z_store, &input[2], &self.store, &mut cache)?; + let expr = populate_z_store(&mut z_store, &input[0], &self.store, &mut cache); + let env = populate_z_store(&mut z_store, &input[1], &self.store, &mut cache); + let cont = populate_z_store(&mut z_store, &input[2], &self.store, &mut cache); let expr_out = - populate_z_store(&mut z_store, &output[0], &self.store, &mut cache)?; + populate_z_store(&mut z_store, &output[0], &self.store, &mut cache); let env_out = - populate_z_store(&mut z_store, &output[1], &self.store, &mut cache)?; + populate_z_store(&mut z_store, &output[1], &self.store, &mut cache); let cont_out = - populate_z_store(&mut z_store, &output[2], &self.store, &mut cache)?; + populate_z_store(&mut z_store, &output[2], &self.store, &mut cache); let claim = Self::proof_claim( &self.store, @@ -230,7 +230,7 @@ impl Repl { (cont.parts(), cont_out.parts()), ); - let claim_comm = Commitment::new(None, claim, &self.store)?; + let claim_comm = Commitment::new(None, claim, &self.store); let claim_hash = &claim_comm.hash.hex_digits(); let proof_key = &Self::proof_key(&self.backend, &self.rc, claim_hash); let proof_path = proof_path(proof_key); @@ -292,7 +292,7 @@ impl Repl { } fn hide(&mut self, secret: F, payload: Ptr) -> Result<()> { - let commitment = Commitment::new(Some(secret), payload, &self.store)?; + let commitment = Commitment::new(Some(secret), payload, &self.store); let hash_str = &commitment.hash.hex_digits(); commitment.persist()?; println!( diff --git a/src/cli/zstore.rs b/src/cli/zstore.rs index 4fe48f2bca..28830c183f 100644 --- a/src/cli/zstore.rs +++ b/src/cli/zstore.rs @@ -5,16 +5,28 @@ use std::collections::{BTreeMap, HashMap}; use crate::{ field::{FWrap, LurkField}, lem::{ - pointers::{Ptr, ZChildren, ZPtr}, + pointers::{Ptr, ZPtr}, store::Store, }, }; use super::field_data::HasFieldModulus; +/// `ZPtrType` holds information about the `Ptr` that originated a certain `ZPtr`. +/// If the `Ptr` was not atomic, `ZPtrType` can refer to its children once they +/// have already been turned into `ZPtr`s. +#[derive(Debug, Serialize, Deserialize)] +pub(crate) enum ZPtrType { + Atom, + Tuple2(ZPtr, ZPtr), + Tuple3(ZPtr, ZPtr, ZPtr), + Tuple4(ZPtr, ZPtr, ZPtr, ZPtr), +} + +/// A `ZStore` is a stable IO format for `Store`, without index-based references #[derive(Debug, Default, Serialize, Deserialize)] pub(crate) struct ZStore { - dag: BTreeMap, ZChildren>, + dag: BTreeMap, ZPtrType>, comms: BTreeMap, (F, ZPtr)>, } @@ -36,7 +48,7 @@ impl ZStore { } #[inline] - pub(crate) fn get_children(&self, z_ptr: &ZPtr) -> Option<&ZChildren> { + pub(crate) fn get_type(&self, z_ptr: &ZPtr) -> Option<&ZPtrType> { self.dag.get(z_ptr) } @@ -59,23 +71,21 @@ pub(crate) fn populate_z_store( ptr: &Ptr, store: &Store, cache: &mut HashMap, ZPtr>, -) -> Result> { - let mut recurse = |ptr: &Ptr| -> Result> { +) -> ZPtr { + let mut recurse = |ptr: &Ptr| -> ZPtr { if let Some(z_ptr) = cache.get(ptr) { - Ok(*z_ptr) + *z_ptr } else { let z_ptr = match ptr { Ptr::Atom(tag, f) => { let z_ptr = ZPtr::from_parts(*tag, *f); - z_store.dag.insert(z_ptr, ZChildren::Atom); + z_store.dag.insert(z_ptr, ZPtrType::Atom); z_ptr } Ptr::Tuple2(tag, idx) => { - let Some((a, b)) = store.fetch_2_ptrs(*idx) else { - bail!("Index {idx} not found on tuple2") - }; - let a = populate_z_store(z_store, a, store, cache)?; - let b = populate_z_store(z_store, b, store, cache)?; + let (a, b) = store.expect_2_ptrs(*idx); + let a = populate_z_store(z_store, a, store, cache); + let b = populate_z_store(z_store, b, store, cache); let z_ptr = ZPtr::from_parts( *tag, store.poseidon_cache.hash4(&[ @@ -85,16 +95,14 @@ pub(crate) fn populate_z_store( *b.value(), ]), ); - z_store.dag.insert(z_ptr, ZChildren::Tuple2(a, b)); + z_store.dag.insert(z_ptr, ZPtrType::Tuple2(a, b)); z_ptr } Ptr::Tuple3(tag, idx) => { - let Some((a, b, c)) = store.fetch_3_ptrs(*idx) else { - bail!("Index {idx} not found on tuple3") - }; - let a = populate_z_store(z_store, a, store, cache)?; - let b = populate_z_store(z_store, b, store, cache)?; - let c = populate_z_store(z_store, c, store, cache)?; + let (a, b, c) = store.expect_3_ptrs(*idx); + let a = populate_z_store(z_store, a, store, cache); + let b = populate_z_store(z_store, b, store, cache); + let c = populate_z_store(z_store, c, store, cache); let z_ptr = ZPtr::from_parts( *tag, store.poseidon_cache.hash6(&[ @@ -106,17 +114,15 @@ pub(crate) fn populate_z_store( *c.value(), ]), ); - z_store.dag.insert(z_ptr, ZChildren::Tuple3(a, b, c)); + z_store.dag.insert(z_ptr, ZPtrType::Tuple3(a, b, c)); z_ptr } Ptr::Tuple4(tag, idx) => { - let Some((a, b, c, d)) = store.fetch_4_ptrs(*idx) else { - bail!("Index {idx} not found on tuple4") - }; - let a = populate_z_store(z_store, a, store, cache)?; - let b = populate_z_store(z_store, b, store, cache)?; - let c = populate_z_store(z_store, c, store, cache)?; - let d = populate_z_store(z_store, d, store, cache)?; + let (a, b, c, d) = store.expect_4_ptrs(*idx); + let a = populate_z_store(z_store, a, store, cache); + let b = populate_z_store(z_store, b, store, cache); + let c = populate_z_store(z_store, c, store, cache); + let d = populate_z_store(z_store, d, store, cache); let z_ptr = ZPtr::from_parts( *tag, store.poseidon_cache.hash8(&[ @@ -130,12 +136,12 @@ pub(crate) fn populate_z_store( *d.value(), ]), ); - z_store.dag.insert(z_ptr, ZChildren::Tuple4(a, b, c, d)); + z_store.dag.insert(z_ptr, ZPtrType::Tuple4(a, b, c, d)); z_ptr } }; cache.insert(*ptr, z_ptr); - Ok(z_ptr) + z_ptr } }; recurse(ptr) @@ -151,26 +157,26 @@ pub(crate) fn populate_store( if let Some(z_ptr) = cache.get(z_ptr) { Ok(*z_ptr) } else { - let ptr = match z_store.get_children(z_ptr) { - None => bail!("Couldn't find ZPtr"), - Some(ZChildren::Atom) => Ptr::Atom(z_ptr.tag(), *z_ptr.value()), - Some(ZChildren::Tuple2(z1, z2)) => { + let ptr = match z_store.get_type(z_ptr) { + None => bail!("Couldn't find ZPtr on ZStore"), + Some(ZPtrType::Atom) => Ptr::Atom(*z_ptr.tag(), *z_ptr.value()), + Some(ZPtrType::Tuple2(z1, z2)) => { let ptr1 = populate_store(store, z1, z_store, cache)?; let ptr2 = populate_store(store, z2, z_store, cache)?; - store.intern_2_ptrs_hydrated(z_ptr.tag(), ptr1, ptr2, *z_ptr) + store.intern_2_ptrs_hydrated(*z_ptr.tag(), ptr1, ptr2, *z_ptr) } - Some(ZChildren::Tuple3(z1, z2, z3)) => { + Some(ZPtrType::Tuple3(z1, z2, z3)) => { let ptr1 = populate_store(store, z1, z_store, cache)?; let ptr2 = populate_store(store, z2, z_store, cache)?; let ptr3 = populate_store(store, z3, z_store, cache)?; - store.intern_3_ptrs_hydrated(z_ptr.tag(), ptr1, ptr2, ptr3, *z_ptr) + store.intern_3_ptrs_hydrated(*z_ptr.tag(), ptr1, ptr2, ptr3, *z_ptr) } - Some(ZChildren::Tuple4(z1, z2, z3, z4)) => { + Some(ZPtrType::Tuple4(z1, z2, z3, z4)) => { let ptr1 = populate_store(store, z1, z_store, cache)?; let ptr2 = populate_store(store, z2, z_store, cache)?; let ptr3 = populate_store(store, z3, z_store, cache)?; let ptr4 = populate_store(store, z4, z_store, cache)?; - store.intern_4_ptrs_hydrated(z_ptr.tag(), ptr1, ptr2, ptr3, ptr4, *z_ptr) + store.intern_4_ptrs_hydrated(*z_ptr.tag(), ptr1, ptr2, ptr3, ptr4, *z_ptr) } }; cache.insert(*z_ptr, ptr); diff --git a/src/coprocessor/circom.rs b/src/coprocessor/circom.rs index 209e5f8cfd..9038059a18 100644 --- a/src/coprocessor/circom.rs +++ b/src/coprocessor/circom.rs @@ -23,7 +23,7 @@ pub mod non_wasm { cli::paths::circom_dir, coprocessor::{CoCircuit, Coprocessor}, field::LurkField, - lem::{pointers::Ptr as LEMPtr, store::Store as LEMStore, Tag}, + lem::{pointers::Ptr as LEMPtr, store::Store as LEMStore}, ptr::Ptr, store::Store, }; @@ -150,7 +150,7 @@ Then run `lurk coprocessor --name {name} <{}_FOLDER>` to instantiate a new gadge })?; let output = circom_scotia::synthesize(cs, self.config.r1cs.clone(), Some(witness))?; let num_tag = g - .get_allocated_const(Tag::Expr(crate::tag::ExprTag::Num).to_field()) + .get_tag(&crate::tag::ExprTag::Num) .expect("Num tag should have been allocated"); let res = AllocatedPtr::from_parts(num_tag.clone(), output); diff --git a/src/coprocessor/gadgets.rs b/src/coprocessor/gadgets.rs new file mode 100644 index 0000000000..6585be265c --- /dev/null +++ b/src/coprocessor/gadgets.rs @@ -0,0 +1,241 @@ +//! Helper gadgets for synthesis +//! +//! TODO: deconstructing gadgets from `circuit_frame.rs` + +use bellpepper_core::{ConstraintSystem, SynthesisError}; + +use crate::{ + circuit::gadgets::{data::hash_poseidon, pointer::AllocatedPtr}, + field::LurkField, + lem::{circuit::GlobalAllocator, store::Store}, + tag::{ExprTag, Tag}, +}; + +pub fn construct_tuple2, T: Tag>( + cs: CS, + g: &GlobalAllocator, + store: &Store, + tag: &T, + a: &AllocatedPtr, + b: &AllocatedPtr, +) -> Result, SynthesisError> { + let tag = g.get_tag_cloned(tag).expect("Tag not allocated"); + + let hash = hash_poseidon( + cs, + vec![ + a.tag().clone(), + a.hash().clone(), + b.tag().clone(), + b.hash().clone(), + ], + store.poseidon_cache.constants.c4(), + )?; + + Ok(AllocatedPtr::from_parts(tag, hash)) +} + +pub fn construct_tuple3, T: Tag>( + cs: CS, + g: &GlobalAllocator, + store: &Store, + tag: &T, + a: &AllocatedPtr, + b: &AllocatedPtr, + c: &AllocatedPtr, +) -> Result, SynthesisError> { + let tag = g.get_tag_cloned(tag).expect("Tag not allocated"); + + let hash = hash_poseidon( + cs, + vec![ + a.tag().clone(), + a.hash().clone(), + b.tag().clone(), + b.hash().clone(), + c.tag().clone(), + c.hash().clone(), + ], + store.poseidon_cache.constants.c6(), + )?; + + Ok(AllocatedPtr::from_parts(tag, hash)) +} + +pub fn construct_tuple4, T: Tag>( + cs: CS, + g: &GlobalAllocator, + store: &Store, + tag: &T, + a: &AllocatedPtr, + b: &AllocatedPtr, + c: &AllocatedPtr, + d: &AllocatedPtr, +) -> Result, SynthesisError> { + let tag = g.get_tag_cloned(tag).expect("Tag not allocated"); + + let hash = hash_poseidon( + cs, + vec![ + a.tag().clone(), + a.hash().clone(), + b.tag().clone(), + b.hash().clone(), + c.tag().clone(), + c.hash().clone(), + d.tag().clone(), + d.hash().clone(), + ], + store.poseidon_cache.constants.c8(), + )?; + + Ok(AllocatedPtr::from_parts(tag, hash)) +} + +#[inline] +pub fn construct_cons>( + cs: CS, + g: &GlobalAllocator, + store: &Store, + car: &AllocatedPtr, + cdr: &AllocatedPtr, +) -> Result, SynthesisError> { + construct_tuple2(cs, g, store, &ExprTag::Cons, car, cdr) +} + +/// Constructs a cons-list with the provided `elts`. The terminating value defaults +/// to the `nil` allocated pointer when `last` is `None` +pub fn construct_list>( + cs: &mut CS, + g: &GlobalAllocator, + store: &Store, + elts: &[&AllocatedPtr], + last: Option>, +) -> Result, SynthesisError> { + let init = last.unwrap_or_else(|| { + g.get_allocated_ptr_from_ptr(&store.intern_nil(), store) + .expect("nil pointer not allocated") + }); + elts.iter() + .rev() + .enumerate() + .try_fold(init, |acc, (i, ptr)| { + construct_cons(cs.namespace(|| format!("Cons {i}")), g, store, ptr, &acc) + }) +} + +#[cfg(test)] +mod test { + use bellpepper::util_cs::witness_cs::WitnessCS; + use bellpepper_core::ConstraintSystem; + use pasta_curves::Fq; + + use crate::{ + coprocessor::gadgets::{construct_tuple2, construct_tuple3, construct_tuple4}, + lem::{circuit::GlobalAllocator, pointers::Ptr, store::Store}, + tag::{ExprTag, Tag}, + }; + + use super::construct_list; + + #[test] + fn test_tuples() { + let mut cs = WitnessCS::new(); + let mut g = GlobalAllocator::default(); + let store = Store::::default(); + let nil = store.intern_nil(); + let z_nil = store.hash_ptr(&nil); + let nil_tag = z_nil.tag(); + g.new_const(&mut cs, nil_tag.to_field()); + g.new_const(&mut cs, *z_nil.value()); + let a_nil = g.get_allocated_ptr(nil_tag, *z_nil.value()).unwrap(); + + let nil2 = construct_tuple2( + &mut cs.namespace(|| "nil2"), + &g, + &store, + nil_tag, + &a_nil, + &a_nil, + ) + .unwrap(); + let nil2_ptr = store.intern_2_ptrs(*nil_tag, nil, nil); + let z_nil2_ptr = store.hash_ptr(&nil2_ptr); + assert_eq!(nil2.tag().get_value(), Some(z_nil2_ptr.tag_field())); + assert_eq!(nil2.hash().get_value(), Some(*z_nil2_ptr.value())); + + let nil3 = construct_tuple3( + &mut cs.namespace(|| "nil3"), + &g, + &store, + nil_tag, + &a_nil, + &a_nil, + &a_nil, + ) + .unwrap(); + let nil3_ptr = store.intern_3_ptrs(*nil_tag, nil, nil, nil); + let z_nil3_ptr = store.hash_ptr(&nil3_ptr); + assert_eq!(nil3.tag().get_value(), Some(z_nil3_ptr.tag_field())); + assert_eq!(nil3.hash().get_value(), Some(*z_nil3_ptr.value())); + + let nil4 = construct_tuple4( + &mut cs.namespace(|| "nil4"), + &g, + &store, + nil_tag, + &a_nil, + &a_nil, + &a_nil, + &a_nil, + ) + .unwrap(); + let nil4_ptr = store.intern_4_ptrs(*nil_tag, nil, nil, nil, nil); + let z_nil4_ptr = store.hash_ptr(&nil4_ptr); + assert_eq!(nil4.tag().get_value(), Some(z_nil4_ptr.tag_field())); + assert_eq!(nil4.hash().get_value(), Some(*z_nil4_ptr.value())); + } + + #[test] + fn test_list() { + let mut cs = WitnessCS::new(); + let mut g = GlobalAllocator::default(); + let store = Store::::default(); + let one = Ptr::num_u64(1); + let nil = store.intern_nil(); + let z_one = store.hash_ptr(&one); + let z_nil = store.hash_ptr(&nil); + g.new_const(&mut cs, z_nil.tag_field()); + g.new_const(&mut cs, *z_nil.value()); + g.new_const(&mut cs, z_one.tag_field()); + g.new_const(&mut cs, *z_one.value()); + g.new_const(&mut cs, ExprTag::Cons.to_field()); + let a_one = g.get_allocated_ptr(z_one.tag(), *z_one.value()).unwrap(); + + // proper list + let a_list = construct_list( + &mut cs.namespace(|| "proper list"), + &g, + &store, + &[&a_one, &a_one], + None, + ) + .unwrap(); + let z_list = store.hash_ptr(&store.list(vec![one, one])); + assert_eq!(a_list.tag().get_value(), Some(z_list.tag_field())); + assert_eq!(a_list.hash().get_value(), Some(*z_list.value())); + + // improper list + let a_list = construct_list( + &mut cs.namespace(|| "improper list"), + &g, + &store, + &[&a_one, &a_one], + Some(a_one.clone()), + ) + .unwrap(); + let z_list = store.hash_ptr(&store.improper_list(vec![one, one], one)); + assert_eq!(a_list.tag().get_value(), Some(z_list.tag_field())); + assert_eq!(a_list.hash().get_value(), Some(*z_list.value())); + } +} diff --git a/src/coprocessor/mod.rs b/src/coprocessor/mod.rs index 7e0d9bb179..080d6dd1be 100644 --- a/src/coprocessor/mod.rs +++ b/src/coprocessor/mod.rs @@ -16,6 +16,7 @@ use crate::tag::Tag; use crate::z_data::z_ptr::ZExprPtr; pub mod circom; +pub mod gadgets; pub mod sha256; pub mod trie; @@ -344,12 +345,11 @@ pub(crate) mod test { input_cont: &AllocatedPtr, ) -> Result>, SynthesisError> { let num_tag = g - .get_allocated_const(LEMTag::Expr(ExprTag::Num).to_field()) + .get_tag(&ExprTag::Num) .expect("Num tag should have been allocated"); - let err_cont_z_ptr = s.hash_ptr(&s.cont_error()); let cont_err = g - .get_allocated_ptr_from_z_ptr(&err_cont_z_ptr) + .get_allocated_ptr_from_ptr(&s.cont_error(), s) .expect("Error pointer should have been allocated"); let (expr, env, cont) = diff --git a/src/coprocessor/trie/mod.rs b/src/coprocessor/trie/mod.rs index 7e6489bf5b..8536cb6a81 100644 --- a/src/coprocessor/trie/mod.rs +++ b/src/coprocessor/trie/mod.rs @@ -281,7 +281,7 @@ impl CoCircuit for LookupCoprocessor { )?; let comm_tag = g - .get_allocated_const(ExprTag::Comm.to_field()) + .get_tag(&ExprTag::Comm) .expect("Comm tag should have been allocated"); Ok(AllocatedPtr::from_parts( @@ -445,7 +445,7 @@ impl CoCircuit for InsertCoprocessor { )?; let num_tag = g - .get_allocated_const(ExprTag::Num.to_field()) + .get_tag(&ExprTag::Num) .expect("Num tag should have been allocated"); Ok(AllocatedPtr::from_parts(num_tag.clone(), new_root_val)) } diff --git a/src/eval/tests/mod.rs b/src/eval/tests/mod.rs index e9cb2095b3..5a4d9b1e79 100644 --- a/src/eval/tests/mod.rs +++ b/src/eval/tests/mod.rs @@ -2671,7 +2671,7 @@ fn test_root_sym() { let z_ptr = &s.hash_expr(&x).unwrap(); assert_eq!(&Fr::zero(), z_ptr.value()); - assert_eq!(ExprTag::Sym, z_ptr.tag()); + assert_eq!(&ExprTag::Sym, z_ptr.tag()); } #[test] @@ -2737,8 +2737,8 @@ fn test_sym_hash_values() { assert_eq!(consed_with_root_z_ptr.value(), toplevel_z_ptr.value()); // The tags differ though. - assert_eq!(ExprTag::Sym, sym_z_ptr.tag()); - assert_eq!(ExprTag::Key, key_z_ptr.tag()); + assert_eq!(&ExprTag::Sym, sym_z_ptr.tag()); + assert_eq!(&ExprTag::Key, key_z_ptr.tag()); } #[test] diff --git a/src/lem/circuit.rs b/src/lem/circuit.rs index 198cc4ffd4..c404f5ac0b 100644 --- a/src/lem/circuit.rs +++ b/src/lem/circuit.rs @@ -91,42 +91,50 @@ pub struct GlobalAllocator(HashMap, AllocatedNum>); impl GlobalAllocator { /// Checks if the allocation for a numeric variable has already been cached. /// If so, don't do anything. Otherwise, allocate and cache it. - fn new_const>(&mut self, cs: &mut CS, f: F) { - self.0.entry(FWrap(f)).or_insert_with(|| { - allocate_constant( - &mut cs.namespace(|| format!("allocate constant {}", f.hex_digits())), - f, - ) - }); + pub fn new_const>(&mut self, cs: &mut CS, f: F) { + self.0 + .entry(FWrap(f)) + .or_insert_with(|| allocate_constant(&mut cs.namespace(|| f.hex_digits()), f)); } #[inline] - pub fn get_allocated_const(&self, f: F) -> Result<&AllocatedNum> { + pub fn get_const(&self, f: F) -> Result<&AllocatedNum> { self.0 .get(&FWrap(f)) .ok_or_else(|| anyhow!("Global allocation not found for {}", f.hex_digits())) } #[inline] - fn get_allocated_const_cloned(&self, f: F) -> Result> { - self.get_allocated_const(f).cloned() + fn get_const_cloned(&self, f: F) -> Result> { + self.get_const(f).cloned() } #[inline] - pub fn get_allocated_ptr(&self, tag: &super::Tag, hash: F) -> Result> { - Ok(AllocatedPtr::from_parts( - self.get_allocated_const_cloned(tag.to_field())?, - self.get_allocated_const_cloned(hash)?, - )) + pub fn get_tag(&self, tag: &T) -> Result<&AllocatedNum> { + self.get_const(tag.to_field()) + } + + #[inline] + pub fn get_tag_cloned(&self, tag: &T) -> Result> { + self.get_tag(tag).cloned() } #[inline] - pub fn get_allocated_ptr_from_z_ptr(&self, z_ptr: &ZPtr) -> Result> { + pub fn get_allocated_ptr(&self, tag: &T, hash: F) -> Result> { Ok(AllocatedPtr::from_parts( - self.get_allocated_const_cloned(z_ptr.tag_field())?, - self.get_allocated_const_cloned(*z_ptr.value())?, + self.get_tag_cloned(tag)?, + self.get_const_cloned(hash)?, )) } + + pub fn get_allocated_ptr_from_ptr( + &self, + ptr: &Ptr, + store: &Store, + ) -> Result> { + let crate::z_ptr::ZPtr(tag, hash) = store.hash_ptr(ptr); + self.get_allocated_ptr(&tag, hash) + } } pub(crate) type BoundAllocations = VarMap>; @@ -518,9 +526,7 @@ fn synthesize_block, C: Coprocessor>( // Allocate the image tag if it hasn't been allocated before, // create the full image pointer and add it to bound allocations - let img_tag = ctx - .global_allocator - .get_allocated_const_cloned($tag.to_field())?; + let img_tag = ctx.global_allocator.get_tag_cloned($tag)?; let AllocatedVal::Number(img_hash) = preallocated_img_hash else { bail!("Expected number") }; @@ -735,20 +741,14 @@ fn synthesize_block, C: Coprocessor>( ); } Op::Lit(tgt, lit) => { - let lit_ptr = lit.to_ptr(ctx.store); - let lit_tag = lit_ptr.tag().to_field(); - let allocated_tag = ctx.global_allocator.get_allocated_const_cloned(lit_tag)?; - let allocated_hash = ctx + let allocated_ptr = ctx .global_allocator - .get_allocated_const_cloned(*ctx.store.hash_ptr(&lit_ptr).value())?; - let allocated_ptr = AllocatedPtr::from_parts(allocated_tag, allocated_hash); + .get_allocated_ptr_from_ptr(&lit.to_ptr(ctx.store), ctx.store)?; bound_allocations.insert_ptr(tgt.clone(), allocated_ptr); } Op::Cast(tgt, tag, src) => { let src = bound_allocations.get_ptr(src)?; - let tag = ctx - .global_allocator - .get_allocated_const_cloned(tag.to_field())?; + let tag = ctx.global_allocator.get_tag_cloned(tag)?; let allocated_ptr = AllocatedPtr::from_parts(tag, src.hash().clone()); bound_allocations.insert_ptr(tgt.clone(), allocated_ptr); } @@ -790,9 +790,7 @@ fn synthesize_block, C: Coprocessor>( let a_num = a.hash(); let b_num = b.hash(); let c_num = add(cs.namespace(|| "add"), a_num, b_num)?; - let tag = ctx - .global_allocator - .get_allocated_const_cloned(Num.to_field())?; + let tag = ctx.global_allocator.get_tag_cloned(&Num)?; let c = AllocatedPtr::from_parts(tag, c_num); bound_allocations.insert_ptr(tgt.clone(), c); } @@ -802,9 +800,7 @@ fn synthesize_block, C: Coprocessor>( let a_num = a.hash(); let b_num = b.hash(); let c_num = sub(cs.namespace(|| "sub"), a_num, b_num)?; - let tag = ctx - .global_allocator - .get_allocated_const_cloned(Num.to_field())?; + let tag = ctx.global_allocator.get_tag_cloned(&Num)?; let c = AllocatedPtr::from_parts(tag, c_num); bound_allocations.insert_ptr(tgt.clone(), c); } @@ -814,9 +810,7 @@ fn synthesize_block, C: Coprocessor>( let a_num = a.hash(); let b_num = b.hash(); let c_num = mul(cs.namespace(|| "mul"), a_num, b_num)?; - let tag = ctx - .global_allocator - .get_allocated_const_cloned(Num.to_field())?; + let tag = ctx.global_allocator.get_tag_cloned(&Num)?; let c = AllocatedPtr::from_parts(tag, c_num); bound_allocations.insert_ptr(tgt.clone(), c); } @@ -827,7 +821,7 @@ fn synthesize_block, C: Coprocessor>( let b_num = b.hash(); let b_is_zero = &alloc_is_zero(cs.namespace(|| "b_is_zero"), b_num)?; - let one = ctx.global_allocator.get_allocated_const(F::ONE)?; + let one = ctx.global_allocator.get_const(F::ONE)?; let divisor = pick( cs.namespace(|| "maybe-dummy divisor"), @@ -838,9 +832,7 @@ fn synthesize_block, C: Coprocessor>( let quotient = div(cs.namespace(|| "quotient"), a_num, &divisor)?; - let tag = ctx - .global_allocator - .get_allocated_const_cloned(Num.to_field())?; + let tag = ctx.global_allocator.get_tag_cloned(&Num)?; let c = AllocatedPtr::from_parts(tag, quotient); bound_allocations.insert_ptr(tgt.clone(), c); } @@ -942,9 +934,7 @@ fn synthesize_block, C: Coprocessor>( trunc_bits, &trunc, ); - let tag = ctx - .global_allocator - .get_allocated_const_cloned(Num.to_field())?; + let tag = ctx.global_allocator.get_tag_cloned(&Num)?; let c = AllocatedPtr::from_parts(tag, trunc); bound_allocations.insert_ptr(tgt.clone(), c); } @@ -973,9 +963,7 @@ fn synthesize_block, C: Coprocessor>( implies_u64(cs.namespace(|| "diff_u64"), not_dummy, &diff)?; enforce_product_and_sum(&mut cs, || "enforce a = b * div + rem", b, &div, &rem, a); - let tag = ctx - .global_allocator - .get_allocated_const_cloned(Num.to_field())?; + let tag = ctx.global_allocator.get_tag_cloned(&Num)?; let div_ptr = AllocatedPtr::from_parts(tag.clone(), div); let rem_ptr = AllocatedPtr::from_parts(tag, rem); bound_allocations.insert_ptr(tgt[0].clone(), div_ptr); @@ -985,7 +973,7 @@ fn synthesize_block, C: Coprocessor>( Op::Hide(tgt, sec, pay) => { let sec = bound_allocations.get_ptr(sec)?; let pay = bound_allocations.get_ptr(pay)?; - let sec_tag = ctx.global_allocator.get_allocated_const(Num.to_field())?; + let sec_tag = ctx.global_allocator.get_const(Num.to_field())?; let (preallocated_preimg, hash) = &ctx.commitment_slots[next_slot.consume_commitment()]; let AllocatedVal::Number(hash) = hash else { @@ -1015,9 +1003,7 @@ fn synthesize_block, C: Coprocessor>( pay.hash(), &preallocated_preimg[2], ); - let tag = ctx - .global_allocator - .get_allocated_const_cloned(Comm.to_field())?; + let tag = ctx.global_allocator.get_tag_cloned(&Comm)?; let allocated_ptr = AllocatedPtr::from_parts(tag, hash.clone()); bound_allocations.insert_ptr(tgt.clone(), allocated_ptr); } @@ -1025,7 +1011,7 @@ fn synthesize_block, C: Coprocessor>( let comm = bound_allocations.get_ptr(comm)?; let (preallocated_preimg, com_hash) = &ctx.commitment_slots[next_slot.consume_commitment()]; - let comm_tag = ctx.global_allocator.get_allocated_const(Comm.to_field())?; + let comm_tag = ctx.global_allocator.get_const(Comm.to_field())?; let AllocatedVal::Number(com_hash) = com_hash else { panic!("Excepted number") }; @@ -1041,9 +1027,7 @@ fn synthesize_block, C: Coprocessor>( comm.hash(), com_hash, ); - let sec_tag = ctx - .global_allocator - .get_allocated_const_cloned(Num.to_field())?; + let sec_tag = ctx.global_allocator.get_tag_cloned(&Num)?; let allocated_sec_ptr = AllocatedPtr::from_parts(sec_tag, preallocated_preimg[0].clone()); let allocated_pay_ptr = AllocatedPtr::from_parts( @@ -1230,7 +1214,7 @@ fn synthesize_block, C: Coprocessor>( )?; // Now we enforce `MatchSymbol`'s tag - let sym_tag = ctx.global_allocator.get_allocated_const(Sym.to_field())?; + let sym_tag = ctx.global_allocator.get_const(Sym.to_field())?; implies_equal( &mut cs.namespace(|| format!("implies equal {match_var}.tag")), not_dummy, diff --git a/src/lem/eval.rs b/src/lem/eval.rs index dd543a02d4..83c38843d7 100644 --- a/src/lem/eval.rs +++ b/src/lem/eval.rs @@ -252,8 +252,10 @@ fn make_eval_step(cprocs: &[(&Symbol, usize)], ivc: bool) -> Func { }) } -fn car_cdr() -> Func { - func!(car_cdr(xs): 2 => { +/// Similar to `car_cdr` semantics, but doesn't deconstruct strings to save some +/// constraints +fn cadr() -> Func { + func!(cadr(xs): 2 => { let nil = Symbol("nil"); let nil = cast(nil, Expr::Nil); match xs.tag { @@ -291,11 +293,11 @@ fn car_cdr() -> Func { /// // save a copy of the evaluated arguments for possible arity error /// let evaluated_args_cp = copy(evaluated_args); /// if !is_nil { -/// let (x{n-1}, evaluated_args) = car_cdr(evaluated_args); +/// let (x{n-1}, evaluated_args) = cadr(evaluated_args); /// let is_nil = eq_tag(evaluated_args, nil); /// if !is_nil { /// ... -/// let (x0, evaluated_args) = car_cdr(evaluated_args); +/// let (x0, evaluated_args) = cadr(evaluated_args); /// let is_nil = eq_tag(evaluated_args, nil); /// // there must be no remaining arguments /// if is_nil { @@ -352,7 +354,7 @@ fn run_cproc(cproc_sym: Symbol, arity: usize) -> Func { let ops = vec![ Op::Call( vec![cproc_arg.clone(), evaluated_args.clone()], - Box::new(car_cdr()), + Box::new(cadr()), vec![evaluated_args.clone()], ), Op::EqTag(is_nil.clone(), evaluated_args.clone(), nil.clone()), @@ -495,11 +497,11 @@ fn is_cproc(cprocs: &[(&Symbol, usize)]) -> Func { /// // save a copy of the evaluated arguments for possible arity error /// let evaluated_args_cp = copy(evaluated_args); /// if !is_nil { -/// let (x{n-1}, evaluated_args) = car_cdr(evaluated_args); +/// let (x{n-1}, evaluated_args) = cadr(evaluated_args); /// let is_nil = eq_tag(evaluated_args, nil); /// if !is_nil { /// ... -/// let (x0, evaluated_args) = car_cdr(evaluated_args); +/// let (x0, evaluated_args) = cadr(evaluated_args); /// let is_nil = eq_tag(evaluated_args, nil); /// // there must be no remaining arguments /// if is_nil { @@ -574,7 +576,7 @@ fn match_and_run_cproc(cprocs: &[(&Symbol, usize)]) -> Func { let ops = vec![ Op::Call( vec![cproc_arg.clone(), evaluated_args.clone()], - Box::new(car_cdr()), + Box::new(cadr()), vec![evaluated_args.clone()], ), Op::EqTag(is_nil.clone(), evaluated_args.clone(), nil.clone()), @@ -631,7 +633,7 @@ fn match_and_run_cproc(cprocs: &[(&Symbol, usize)]) -> Func { fn reduce(cprocs: &[(&Symbol, usize)]) -> Func { // Auxiliary functions - let car_cdr = car_cdr(); + let cadr = cadr(); let env_to_use = func!(env_to_use(smaller_env, smaller_rec_env): 1 => { match smaller_rec_env.tag { Expr::Nil => { @@ -887,7 +889,7 @@ fn reduce(cprocs: &[(&Symbol, usize)]) -> Func { } }; - let (binding, smaller_env) = car_cdr(env); + let (binding, smaller_env) = cadr(env); match binding.tag { Expr::Nil => { return (expr, env, err, errctrl) @@ -895,7 +897,7 @@ fn reduce(cprocs: &[(&Symbol, usize)]) -> Func { }; let (var_or_rec_binding, val_or_more_rec_env) = - car_cdr(binding); + cadr(binding); match var_or_rec_binding.tag { Expr::Sym => { let eq_val = eq_val(var_or_rec_binding, expr); @@ -942,7 +944,7 @@ fn reduce(cprocs: &[(&Symbol, usize)]) -> Func { return (expr, env, err, errctrl) } Expr::Cons => { - // No need for `car_cdr` since the expression is already a `Cons` + // No need for `cadr` since the expression is already a `Cons` let (head, rest) = decons2(expr); let rest_is_nil = eq_tag(rest, nil); let rest_is_cons = eq_tag(rest, expr); @@ -959,8 +961,8 @@ fn reduce(cprocs: &[(&Symbol, usize)]) -> Func { let head_is_letrec_sym = eq_val(head, letrec_sym); let head_is_let_or_letrec_sym = or(head_is_let_sym, head_is_letrec_sym); if head_is_let_or_letrec_sym { - let (bindings, body) = car_cdr(rest); - let (body1, rest_body) = car_cdr(body); + let (bindings, body) = cadr(rest); + let (body1, rest_body) = cadr(body); // Only a single body form allowed for now. match body.tag { Expr::Nil => { @@ -974,11 +976,11 @@ fn reduce(cprocs: &[(&Symbol, usize)]) -> Func { return (body1, env, cont, ret) } }; - let (binding1, rest_bindings) = car_cdr(bindings); - let (var, vals) = car_cdr(binding1); + let (binding1, rest_bindings) = cadr(bindings); + let (var, vals) = cadr(binding1); match var.tag { Expr::Sym => { - let (val, end) = car_cdr(vals); + let (val, end) = cadr(vals); match end.tag { Expr::Nil => { let (expanded) = expand_bindings(head, body, body1, rest_bindings); @@ -996,7 +998,7 @@ fn reduce(cprocs: &[(&Symbol, usize)]) -> Func { } match symbol head { "lambda" => { - let (args, body) = car_cdr(rest); + let (args, body) = cadr(rest); let (arg, cdr_args) = extract_arg(args); match arg.tag { @@ -1017,7 +1019,7 @@ fn reduce(cprocs: &[(&Symbol, usize)]) -> Func { return (expr, env, err, errctrl) } "quote" => { - let (quoted, end) = car_cdr(rest); + let (quoted, end) = cadr(rest); match end.tag { Expr::Nil => { @@ -1027,7 +1029,7 @@ fn reduce(cprocs: &[(&Symbol, usize)]) -> Func { return (expr, env, err, errctrl) } "begin" => { - let (arg1, more) = car_cdr(rest); + let (arg1, more) = cadr(rest); match more.tag { Expr::Nil => { return (arg1, env, cont, ret) @@ -1043,7 +1045,7 @@ fn reduce(cprocs: &[(&Symbol, usize)]) -> Func { return (expr, env, err, errctrl) } }; - let (arg1, more) = car_cdr(rest); + let (arg1, more) = cadr(rest); match more.tag { Expr::Nil => { let op: Op1::Eval; @@ -1056,7 +1058,7 @@ fn reduce(cprocs: &[(&Symbol, usize)]) -> Func { return (arg1, env, cont, ret) } "if" => { - let (condition, more) = car_cdr(rest); + let (condition, more) = cadr(rest); match more.tag { Expr::Nil => { return (expr, env, err, errctrl) @@ -1110,7 +1112,7 @@ fn reduce(cprocs: &[(&Symbol, usize)]) -> Func { let (is_cproc) = is_cproc(head); let is_cproc_is_t = eq_val(is_cproc, t); if is_cproc_is_t { - let (arg, unevaled_args) = car_cdr(rest); + let (arg, unevaled_args) = cadr(rest); let cont: Cont::Cproc = cons4(head, unevaled_args, nil, cont); return (arg, env, cont, ret); } @@ -1164,7 +1166,7 @@ fn choose_cproc_call(cprocs: &[(&Symbol, usize)], ivc: bool) -> Func { } fn apply_cont(cprocs: &[(&Symbol, usize)], ivc: bool) -> Func { - let car_cdr = car_cdr(); + let cadr = cadr(); let make_tail_continuation = func!(make_tail_continuation(env, continuation): 1 => { match continuation.tag { Cont::Tail => { @@ -1180,8 +1182,8 @@ fn apply_cont(cprocs: &[(&Symbol, usize)], ivc: bool) -> Func { let nil = Symbol("nil"); let nil = cast(nil, Expr::Nil); let sym: Expr::Sym; - let (binding_or_env, rest) = car_cdr(env); - let (var_or_binding, _val_or_more_bindings) = car_cdr(binding_or_env); + let (binding_or_env, rest) = cadr(env); + let (var_or_binding, _val_or_more_bindings) = cadr(binding_or_env); let binding: Expr::Cons = cons2(var, result); let var_or_binding_is_sym = eq_tag(var_or_binding, sym); let var_or_binding_is_nil = eq_tag(var_or_binding, nil); @@ -1279,7 +1281,7 @@ fn apply_cont(cprocs: &[(&Symbol, usize)], ivc: bool) -> Func { return (result, env, err, errctrl) } }; - let (body_form, end) = car_cdr(body); + let (body_form, end) = cadr(body); match end.tag { Expr::Nil => { let (cont) = make_tail_continuation(saved_env, continuation); @@ -1356,8 +1358,7 @@ fn apply_cont(cprocs: &[(&Symbol, usize)], ivc: bool) -> Func { let (operator, continuation, _foo, _foo) = decons4(cont); match operator.tag { Op1::Car => { - // Almost like car_cdr, except it returns - // an error in case it can't deconstruct it + // `car_cdr` semantics match result.tag { Expr::Nil => { return (nil, env, continuation, makethunk) @@ -1378,6 +1379,7 @@ fn apply_cont(cprocs: &[(&Symbol, usize)], ivc: bool) -> Func { return(result, env, err, errctrl) } Op1::Cdr => { + // `car_cdr` semantics match result.tag { Expr::Nil => { return (nil, env, continuation, makethunk) @@ -1474,7 +1476,7 @@ fn apply_cont(cprocs: &[(&Symbol, usize)], ivc: bool) -> Func { } Cont::Binop => { let (operator, saved_env, unevaled_args, continuation) = decons4(cont); - let (arg2, rest) = car_cdr(unevaled_args); + let (arg2, rest) = cadr(unevaled_args); match operator.tag { Op2::Begin => { match rest.tag { @@ -1687,8 +1689,8 @@ fn apply_cont(cprocs: &[(&Symbol, usize)], ivc: bool) -> Func { } Cont::If => { let (unevaled_args, continuation, _foo, _foo) = decons4(cont); - let (arg1, more) = car_cdr(unevaled_args); - let (arg2, end) = car_cdr(more); + let (arg1, more) = cadr(unevaled_args); + let (arg2, end) = cadr(more); match end.tag { Expr::Nil => { match result.tag { diff --git a/src/lem/pointers.rs b/src/lem/pointers.rs index 7d5a116145..d611225d1e 100644 --- a/src/lem/pointers.rs +++ b/src/lem/pointers.rs @@ -158,14 +158,3 @@ impl ZPtr { Self(Tag::Expr(Nil), F::ZERO) } } - -/// `ZChildren` keeps track of the children of `ZPtr`s, in case they have any. -/// This information is saved during hydration and is needed to content-address -/// a store. -#[derive(Debug, Serialize, Deserialize)] -pub enum ZChildren { - Atom, - Tuple2(ZPtr, ZPtr), - Tuple3(ZPtr, ZPtr, ZPtr), - Tuple4(ZPtr, ZPtr, ZPtr, ZPtr), -} diff --git a/src/lem/store.rs b/src/lem/store.rs index 31979d786c..52c6feb363 100644 --- a/src/lem/store.rs +++ b/src/lem/store.rs @@ -445,10 +445,27 @@ impl Store { } } + /// Interns a sequence of pointers as a cons-list. The terminating element + /// defaults to `nil` if `last` is `None` + fn intern_list(&self, elts: Vec>, last: Option>) -> Ptr { + elts.into_iter() + .rev() + .fold(last.unwrap_or_else(|| self.intern_nil()), |acc, elt| { + self.intern_2_ptrs(Tag::Expr(Cons), elt, acc) + }) + } + + /// Interns a sequence of pointers as a proper (`nil`-terminated) cons-list + #[inline] pub fn list(&self, elts: Vec>) -> Ptr { - elts.into_iter().rev().fold(self.intern_nil(), |acc, elt| { - self.intern_2_ptrs(Tag::Expr(Cons), elt, acc) - }) + self.intern_list(elts, None) + } + + /// Interns a sequence of pointers as an improper cons-list whose last + /// element is `last` + #[inline] + pub fn improper_list(&self, elts: Vec>, last: Ptr) -> Ptr { + self.intern_list(elts, Some(last)) } /// Fetches a cons list that was interned. If the list is improper, the second @@ -541,6 +558,21 @@ impl Store { self.read(State::init_lurk_state().rccell(), input) } + #[inline] + pub fn expect_2_ptrs(&self, idx: usize) -> &(Ptr, Ptr) { + self.fetch_2_ptrs(idx).expect("Index missing from tuple2") + } + + #[inline] + pub fn expect_3_ptrs(&self, idx: usize) -> &(Ptr, Ptr, Ptr) { + self.fetch_3_ptrs(idx).expect("Index missing from tuple3") + } + + #[inline] + pub fn expect_4_ptrs(&self, idx: usize) -> &(Ptr, Ptr, Ptr, Ptr) { + self.fetch_4_ptrs(idx).expect("Index missing from tuple4") + } + /// Recursively hashes the children of a `Ptr` in order to obtain its /// corresponding `ZPtr`. While traversing a `Ptr` tree, it consults the /// cache of `Ptr`s that have already been hydrated and also populates this @@ -556,7 +588,7 @@ impl Store { if let Some(z_ptr) = self.z_cache.get(ptr) { *z_ptr } else { - let (a, b) = self.fetch_2_ptrs(*idx).expect("Index missing from tuple2"); + let (a, b) = self.expect_2_ptrs(*idx); let a = self.hash_ptr_unsafe(a); let b = self.hash_ptr_unsafe(b); let z_ptr = ZPtr::from_parts( @@ -576,7 +608,7 @@ impl Store { if let Some(z_ptr) = self.z_cache.get(ptr) { *z_ptr } else { - let (a, b, c) = self.fetch_3_ptrs(*idx).expect("Index missing from tuple3"); + let (a, b, c) = self.expect_3_ptrs(*idx); let a = self.hash_ptr_unsafe(a); let b = self.hash_ptr_unsafe(b); let c = self.hash_ptr_unsafe(c); @@ -599,7 +631,7 @@ impl Store { if let Some(z_ptr) = self.z_cache.get(ptr) { *z_ptr } else { - let (a, b, c, d) = self.fetch_4_ptrs(*idx).expect("Index missing from tuple4"); + let (a, b, c, d) = self.expect_4_ptrs(*idx); let a = self.hash_ptr_unsafe(a); let b = self.hash_ptr_unsafe(b); let c = self.hash_ptr_unsafe(c); @@ -625,13 +657,17 @@ impl Store { } /// Hashes pointers in parallel, consuming chunks of length 256, which is a - /// reasonably safe limit in terms of memory consumption. + /// reasonably safe limit. The danger of longer chunks is that the rightmost + /// pointers are the ones which are more likely to reach the recursion depth + /// limit in `hash_ptr_unsafe`. So we move in smaller chunks from left to + /// right, populating the `z_cache`, which can rescue `hash_ptr_unsafe` from + /// dangerously deep recursions fn hydrate_z_cache_with_ptrs(&self, ptrs: &[&Ptr]) { - for chunk in ptrs.chunks(256) { + ptrs.chunks(256).for_each(|chunk| { chunk.par_iter().for_each(|ptr| { self.hash_ptr_unsafe(ptr); }); - } + }); } /// Hashes enqueued `Ptr` trees from the bottom to the top, avoiding deep @@ -680,19 +716,19 @@ impl Store { match ptr { Ptr::Atom(..) => (), Ptr::Tuple2(_, idx) => { - let (a, b) = self.fetch_2_ptrs(*idx).expect("Index missing from tuple2"); + let (a, b) = self.expect_2_ptrs(*idx); for ptr in [a, b] { feed_loop!(ptr) } } Ptr::Tuple3(_, idx) => { - let (a, b, c) = self.fetch_3_ptrs(*idx).expect("Index missing from tuple3"); + let (a, b, c) = self.expect_3_ptrs(*idx); for ptr in [a, b, c] { feed_loop!(ptr) } } Ptr::Tuple4(_, idx) => { - let (a, b, c, d) = self.fetch_4_ptrs(*idx).expect("Index missing from tuple4"); + let (a, b, c, d) = self.expect_4_ptrs(*idx); for ptr in [a, b, c, d] { feed_loop!(ptr) } @@ -1096,6 +1132,8 @@ mod tests { let c = Ptr::::char('c'); let b_c = store.cons(b, c); let a_b_c = store.cons(a, b_c); + let a_b_c_ = store.improper_list(vec![a, b], c); + assert_eq!(a_b_c, a_b_c_); assert_eq!(a_b_c.fmt_to_string(&store, state), "('a' 'b' . 'c')"); let (elts, non_nil) = store.fetch_list(&a_b_c).unwrap(); assert_eq!(elts.len(), 2); diff --git a/src/lem/tests/eval_tests.rs b/src/lem/tests/eval_tests.rs index 46e5c2198c..63b0a94cc0 100644 --- a/src/lem/tests/eval_tests.rs +++ b/src/lem/tests/eval_tests.rs @@ -2598,8 +2598,8 @@ fn test_sym_hash_values() { // The tags differ though. use crate::tag::ExprTag::{Key, Sym}; - assert_eq!(Tag::Expr(Sym), sym_z_ptr.tag()); - assert_eq!(Tag::Expr(Key), key_z_ptr.tag()); + assert_eq!(&Tag::Expr(Sym), sym_z_ptr.tag()); + assert_eq!(&Tag::Expr(Key), key_z_ptr.tag()); } #[test] diff --git a/src/store.rs b/src/store.rs index df7b68ca4d..32831dde24 100644 --- a/src/store.rs +++ b/src/store.rs @@ -1518,7 +1518,7 @@ impl Store { Some(ptr) } (tag, None) => { - let ptr = self.intern_maybe_opaque(tag, z_ptr.1); + let ptr = self.intern_maybe_opaque(*tag, z_ptr.1); self.create_z_expr_ptr(ptr, *z_ptr.value()); Some(ptr) } @@ -1545,7 +1545,7 @@ impl Store { Binop, Binop2, Call, Call0, Call2, Dummy, Emit, Error, If, Let, LetRec, Lookup, Outermost, Tail, Terminal, Unop, }; - let tag: ContTag = z_ptr.tag(); + let tag: ContTag = *z_ptr.tag(); if let Some(cont) = z_store.get_cont(z_ptr) { let continuation = match cont { diff --git a/src/z_data/z_ptr.rs b/src/z_data/z_ptr.rs index 7eb4f5588e..aceab9a997 100644 --- a/src/z_data/z_ptr.rs +++ b/src/z_data/z_ptr.rs @@ -86,8 +86,8 @@ impl ZPtr { } /// Returns the tag - pub fn tag(&self) -> E { - self.0 + pub fn tag(&self) -> &E { + &self.0 } /// Returns the tag in field representation