From bf8651db29b532fe59e78185865aee2b562352b2 Mon Sep 17 00:00:00 2001 From: Skgland Date: Tue, 18 Nov 2025 21:27:23 +0100 Subject: [PATCH 01/14] remove two unecessary clones --- src/machine/lib_machine/mod.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/machine/lib_machine/mod.rs b/src/machine/lib_machine/mod.rs index fc036940..653fc2a4 100644 --- a/src/machine/lib_machine/mod.rs +++ b/src/machine/lib_machine/mod.rs @@ -128,9 +128,11 @@ impl Term { pub fn try_conjunction(value: impl IntoIterator) -> Option { let mut iter = value.into_iter(); iter.next().map(|first| { - Term::try_conjunction(iter) - .map(|rest| Term::compound(",", [first.clone(), rest])) - .unwrap_or(first) + if let Some(rest) = Term::try_conjunction(iter) { + Term::compound(",", [first, rest]) + } else { + first + } }) } @@ -143,9 +145,11 @@ impl Term { pub fn try_disjunction(value: impl IntoIterator) -> Option { let mut iter = value.into_iter(); iter.next().map(|first| { - Term::try_disjunction(iter) - .map(|rest| Term::compound(";", [first.clone(), rest])) - .unwrap_or(first) + if let Some(rest) = Term::try_disjunction(iter) { + Term::compound(";", [first, rest]) + } else { + first + } }) } } From 119761ad10e03042e88321a3d820eeb89bcf336b Mon Sep 17 00:00:00 2001 From: Skgland Date: Tue, 18 Nov 2025 21:42:00 +0100 Subject: [PATCH 02/14] ignore unused_parens warning parens are generated by external macro --- src/arena.rs | 1 + src/atom_table.rs | 1 + src/heap_iter.rs | 1 + src/machine/copier.rs | 2 ++ src/machine/machine_indices.rs | 1 + src/machine/streams.rs | 2 ++ src/parser/ast.rs | 1 + src/types.rs | 1 + 8 files changed, 10 insertions(+) diff --git a/src/arena.rs b/src/arena.rs index 11db7581..09c52728 100644 --- a/src/arena.rs +++ b/src/arena.rs @@ -1,4 +1,5 @@ #![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work +#![allow(unused_parens)] // see mthom/scryer-prolog#3092 and rust-lang/rust#147126 #[cfg(feature = "http")] use crate::http::{HttpListener, HttpResponse}; diff --git a/src/atom_table.rs b/src/atom_table.rs index d7796d73..cb4e5230 100644 --- a/src/atom_table.rs +++ b/src/atom_table.rs @@ -1,4 +1,5 @@ #![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work +#![allow(unused_parens)] // see mthom/scryer-prolog#3092 and rust-lang/rust#147126 use crate::parser::ast::MAX_ARITY; use crate::raw_block::*; diff --git a/src/heap_iter.rs b/src/heap_iter.rs index ceefe4a9..70314082 100644 --- a/src/heap_iter.rs +++ b/src/heap_iter.rs @@ -1,4 +1,5 @@ #![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work +#![allow(unused_parens)] // see mthom/scryer-prolog#3092 and rust-lang/rust#147126 #[cfg(test)] pub(crate) use crate::machine::gc::StacklessPreOrderHeapIter; diff --git a/src/machine/copier.rs b/src/machine/copier.rs index 110371bc..965902df 100644 --- a/src/machine/copier.rs +++ b/src/machine/copier.rs @@ -1,3 +1,5 @@ +#![allow(unused_parens)] // see mthom/scryer-prolog#3092 and rust-lang/rust#147126 + use fxhash::FxBuildHasher; use indexmap::IndexSet; diff --git a/src/machine/machine_indices.rs b/src/machine/machine_indices.rs index 7f7bfe35..a2cb5050 100644 --- a/src/machine/machine_indices.rs +++ b/src/machine/machine_indices.rs @@ -1,4 +1,5 @@ #![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work +#![allow(unused_parens)] // see mthom/scryer-prolog#3092 and rust-lang/rust#147126 use crate::parser::ast::*; diff --git a/src/machine/streams.rs b/src/machine/streams.rs index c0f5c7b3..0d88ca58 100644 --- a/src/machine/streams.rs +++ b/src/machine/streams.rs @@ -1,3 +1,5 @@ +#![allow(unused_parens)] // see mthom/scryer-prolog#3092 and rust-lang/rust#147126 + use crate::arena::*; use crate::atom_table::*; use crate::functor_macro::*; diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 9effb51e..3728da75 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -1,4 +1,5 @@ #![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work +#![allow(unused_parens)] // see mthom/scryer-prolog#3092 and rust-lang/rust#147126 use crate::arena::*; use crate::atom_table::*; diff --git a/src/types.rs b/src/types.rs index 97df4bc4..4549c3ea 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,4 +1,5 @@ #![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work +#![allow(unused_parens)] // see mthom/scryer-prolog#3092 and rust-lang/rust#147126 use crate::arena::*; use crate::atom_table::*; From a0b4162d7a6682f839d625695575e9ed069079b7 Mon Sep 17 00:00:00 2001 From: Skgland Date: Tue, 18 Nov 2025 21:42:10 +0100 Subject: [PATCH 03/14] improve count_to_letter_code - reserve the complete required length at the beginning to reduce reallocations - use u8 instead of char so that we can re-use the allocation for the string --- src/machine/lib_machine/mod.rs | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/machine/lib_machine/mod.rs b/src/machine/lib_machine/mod.rs index 653fc2a4..7527c05c 100644 --- a/src/machine/lib_machine/mod.rs +++ b/src/machine/lib_machine/mod.rs @@ -159,9 +159,14 @@ impl Term { fn count_to_letter_code(mut count: usize) -> String { let mut letters = Vec::new(); + // +2 rather than +1 to account for the _ at the end + let length = count.checked_ilog(26).unwrap_or(0) as usize + 2; + + letters.reserve(length); + loop { - let letter_idx = (count % 26) as u32; - letters.push(char::from_u32('A' as u32 + letter_idx).unwrap()); + let letter_idx = (count % 26) as u8; + letters.push(b'A' + letter_idx); count /= 26; if count == 0 { @@ -169,7 +174,15 @@ fn count_to_letter_code(mut count: usize) -> String { } } - letters.into_iter().chain("_".chars()).rev().collect() + letters.push(b'_'); + + debug_assert_eq!(length, letters.len()); + + letters.reverse(); + + // Safety: we only push ascii chars A-Z and _ + // an ascii only byte sequence is always valid utf-8 + unsafe { String::from_utf8_unchecked(letters) } } impl Term { @@ -623,3 +636,11 @@ impl Machine { } } } + +#[test] +fn test_count_to_letter_code() { + for idx in 0..1000 { + // ensure the debug assert doesn't trigger + count_to_letter_code(idx); + } +} From 6b5ec3fab86543b0e99c8fadd89457d81d06739f Mon Sep 17 00:00:00 2001 From: Skgland Date: Tue, 18 Nov 2025 21:54:02 +0100 Subject: [PATCH 04/14] ignore incompatible msrv in compat module --- src/machine/streams/compat.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/machine/streams/compat.rs b/src/machine/streams/compat.rs index bb040ca5..45e370fa 100644 --- a/src/machine/streams/compat.rs +++ b/src/machine/streams/compat.rs @@ -65,6 +65,8 @@ mod lt_1_87_0 { #[cfg(rust_version = "1.87.0")] mod ge_1_87_0 { + #![allow(clippy::incompatible_msrv)] + pub type PipeReader = std::io::PipeReader; pub type PipeWriter = std::io::PipeWriter; } From a283c8bdc2606dc4d866b282cdd64f75c6d74e0b Mon Sep 17 00:00:00 2001 From: Skgland Date: Tue, 18 Nov 2025 21:59:55 +0100 Subject: [PATCH 05/14] remove some uncessary allocations/copies --- src/indexing.rs | 10 +++------- src/machine/args.rs | 4 +--- src/machine/lib_machine/mod.rs | 6 +++--- src/machine/loader.rs | 4 +--- 4 files changed, 8 insertions(+), 16 deletions(-) diff --git a/src/indexing.rs b/src/indexing.rs index 345533ad..a1105fdf 100644 --- a/src/indexing.rs +++ b/src/indexing.rs @@ -1328,9 +1328,7 @@ impl Indexer for DynamicCodeIndices { for (key, code) in indices.into_iter() { if code.len() > 1 { index_locs.insert(key, IndexingCodePtr::Internal(prelude.len() + 1)); - prelude.push_back(IndexingLine::DynamicIndexedChoice( - code.into_iter().collect(), - )); + prelude.push_back(IndexingLine::DynamicIndexedChoice(code)); } else if let Some(i) = code.front() { index_locs.insert(key, IndexingCodePtr::DynamicExternal(*i)); } @@ -1369,9 +1367,7 @@ impl Indexer for DynamicCodeIndices { ) -> IndexingCodePtr { if lists.len() > 1 { let lists = std::mem::take(lists); - prelude.push_back(IndexingLine::DynamicIndexedChoice( - lists.into_iter().collect(), - )); + prelude.push_back(IndexingLine::DynamicIndexedChoice(lists)); IndexingCodePtr::Internal(1) } else { lists @@ -1548,6 +1544,6 @@ impl CodeOffsets { str_loc, ))); - prelude.into_iter().collect() + prelude.into() } } diff --git a/src/machine/args.rs b/src/machine/args.rs index 6e24c87a..02a9d332 100644 --- a/src/machine/args.rs +++ b/src/machine/args.rs @@ -1,4 +1,3 @@ -use std::collections::BTreeSet; use std::env; #[derive(Debug)] @@ -8,9 +7,8 @@ pub struct MachineArgs { impl MachineArgs { pub fn new() -> Self { - let args: BTreeSet = env::args().collect(); Self { - add_history: !args.contains("--no-add-history"), + add_history: env::args().all(|arg| arg != "--no-add-history"), } } } diff --git a/src/machine/lib_machine/mod.rs b/src/machine/lib_machine/mod.rs index 7527c05c..810c607d 100644 --- a/src/machine/lib_machine/mod.rs +++ b/src/machine/lib_machine/mod.rs @@ -224,14 +224,14 @@ impl Term { let list = match tail { Term::Atom(atom) if atom == "[]" => match head { - Term::Atom(ref a) if a.chars().collect::>().len() == 1 => { + Term::Atom(ref a) if a.chars().count() == 1 => { // Handle lists of char as strings Term::String(a.to_string()) } _ => Term::List(vec![head]), }, Term::List(elems) if elems.is_empty() => match head { - Term::Atom(ref a) if a.chars().collect::>().len() == 1 => { + Term::Atom(ref a) if a.chars().count() == 1 => { // Handle lists of char as strings Term::String(a.to_string()) }, @@ -242,7 +242,7 @@ impl Term { Term::List(elems) }, Term::String(mut elems) => match head { - Term::Atom(ref a) if a.chars().collect::>().len() == 1 => { + Term::Atom(ref a) if a.chars().count() == 1 => { // Handle lists of char as strings elems.insert(0, a.chars().next().unwrap()); Term::String(elems) diff --git a/src/machine/loader.rs b/src/machine/loader.rs index 763fa447..fded231b 100644 --- a/src/machine/loader.rs +++ b/src/machine/loader.rs @@ -1842,9 +1842,7 @@ impl Machine { let err = self.machine_st.permission_error( Permission::Modify, atom!("static_procedure"), - functor_stub(atom!(":"), 2) - .into_iter() - .collect::(), + functor_stub(atom!(":"), 2), ); self.machine_st From 248b05c992286bb14d9a65eb7bbaca574a543075 Mon Sep 17 00:00:00 2001 From: Skgland Date: Tue, 18 Nov 2025 22:01:42 +0100 Subject: [PATCH 06/14] use as_bytes().to_vec() instead of bytes().collect() copying a slice into a vec should be easier to optimize by the complier than collecting a byte iteration into a vec --- src/machine/system_calls.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 5fc4088b..4e1797a9 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -3303,7 +3303,7 @@ impl Machine { bytes.push(c as u8); } } else { - bytes = string.as_str().bytes().collect(); + bytes = string.as_str().as_bytes().to_vec(); } match stream.write_all(&bytes) { @@ -4420,7 +4420,7 @@ impl Machine { let address_data = self.deref_register(5); let mut bytes: Vec = Vec::new(); if let Some(string) = self.machine_st.value_to_str_like(address_data) { - bytes = string.as_str().bytes().collect(); + bytes = string.as_str().as_bytes().to_vec(); } let stub_gen = || functor_stub(atom!("http_open"), 3); @@ -9309,7 +9309,7 @@ impl Machine { let data = self.machine_st.value_to_str_like(data_arg).unwrap(); match encoding { - atom!("utf8") => data.as_str().bytes().collect(), + atom!("utf8") => data.as_str().as_bytes().to_vec(), atom!("octet") => data.as_str().chars().map(|c| c as u8).collect(), _ => { unreachable!() From 5686b69013b9478e0b7cb9ca706dc0d43cdf9016 Mon Sep 17 00:00:00 2001 From: Skgland Date: Tue, 18 Nov 2025 22:13:18 +0100 Subject: [PATCH 07/14] stub a clippy config with things we need to get rid of replace if we want to eliminate panics due to oom --- clippy.toml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 clippy.toml diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 00000000..523192fe --- /dev/null +++ b/clippy.toml @@ -0,0 +1,26 @@ +disallowed-macros = [ + # https://rust-lang.github.io/rust-clippy/master/#disallowed_macros + + # list of macros that may panic on allocation failure e.g. + + # "std::vec", +] + +disallowed-methods = [ + # https://rust-lang.github.io/rust-clippy/master/#disallowed_method + + # list of methods that may panic on allocation failue + # though not including things that can be used correctly by reversing ahead of time (i.e. std::vec::Vec::try_reserve + std::iter::Extend::extend ). + + # "std::iter::Iter::collect", + # { path = "std::vec::Vec::with_capacity", replacement = "std::vec::Vec::new + std::vec::Vec::try_reserve" }, + # { path = "std::string::String::with_capacity", replacement = "std::string::String::new + std::string::String::try_reserve" }, +] + +disallowed-types = [ + # https://rust-lang.github.io/rust-clippy/master/#disallowed_types + + # list of types that can't be used without risking a panic due to allocation failure + + # { path = "std::collections::BTreeMap", reason = "unlike Vec and HashMap BTreeMap cannot reserve capacity ahead of time (i.e. try_reserve) making it unusable without risk of oom panic"}, +] From e9e97fe82a059f4f22133aa638b71509b35a93bb Mon Sep 17 00:00:00 2001 From: Skgland Date: Wed, 19 Nov 2025 22:08:42 +0100 Subject: [PATCH 08/14] handle machine heap/stack allocation error --- src/arena.rs | 11 +++--- src/atom_table.rs | 16 ++++++--- src/functor_macro.rs | 2 +- src/machine/attributed_variables.rs | 14 +++++--- src/machine/copier.rs | 30 +++++++++------- src/machine/dispatch.rs | 27 +++++++++++---- src/machine/heap.rs | 45 ++++++++++++++++-------- src/machine/lib_machine/mod.rs | 15 +++++--- src/machine/loader.rs | 4 +-- src/machine/machine_errors.rs | 9 ++--- src/machine/machine_state.rs | 17 ++++----- src/machine/machine_state_impl.rs | 15 ++++---- src/machine/mock_wam.rs | 6 ++-- src/machine/mod.rs | 17 ++++++--- src/machine/stack.rs | 54 ++++++++++++++--------------- src/machine/system_calls.rs | 50 ++++++++++++++++---------- src/macros.rs | 8 ++--- src/offset_table.rs | 25 +++++++------ src/raw_block.rs | 44 ++++++++++------------- src/read.rs | 6 ++-- 20 files changed, 245 insertions(+), 170 deletions(-) diff --git a/src/arena.rs b/src/arena.rs index 09c52728..c27ed5f4 100644 --- a/src/arena.rs +++ b/src/arena.rs @@ -3,6 +3,7 @@ #[cfg(feature = "http")] use crate::http::{HttpListener, HttpResponse}; +use crate::machine::heap::AllocError; use crate::machine::loader::LiveLoadState; use crate::machine::streams::*; use crate::offset_table::*; @@ -486,12 +487,12 @@ unsafe impl Sync for Arena {} #[allow(clippy::new_without_default)] impl Arena { #[inline] - pub fn new() -> Self { - Arena { + pub fn new() -> Result { + Ok(Arena { base: None, - f64_tbl: F64Table::new(), - code_index_tbl: CodeIndexTable::new(), - } + f64_tbl: F64Table::new()?, + code_index_tbl: CodeIndexTable::new()?, + }) } } diff --git a/src/atom_table.rs b/src/atom_table.rs index cb4e5230..d9e970db 100644 --- a/src/atom_table.rs +++ b/src/atom_table.rs @@ -1,6 +1,7 @@ #![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work #![allow(unused_parens)] // see mthom/scryer-prolog#3092 and rust-lang/rust#147126 +use crate::machine::heap::AllocError; use crate::parser::ast::MAX_ARITY; use crate::raw_block::*; use crate::types::*; @@ -437,21 +438,21 @@ impl InnerAtomTable { impl AtomTable { #[inline] - pub fn new() -> Arc { + pub fn new() -> Result, AllocError> { let upgraded = global_atom_table().read().unwrap().upgrade(); // don't inline upgraded, otherwise temporary will be dropped too late in case of None if let Some(atom_table) = upgraded { - atom_table + Ok(atom_table) } else { let mut guard = global_atom_table().write().unwrap(); // try to upgrade again in case we lost the race on the write lock if let Some(atom_table) = guard.upgrade() { - atom_table + Ok(atom_table) } else { let atom_table = Arc::new(Self { inner: Arcu::new( InnerAtomTable { - block: RawBlock::new(), + block: RawBlock::new()?, table: Arcu::new(IndexSet::new(), GlobalEpochCounterPool), }, GlobalEpochCounterPool, @@ -459,11 +460,16 @@ impl AtomTable { update: Mutex::new(()), }); *guard = Arc::downgrade(&atom_table); - atom_table + Ok(atom_table) } } } + #[inline] + pub fn retrieve() -> Arc { + global_atom_table().read().unwrap().upgrade().unwrap() + } + pub fn active_table(&self) -> RcuRef, IndexSet> { self.inner.read().table.read() } diff --git a/src/functor_macro.rs b/src/functor_macro.rs index 88e5a5a2..82d6d98e 100644 --- a/src/functor_macro.rs +++ b/src/functor_macro.rs @@ -551,7 +551,7 @@ mod tests { #[test] fn inlined_atoms() { - let atom_table = AtomTable::new(); + let atom_table = AtomTable::new().unwrap(); let inlined = AtomTable::build_with(&atom_table, "inline"); assert!(inlined.is_inlined()); diff --git a/src/machine/attributed_variables.rs b/src/machine/attributed_variables.rs index 4aaf45ee..f1b5ba6c 100644 --- a/src/machine/attributed_variables.rs +++ b/src/machine/attributed_variables.rs @@ -54,7 +54,9 @@ impl MachineState { self.attr_var_init.bindings.push((h, addr)); } - fn populate_var_and_value_lists(&mut self) -> Result<(HeapCellValue, HeapCellValue), usize> { + fn populate_var_and_value_lists( + &mut self, + ) -> Result<(HeapCellValue, HeapCellValue), AllocError> { let size = self.attr_var_init.bindings.len(); let iter = self @@ -70,7 +72,7 @@ impl MachineState { Ok((var_list_addr, value_list_addr)) } - fn verify_attributes(&mut self) -> Result<(), usize> { + fn verify_attributes(&mut self) -> Result<(), AllocError> { for (h, _) in &self.attr_var_init.bindings { self.heap[*h] = attr_var_as_cell!(*h); } @@ -110,8 +112,12 @@ impl MachineState { attr_vars } - pub(super) fn verify_attr_interrupt(&mut self, p: usize, arity: usize) -> Result<(), usize> { - self.allocate(arity + 3); + pub(super) fn verify_attr_interrupt( + &mut self, + p: usize, + arity: usize, + ) -> Result<(), AllocError> { + self.allocate(arity + 3)?; let e = self.e; let and_frame = self.stack.index_and_frame_mut(e); diff --git a/src/machine/copier.rs b/src/machine/copier.rs index 965902df..42359806 100644 --- a/src/machine/copier.rs +++ b/src/machine/copier.rs @@ -83,16 +83,16 @@ pub trait CopierTarget: IndexMut { fn threshold(&self) -> usize; // returns the tail location of the pstr on success fn as_slice_from<'a>(&'a self, from: usize) -> Box + 'a>; - fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result; - fn reserve(&mut self, num_cells: usize) -> Result, usize>; - fn copy_slice_to_end(&mut self, bounds: Range) -> Result<(), usize>; + fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result; + fn reserve(&mut self, num_cells: usize) -> Result, AllocError>; + fn copy_slice_to_end(&mut self, bounds: Range) -> Result<(), AllocError>; } pub(crate) fn copy_term( target: T, addr: HeapCellValue, attr_var_policy: AttrVarPolicy, -) -> Result { +) -> Result { let mut copy_term_state = CopyTermState::new(target, attr_var_policy); let old_threshold = copy_term_state.target.threshold(); @@ -149,7 +149,7 @@ impl CopyTermState { self.trail.push((TrailRef::heap_cell(addr), trail_item)); } - fn copy_list(&mut self, addr: usize) -> Result<(), usize> { + fn copy_list(&mut self, addr: usize) -> Result<(), AllocError> { for offset in 0..2 { read_heap_cell!(self.target[addr + offset], (HeapCellValueTag::Lis, h) => { @@ -194,7 +194,7 @@ impl CopyTermState { Ok(()) } - fn copy_partial_string(&mut self, pstr_loc: usize) -> Result<(), usize> { + fn copy_partial_string(&mut self, pstr_loc: usize) -> Result<(), AllocError> { match self.pstr_loc_locs.range_mut(..=pstr_loc).next_back() { Some(( _prev_pstr_loc, @@ -281,7 +281,7 @@ impl CopyTermState { Ok(()) } - fn copy_attr_var_lists(&mut self) -> Result<(), usize> { + fn copy_attr_var_lists(&mut self) -> Result<(), AllocError> { while !self.attr_var_list_locs.is_empty() { let mut list_loc_vec = std::mem::take(&mut self.attr_var_list_locs); @@ -300,7 +300,7 @@ impl CopyTermState { * structure which is ensured by this function and not at all by * the vanilla copier. */ - fn copy_attr_var_list(&mut self, mut list_addr: HeapCellValue) -> Result<(), usize> { + fn copy_attr_var_list(&mut self, mut list_addr: HeapCellValue) -> Result<(), AllocError> { while let HeapCellValueTag::Lis = list_addr.get_tag() { let threshold = self.target.threshold(); let heap_loc = list_addr.get_value() as usize; @@ -330,7 +330,11 @@ impl CopyTermState { Ok(()) } - fn reinstantiate_var(&mut self, addr: HeapCellValue, frontier: usize) -> Result<(), usize> { + fn reinstantiate_var( + &mut self, + addr: HeapCellValue, + frontier: usize, + ) -> Result<(), AllocError> { read_heap_cell!(addr, (HeapCellValueTag::Var, h) => { self.target[frontier] = heap_loc_as_cell!(frontier); @@ -381,7 +385,7 @@ impl CopyTermState { Ok(()) } - fn copy_var(&mut self, addr: HeapCellValue) -> Result<(), usize> { + fn copy_var(&mut self, addr: HeapCellValue) -> Result<(), AllocError> { let index = addr.get_value() as usize; let rd = self.target.deref(addr); let ra = self.target.store(rd); @@ -421,7 +425,7 @@ impl CopyTermState { Ok(()) } - fn copy_structure(&mut self, addr: usize) -> Result<(), usize> { + fn copy_structure(&mut self, addr: usize) -> Result<(), AllocError> { read_heap_cell!(self.target[addr], (HeapCellValueTag::Atom, (_name, arity)) => { let threshold = self.target.threshold(); @@ -464,7 +468,7 @@ impl CopyTermState { Ok(()) } - fn copy_term_impl(&mut self, addr: HeapCellValue) -> Result<(), usize> { + fn copy_term_impl(&mut self, addr: HeapCellValue) -> Result<(), AllocError> { self.scan = self.target.threshold(); let mut writer = self.target.reserve(1)?; @@ -503,7 +507,7 @@ impl CopyTermState { Ok(()) } - fn copy_pstrs(&mut self) -> Result<(), usize> { + fn copy_pstrs(&mut self) -> Result<(), AllocError> { while let Some((least_pstr_loc, pstr_data)) = self.pstr_loc_locs.pop_first() { let threshold = heap_index!(self.target.threshold()); diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index fc13d825..d593bedf 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -1051,7 +1051,10 @@ impl Machine { }); self.machine_st.num_of_args += 1; - self.try_me_else(next_i); + backtrack_on_resource_error!( + self.machine_st, + self.try_me_else(next_i) + ); self.machine_st.num_of_args -= 1; } None => { @@ -1130,7 +1133,10 @@ impl Machine { ); self.machine_st.num_of_args += 1; - self.try_me_else(next_i); + backtrack_on_resource_error!( + self.machine_st, + self.try_me_else(next_i) + ); self.machine_st.num_of_args -= 1; } None => { @@ -1183,7 +1189,7 @@ impl Machine { } } &Instruction::TryMeElse(offset) => { - self.try_me_else(offset); + backtrack_on_resource_error!(self.machine_st, self.try_me_else(offset)); } &Instruction::DefaultRetryMeElse(offset) => { self.retry_me_else(offset); @@ -1265,7 +1271,10 @@ impl Machine { self.machine_st.p += 1; } &Instruction::Allocate(num_cells) => { - self.machine_st.allocate(num_cells); + backtrack_on_resource_error!( + self.machine_st, + self.machine_st.allocate(num_cells) + ); } &Instruction::DefaultCallAcyclicTerm => { let addr = self.deref_register(1); @@ -3155,7 +3164,10 @@ impl Machine { IndexingLine::IndexedChoice(ref indexed_choice) => { match indexed_choice[self.machine_st.iip as usize] { IndexedChoiceInstruction::Try(offset) => { - self.indexed_try(offset); + backtrack_on_resource_error!( + self.machine_st, + self.indexed_try(offset) + ); } IndexedChoiceInstruction::Retry(l) => { self.retry(l); @@ -3208,7 +3220,10 @@ impl Machine { ); self.machine_st.num_of_args += 1; - self.indexed_try(offset); + backtrack_on_resource_error!( + self.machine_st, + self.indexed_try(offset) + ); self.machine_st.num_of_args -= 1; } None => { diff --git a/src/machine/heap.rs b/src/machine/heap.rs index c6ca9871..964d983e 100644 --- a/src/machine/heap.rs +++ b/src/machine/heap.rs @@ -1,5 +1,6 @@ use crate::atom_table::*; use crate::functor_macro::*; +use crate::machine::machine_errors::CompilationError; use crate::machine::{ArenaHeaderTag, Fixnum, Integer}; use crate::types::*; @@ -11,6 +12,19 @@ use std::sync::Once; const ALIGN: usize = Heap::heap_cell_alignment(); +#[derive(Debug, Clone)] +pub struct AllocError; + +impl AllocError { + pub(crate) fn to_compilation_error(&self, heap: &mut Heap) -> CompilationError { + CompilationError::FiniteMemoryInHeap(self.resource_error_offset(heap)) + } + + pub(crate) fn resource_error_offset(&self, heap: &mut Heap) -> usize { + heap.resource_error_offset() + } +} + #[derive(Debug)] pub struct Heap { inner: InnerHeap, @@ -612,7 +626,7 @@ impl Heap { } } - pub fn reserve(&mut self, num_cells: usize) -> Result, usize> { + pub fn reserve(&mut self, num_cells: usize) -> Result, AllocError> { let section; let len = heap_index!(num_cells); @@ -625,7 +639,7 @@ impl Heap { }; break; } else if !self.grow() { - return Err(self.resource_error_offset()); + return Err(AllocError); } } } @@ -649,7 +663,7 @@ impl Heap { } } - pub(crate) fn append(&mut self, other_heap: &impl SizedHeap) -> Result<(), usize> { + pub(crate) fn append(&mut self, other_heap: &impl SizedHeap) -> Result<(), AllocError> { let other_len = heap_index!(other_heap.cell_len()); loop { @@ -665,7 +679,7 @@ impl Heap { self.inner.byte_len += heap_index!(other_heap.cell_len()); break; } else if unsafe { !self.grow() } { - return Err(self.resource_error_offset()); + return Err(AllocError); } } @@ -742,10 +756,10 @@ impl Heap { // either succeed & return nothing or fail & return an offset into // the heap to a pre-allocated resource error - pub(crate) fn push_cell(&mut self, cell: HeapCellValue) -> Result<(), usize> { + pub(crate) fn push_cell(&mut self, cell: HeapCellValue) -> Result<(), AllocError> { unsafe { if self.inner.byte_len == self.inner.byte_cap && !self.grow() { - return Err(self.resource_error_offset()); + return Err(AllocError); } // SAFETY: @@ -779,7 +793,7 @@ impl Heap { Range { start, end } } - pub fn allocate_pstr(&mut self, src: &str) -> Result { + pub fn allocate_pstr(&mut self, src: &str) -> Result { let size_in_heap = Self::compute_pstr_size(src); let mut writer = self.reserve(size_in_heap)?; let HeapSectionWriteResult { result, .. } = @@ -794,7 +808,7 @@ impl Heap { // note that allocate_cstr emits a tail cell to the string (completing it with the empty list) // unlike any version of allocate_pstr. - pub fn allocate_cstr(&mut self, src: &str) -> Result { + pub fn allocate_cstr(&mut self, src: &str) -> Result { let size_in_heap = Self::compute_pstr_size(src); let mut writer = self.reserve(size_in_heap + 1)?; let HeapSectionWriteResult { result, .. } = @@ -849,7 +863,7 @@ impl Heap { // copies only the string, not its tail. returns the cell index of // the tail location - pub(crate) fn copy_pstr_within(&mut self, pstr_loc: usize) -> Result { + pub(crate) fn copy_pstr_within(&mut self, pstr_loc: usize) -> Result { let HeapStringScan { string, tail_idx } = self.scan_slice_to_str(pstr_loc); let s_len = string.len(); @@ -884,7 +898,7 @@ impl Heap { break; } else if !self.grow() { - return Err(self.resource_error_offset()); + return Err(AllocError); } } } @@ -893,7 +907,10 @@ impl Heap { } // src is a cell-indexed range. - pub(crate) fn copy_slice_to_end>(&mut self, src: R) -> Result<(), usize> { + pub(crate) fn copy_slice_to_end>( + &mut self, + src: R, + ) -> Result<(), AllocError> { let range = self.slice_range(src); let len = range.end - range.start; @@ -911,7 +928,7 @@ impl Heap { break; } else if !self.grow() { - return Err(self.resource_error_offset()); + return Err(AllocError); } } } @@ -970,7 +987,7 @@ impl Heap { pub(crate) fn functor_writer( functor: Vec, - ) -> impl FnMut(&mut Heap) -> Result { + ) -> impl FnMut(&mut Heap) -> Result { let size = Heap::compute_functor_byte_size(&functor); let mut functor_writer = ReservedHeapSection::functor_writer(functor); @@ -1133,7 +1150,7 @@ pub fn sized_iter_to_heap_list>( heap: &mut Heap, size: usize, values: impl Iterator, -) -> Result { +) -> Result { if size > 0 { let h = heap.cell_len(); let mut writer = heap.reserve(1 + 2 * size)?; diff --git a/src/machine/lib_machine/mod.rs b/src/machine/lib_machine/mod.rs index 810c607d..b4409aa5 100644 --- a/src/machine/lib_machine/mod.rs +++ b/src/machine/lib_machine/mod.rs @@ -4,6 +4,7 @@ use std::rc::Rc; use crate::atom_table; use crate::heap_iter::{stackful_post_order_iter, NonListElider}; +use crate::machine::heap::AllocError; use crate::machine::machine_indices::VarKey; use crate::machine::mock_wam::CompositeOpDir; use crate::machine::{ @@ -445,14 +446,15 @@ impl Iterator for QueryState<'_> { // contained in self.machine_st.ball. let h = machine.machine_st.heap.cell_len(); - if let Err(resource_err_loc) = machine + if let Err(err) = machine .machine_st .heap .append(&machine.machine_st.ball.stub) { + let resource_error_offset = err.resource_error_offset(&mut machine.machine_st.heap); return Some(Err(Term::from_heapcell( machine, - machine.machine_st.heap[resource_err_loc], + machine.machine_st.heap[resource_error_offset], &mut IndexMap::new(), ))); } @@ -553,11 +555,11 @@ impl Machine { self.run_module_predicate(atom!("loader"), (atom!("consult_stream"), 2)); } - pub(crate) fn allocate_stub_choice_point(&mut self) { + pub(crate) fn allocate_stub_choice_point(&mut self) -> Result<(), AllocError> { // NOTE: create a choice point to terminate the dispatch_loop // if an exception is thrown. - let stub_b = self.machine_st.stack.allocate_or_frame(0); + let stub_b = self.machine_st.stack.allocate_or_frame(0)?; let or_frame = self.machine_st.stack.index_or_frame_mut(stub_b); or_frame.prelude.num_cells = 0; @@ -575,6 +577,8 @@ impl Machine { self.machine_st.b = stub_b; self.machine_st.hb = self.machine_st.heap.cell_len(); self.machine_st.block = stub_b; + + Ok(()) } /// Runs a query. @@ -588,7 +592,8 @@ impl Machine { .read_term(&op_dir, Tokens::Default) .expect("Failed to parse query"); - self.allocate_stub_choice_point(); + self.allocate_stub_choice_point() + .expect("failed to allocate stub choice point"); // Write parsed term to heap let term_write_result = write_term_to_heap(&term, &mut self.machine_st.heap) diff --git a/src/machine/loader.rs b/src/machine/loader.rs index fded231b..1ad46c88 100644 --- a/src/machine/loader.rs +++ b/src/machine/loader.rs @@ -2359,8 +2359,8 @@ impl Machine { let mut writer = match self.machine_st.heap.reserve(3 + meta_specs.len()) { Ok(writer) => writer, - Err(err_loc) => { - self.machine_st.throw_resource_error(err_loc); + Err(err) => { + self.machine_st.throw_resource_error(err); return; } }; diff --git a/src/machine/machine_errors.rs b/src/machine/machine_errors.rs index 9b56b4fb..4df767bf 100644 --- a/src/machine/machine_errors.rs +++ b/src/machine/machine_errors.rs @@ -6,6 +6,7 @@ use crate::parser::ast::*; use crate::ffi::{self, FfiError}; use crate::forms::*; use crate::functor_macro::*; +use crate::machine::heap::AllocError; use crate::machine::heap::*; use crate::machine::loader::CompilationTarget; use crate::machine::machine_state::*; @@ -763,8 +764,8 @@ impl MachineState { } // throw an error pre-allocated in the heap - pub(super) fn throw_resource_error(&mut self, err_loc: usize) { - self.registers[1] = str_loc_as_cell!(err_loc); + pub(super) fn throw_resource_error(&mut self, err: AllocError) { + self.registers[1] = str_loc_as_cell!(err.resource_error_offset(&mut self.heap)); self.set_ball(); self.unwind_stack(); } @@ -777,8 +778,8 @@ impl MachineState { self.registers[1] = match writer(&mut self.heap) { Ok(loc) => loc, - Err(resource_err_loc) => { - self.throw_resource_error(resource_err_loc); + Err(err) => { + self.throw_resource_error(err); return; } }; diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index ca88a6ea..b915e3a1 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -5,6 +5,7 @@ use crate::heap_iter::*; use crate::heap_print::*; use crate::machine::attributed_variables::*; use crate::machine::copier::*; +use crate::machine::heap::AllocError; use crate::machine::heap::*; use crate::machine::machine_errors::*; use crate::machine::machine_indices::*; @@ -192,7 +193,7 @@ fn push_var_eq_functors<'a>( size: usize, iter: impl Iterator, atom_tbl: &AtomTable, -) -> Result { +) -> Result { let src_h = heap.cell_len(); let true_size = if size > 0 { @@ -257,7 +258,7 @@ impl Ball { self.stub.clear(); } - pub(super) fn copy_and_align_to(&self, dest: &mut Heap) -> Result { + pub(super) fn copy_and_align_to(&self, dest: &mut Heap) -> Result { let h = dest.cell_len(); let diff = self.boundary as i64 - h as i64; @@ -346,17 +347,17 @@ impl<'a> CopierTarget for CopyTerm<'a> { } #[inline(always)] - fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result { + fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result { self.state.heap.copy_pstr_within(pstr_loc) } #[inline(always)] - fn reserve(&mut self, num_cells: usize) -> Result, usize> { + fn reserve(&mut self, num_cells: usize) -> Result, AllocError> { self.state.heap.reserve(num_cells) } #[inline(always)] - fn copy_slice_to_end(&mut self, bounds: Range) -> Result<(), usize> { + fn copy_slice_to_end(&mut self, bounds: Range) -> Result<(), AllocError> { self.state.heap.copy_slice_to_end(bounds) } } @@ -455,7 +456,7 @@ impl<'a> CopierTarget for CopyBallTerm<'a> { self.stack } - fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result { + fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result { debug_assert!(pstr_loc < self.heap.byte_len()); let HeapStringScan { string, tail_idx } = self.heap.scan_slice_to_str(pstr_loc); @@ -477,11 +478,11 @@ impl<'a> CopierTarget for CopyBallTerm<'a> { } #[inline] - fn reserve(&mut self, num_cells: usize) -> Result, usize> { + fn reserve(&mut self, num_cells: usize) -> Result, AllocError> { self.stub.reserve(num_cells) } - fn copy_slice_to_end(&mut self, bounds: Range) -> Result<(), usize> { + fn copy_slice_to_end(&mut self, bounds: Range) -> Result<(), AllocError> { let len = bounds.end - bounds.start; let mut stub_writer = self.stub.reserve(len)?; diff --git a/src/machine/machine_state_impl.rs b/src/machine/machine_state_impl.rs index 04fd143f..a4de9f93 100644 --- a/src/machine/machine_state_impl.rs +++ b/src/machine/machine_state_impl.rs @@ -4,6 +4,7 @@ use crate::forms::*; use crate::heap_iter::*; use crate::machine::attributed_variables::*; use crate::machine::copier::*; +use crate::machine::heap::AllocError; use crate::machine::heap::*; use crate::machine::machine_errors::*; use crate::machine::machine_indices::*; @@ -30,8 +31,8 @@ impl MachineState { heap.store_resource_error(); MachineState { - arena: Arena::new(), - atom_tbl: AtomTable::new(), + arena: Arena::new().unwrap(), + atom_tbl: AtomTable::new().unwrap(), pdl: Vec::with_capacity(1024), s: HeapPtr::default(), s_offset: 0, @@ -47,7 +48,7 @@ impl MachineState { fail: false, heap, mode: MachineMode::Write, - stack: Stack::new(), + stack: Stack::new().unwrap(), registers: [heap_loc_as_cell!(0); MAX_ARITY + 1], // self.registers[0] is never used. trail: vec![], tr: 0, @@ -174,8 +175,8 @@ impl MachineState { } } - pub fn allocate(&mut self, num_cells: usize) { - let e = self.stack.allocate_and_frame(num_cells); + pub fn allocate(&mut self, num_cells: usize) -> Result<(), AllocError> { + let e = self.stack.allocate_and_frame(num_cells)?; let and_frame = self.stack.index_and_frame_mut(e); and_frame.prelude.e = self.e; @@ -183,6 +184,8 @@ impl MachineState { self.e = e; self.p += 1; + + Ok(()) } pub fn bind(&mut self, r1: Ref, a2: HeapCellValue) { @@ -922,7 +925,7 @@ impl MachineState { name: Atom, arity: usize, r: Ref, - ) -> Result<(), usize> { + ) -> Result<(), AllocError> { let h = self.heap.cell_len(); let mut writer = self.heap.reserve(arity + 1)?; diff --git a/src/machine/mock_wam.rs b/src/machine/mock_wam.rs index df9dbe75..7fcc5613 100644 --- a/src/machine/mock_wam.rs +++ b/src/machine/mock_wam.rs @@ -168,7 +168,7 @@ impl<'a> CopierTarget for TermCopyingMockWAM<'a> { } #[inline(always)] - fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result { + fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result { self.wam.machine_st.heap.copy_pstr_within(pstr_loc) } @@ -178,12 +178,12 @@ impl<'a> CopierTarget for TermCopyingMockWAM<'a> { } #[inline(always)] - fn reserve(&mut self, num_cells: usize) -> Result, usize> { + fn reserve(&mut self, num_cells: usize) -> Result, AllocError> { self.wam.machine_st.heap.reserve(num_cells) } #[inline(always)] - fn copy_slice_to_end(&mut self, bounds: Range) -> Result<(), usize> { + fn copy_slice_to_end(&mut self, bounds: Range) -> Result<(), AllocError> { self.wam.machine_st.heap.copy_slice_to_end(bounds) } } diff --git a/src/machine/mod.rs b/src/machine/mod.rs index db6dae7e..5f581900 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -38,6 +38,7 @@ use crate::instructions::*; use crate::machine::args::*; use crate::machine::compile::*; use crate::machine::copier::*; +use crate::machine::heap::AllocError; use crate::machine::heap::*; use crate::machine::loader::*; use crate::machine::machine_errors::*; @@ -60,6 +61,7 @@ use std::cmp::Ordering; use std::env; use std::io::Read; use std::path::PathBuf; +use std::process::ExitCode; use std::sync::atomic::AtomicBool; use std::sync::OnceLock; @@ -261,7 +263,9 @@ impl Machine { let p = index_ptr.local().unwrap(); // Leave a halting choice point to backtrack to in case the predicate fails or throws. - self.allocate_stub_choice_point(); + if self.allocate_stub_choice_point().is_err() { + return ExitCode::FAILURE; + } self.machine_st.cp = BREAK_FROM_DISPATCH_LOOP_LOC; self.machine_st.p = p; @@ -729,10 +733,10 @@ impl Machine { } #[inline(always)] - pub(super) fn try_me_else(&mut self, offset: usize) { + pub(super) fn try_me_else(&mut self, offset: usize) -> Result<(), AllocError> { if let Some(offset) = self.next_applicable_clause(offset) { let n = self.machine_st.num_of_args; - let b = self.machine_st.stack.allocate_or_frame(n); + let b = self.machine_st.stack.allocate_or_frame(n)?; let or_frame = self.machine_st.stack.index_or_frame_mut(b); or_frame.prelude.num_cells = n; @@ -758,13 +762,15 @@ impl Machine { } self.machine_st.p += 1; + + Ok(()) } #[inline(always)] - pub(super) fn indexed_try(&mut self, offset: usize) { + pub(super) fn indexed_try(&mut self, offset: usize) -> Result<(), AllocError> { if let Some(iip_offset) = self.next_inner_applicable_clause() { let n = self.machine_st.num_of_args; - let b = self.machine_st.stack.allocate_or_frame(n); + let b = self.machine_st.stack.allocate_or_frame(n)?; let or_frame = self.machine_st.stack.index_or_frame_mut(b); or_frame.prelude.num_cells = n; @@ -793,6 +799,7 @@ impl Machine { } self.machine_st.p += offset; + Ok(()) } #[inline(always)] diff --git a/src/machine/stack.rs b/src/machine/stack.rs index 4247149f..98e1dab8 100644 --- a/src/machine/stack.rs +++ b/src/machine/stack.rs @@ -1,5 +1,7 @@ use core::marker::PhantomData; +use std::ptr::NonNull; +use crate::machine::heap::AllocError; use crate::raw_block::*; use crate::types::*; @@ -159,45 +161,42 @@ impl OrFrame { } impl Stack { - pub(crate) fn new() -> Self { - Stack { - buf: RawBlock::new(), + pub(crate) fn new() -> Result { + Ok(Stack { + buf: RawBlock::new()?, _marker: PhantomData, - } + }) } #[inline(always)] - unsafe fn alloc(&mut self, frame_size: usize) -> *mut u8 { + unsafe fn alloc(&mut self, frame_size: usize) -> Result, AllocError> { loop { let ptr = self.buf.alloc(frame_size); - - if ptr.is_null() { - if !self.buf.grow() { - panic!("growing the stack failed") - } - } else { - return ptr; + if let Some(ptr) = NonNull::new(ptr) { + return Ok(ptr); } + self.buf.grow()?; } } - pub(crate) fn allocate_and_frame(&mut self, num_cells: usize) -> usize { + pub(crate) fn allocate_and_frame(&mut self, num_cells: usize) -> Result { let frame_size = AndFrame::size_of(num_cells); unsafe { let e = (*self.buf.ptr.get_mut()).addr() - self.buf.base.addr(); - let new_ptr = self.alloc(frame_size); + let new_ptr = self.alloc(frame_size)?; let mut offset = prelude_size::(); for idx in 0..num_cells { - let cell_ptr = new_ptr.add(offset) as *mut HeapCellValue; - ptr::write(cell_ptr, stack_loc_as_cell!(AndFrame, e, idx + 1)); + let cell_ptr = new_ptr.add(offset).cast::(); + ptr::write(cell_ptr.as_ptr(), stack_loc_as_cell!(AndFrame, e, idx + 1)); // Because in the Index and IndexMut inplementations we need to get this from // exposed provenance, we need to expose the provenance here, even though we don't // actually use the value for anything. This is a reminder that `expose_provenance` // isn't just a cast from a pointer to an integer but has actual side effects. - cell_ptr.expose_provenance(); + // FIXME(msrv) remove the as_ptr() call once MSRV reaches 1.89.0 + cell_ptr.as_ptr().expose_provenance(); offset += mem::size_of::(); } @@ -205,7 +204,7 @@ impl Stack { let and_frame = self.index_and_frame_mut(e); and_frame.prelude.num_cells = num_cells; - e + Ok(e) } } @@ -213,23 +212,24 @@ impl Stack { unsafe { (*self.buf.ptr.get()).addr() - self.buf.base.addr() } } - pub(crate) fn allocate_or_frame(&mut self, num_cells: usize) -> usize { + pub(crate) fn allocate_or_frame(&mut self, num_cells: usize) -> Result { let frame_size = OrFrame::size_of(num_cells); unsafe { let b = (*self.buf.ptr.get_mut()).addr() - self.buf.base.addr(); - let new_ptr = self.alloc(frame_size); + let new_ptr = self.alloc(frame_size)?; let mut offset = prelude_size::(); for idx in 0..num_cells { - let cell_ptr = new_ptr.byte_add(offset) as *mut HeapCellValue; - ptr::write(cell_ptr, stack_loc_as_cell!(OrFrame, b, idx)); + let cell_ptr = new_ptr.byte_add(offset).cast::(); + ptr::write(cell_ptr.as_ptr(), stack_loc_as_cell!(OrFrame, b, idx)); // Because in the Index and IndexMut inplementations we need to get this from // exposed provenance, we need to expose the provenance here, even though we don't // actually use the value for anything. This is a reminder that `expose_provenance` // isn't just a cast from a pointer to an integer but has actual side effects. - cell_ptr.expose_provenance(); + // FIXME(msrv) remove as_ptr() call once msrv reaches 1.89.0 + cell_ptr.as_ptr().expose_provenance(); offset += mem::size_of::(); } @@ -237,7 +237,7 @@ impl Stack { let or_frame = self.index_or_frame_mut(b); or_frame.prelude.num_cells = num_cells; - b + Ok(b) } } @@ -285,7 +285,7 @@ mod tests { fn stack_tests() { let mut wam = MockWAM::new(); - let e = wam.machine_st.stack.allocate_and_frame(10); // create an AND frame! + let e = wam.machine_st.stack.allocate_and_frame(10).unwrap(); // create an AND frame! let and_frame = wam.machine_st.stack.index_and_frame_mut(e); assert_eq!( @@ -303,7 +303,7 @@ mod tests { assert_eq!(and_frame[5], empty_list_as_cell!()); - let b = wam.machine_st.stack.allocate_or_frame(5); + let b = wam.machine_st.stack.allocate_or_frame(5).unwrap(); let or_frame = wam.machine_st.stack.index_or_frame_mut(b); @@ -311,7 +311,7 @@ mod tests { assert_eq!(or_frame[idx], stack_loc_as_cell!(OrFrame, b, idx)); } - let next_e = wam.machine_st.stack.allocate_and_frame(9); // create an AND frame! + let next_e = wam.machine_st.stack.allocate_and_frame(9).unwrap(); // create an AND frame! let and_frame = wam.machine_st.stack.index_and_frame_mut(next_e); for idx in 0..9 { diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 4e1797a9..8cc5f83b 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -17,6 +17,7 @@ use crate::instructions::*; use crate::machine; use crate::machine::code_walker::*; use crate::machine::copier::*; +use crate::machine::heap::AllocError; use crate::machine::heap::*; use crate::machine::machine_errors::*; use crate::machine::machine_indices::*; @@ -635,7 +636,7 @@ impl MachineState { pub(crate) fn get_attr_var_list( &mut self, attr_var: HeapCellValue, - ) -> Result, usize> { + ) -> Result, AllocError> { read_heap_cell!(attr_var, (HeapCellValueTag::AttrVar, h) => { Ok(Some(h + 1)) @@ -903,7 +904,7 @@ impl MachineState { &mut self, lh_offset: usize, copy_target: HeapCellValue, - ) -> Result { + ) -> Result { let threshold = self.lifted_heap.cell_len() - lh_offset; let mut writer = self.lifted_heap.reserve(5)?; @@ -1041,7 +1042,7 @@ impl MachineState { &mut self, chunk: HeapCellValue, return_p: usize, - ) -> usize { + ) -> Result { let chunk = self.store(self.deref(chunk)); let s = chunk.get_value() as usize; @@ -1053,7 +1054,7 @@ impl MachineState { let cp = to_local_code_ptr(&self.heap, p_functor).unwrap(); let prev_e = self.e; - let e = self.stack.allocate_and_frame(num_cells); + let e = self.stack.allocate_and_frame(num_cells)?; let and_frame = self.stack.index_and_frame_mut(e); and_frame.prelude.e = prev_e; @@ -1084,7 +1085,7 @@ impl MachineState { } self.e = e; - self.p + Ok(self.p) } pub fn value_to_str_like(&mut self, value: HeapCellValue) -> Option { @@ -2458,7 +2459,14 @@ impl Machine { self.machine_st.p = return_p; for chunk in cont_chunks.into_iter().rev() { - return_p = self.machine_st.call_continuation_chunk(chunk, return_p); + match self.machine_st.call_continuation_chunk(chunk, return_p) { + Ok(ret_p) => { + return_p = ret_p; + } + Err(err) => { + self.machine_st.throw_resource_error(err); + } + } } Ok(()) @@ -4092,7 +4100,7 @@ impl Machine { fn write_op_functors_to_heap( heap: &mut Heap, op_descs: impl Iterator, - ) -> Result { + ) -> Result { let mut num_functors = 0; for (name, op_desc) in op_descs { @@ -5130,7 +5138,11 @@ impl Machine { } #[cfg(feature = "ffi")] - fn build_struct(&mut self, name: Atom, mut args: Vec) -> Result { + fn build_struct( + &mut self, + name: Atom, + mut args: Vec, + ) -> Result { args.insert(0, Value::CString(CString::new(&*name.as_str()).unwrap())); let cells: Vec<_> = args @@ -5150,7 +5162,7 @@ impl Machine { Value::Struct(name, struct_args) => self.build_struct(name, struct_args)?, }) }) - .collect::>()?; + .collect::>()?; sized_iter_to_heap_list(&mut self.machine_st.heap, cells.len(), cells.into_iter()) } @@ -7579,7 +7591,7 @@ impl Machine { false } - fn walk_code_at_ptr(&mut self, index_ptr: usize) -> Result { + fn walk_code_at_ptr(&mut self, index_ptr: usize) -> Result { let orig_h = self.machine_st.heap.cell_len(); let mut h = orig_h; @@ -8400,7 +8412,7 @@ impl Machine { } #[inline(always)] - pub(crate) fn load_html(&mut self) -> Result<(), usize> { + pub(crate) fn load_html(&mut self) -> Result<(), AllocError> { if let Some(string) = self .machine_st .value_to_str_like(self.machine_st.registers[1]) @@ -8429,7 +8441,7 @@ impl Machine { } #[inline(always)] - pub(crate) fn load_xml(&mut self) -> Result<(), usize> { + pub(crate) fn load_xml(&mut self) -> Result<(), AllocError> { if let Some(string) = self .machine_st .value_to_str_like(self.machine_st.registers[1]) @@ -8965,8 +8977,8 @@ impl Machine { Ok(loc) => { unify!(self.machine_st, status_r, loc); } - Err(resource_err_loc) => { - self.machine_st.throw_resource_error(resource_err_loc); + Err(err) => { + self.machine_st.throw_resource_error(err); } } Ok(()) @@ -8983,8 +8995,8 @@ impl Machine { Ok(loc) => { unify!(self.machine_st, status_r, loc); } - Err(resource_err_loc) => { - self.machine_st.throw_resource_error(resource_err_loc); + Err(err) => { + self.machine_st.throw_resource_error(err); } } Ok(()) @@ -9320,7 +9332,7 @@ impl Machine { pub(super) fn xml_node_to_term( &mut self, node: roxmltree::Node, - ) -> Result { + ) -> Result { if node.is_text() { self.machine_st.heap.allocate_cstr(node.text().unwrap()) } else { @@ -9371,7 +9383,7 @@ impl Machine { pub(super) fn html_node_to_term( &mut self, node: ego_tree::NodeRef<'_, scraper::Node>, - ) -> Result { + ) -> Result { match node.value() { scraper::Node::Document | scraper::Node::Fragment => { unreachable!("we never iterate the root itself only its children") @@ -9476,7 +9488,7 @@ impl Machine { } } - pub(super) fn u8s_to_string(&mut self, data: &[u8]) -> Result { + pub(super) fn u8s_to_string(&mut self, data: &[u8]) -> Result { let buffer = String::from_iter(data.iter().map(|b| *b as char)); if buffer.is_empty() { diff --git a/src/macros.rs b/src/macros.rs index 0bf7b6af..92e2179e 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -451,8 +451,8 @@ macro_rules! step_or_resource_error { ($machine_st:expr, $val:expr) => {{ match $val { Ok(r) => r, - Err(err_loc) => { - $machine_st.throw_resource_error(err_loc); + Err(err) => { + $machine_st.throw_resource_error(err); return; } } @@ -460,8 +460,8 @@ macro_rules! step_or_resource_error { ($machine_st:expr, $val:expr, $fail:block) => {{ match $val { Ok(r) => r, - Err(err_loc) => { - $machine_st.throw_resource_error(err_loc); + Err(err) => { + $machine_st.throw_resource_error(err); $fail } } diff --git a/src/offset_table.rs b/src/offset_table.rs index 74967c60..ec3f6031 100644 --- a/src/offset_table.rs +++ b/src/offset_table.rs @@ -10,6 +10,7 @@ use fxhash::FxBuildHasher; use indexmap::IndexMap; use parking_lot::{Mutex, RwLock}; +use crate::machine::heap::AllocError; use crate::machine::machine_indices::IndexPtr; use crate::raw_block::RawBlock; use crate::raw_block::RawBlockTraits; @@ -58,8 +59,10 @@ impl From>> for OffsetTableImpl< impl OffsetTableImpl { #[inline(always)] - pub fn new() -> Self { - Self(InnerOffsetTableImpl::Serial(SerialOffsetTable::new())) + pub fn new() -> Result { + Ok(Self( + InnerOffsetTableImpl::Serial(SerialOffsetTable::new()?), + )) } #[must_use = "the returned concurrent table must be absorbed into the owned OffsetTable"] @@ -116,7 +119,7 @@ impl OffsetTableImpl { impl Default for OffsetTableImpl { fn default() -> Self { - Self::new() + Self::new().unwrap() } } @@ -201,10 +204,10 @@ impl OffsetTable for OffsetTableImpl { impl SerialOffsetTable { #[inline] - fn new() -> Self { - Self { - block: RawBlock::new(), - } + fn new() -> Result { + Ok(Self { + block: RawBlock::new()?, + }) } unsafe fn build_with(&mut self, value: T) -> usize { @@ -374,11 +377,11 @@ pub enum F64Table { } impl F64Table { - pub fn new() -> Self { - Self::Serial(SerialF64Table { + pub fn new() -> Result { + Ok(Self::Serial(SerialF64Table { indirection_tbl: IndexMap::with_hasher(FxBuildHasher::new()), - offset_tbl: SerialOffsetTable::new(), - }) + offset_tbl: SerialOffsetTable::new()?, + })) } pub fn build_with(&mut self, value: OrderedFloat) -> F64Offset { diff --git a/src/raw_block.rs b/src/raw_block.rs index 02bea9f0..64346883 100644 --- a/src/raw_block.rs +++ b/src/raw_block.rs @@ -4,6 +4,8 @@ use std::alloc; use std::cell::UnsafeCell; use std::ptr; +use crate::machine::heap::AllocError; + pub trait RawBlockTraits { fn init_size() -> usize; fn align() -> usize; @@ -29,65 +31,57 @@ impl RawBlock { } #[allow(clippy::new_without_default)] - pub fn new() -> Self { + pub fn new() -> Result { let mut block = Self::empty_block(); unsafe { - block.grow(); + block.grow()?; } - block + Ok(block) } - unsafe fn init_at_size(&mut self, cap: usize) { + unsafe fn init_at_size(&mut self, cap: usize) -> Result<(), AllocError> { let layout = alloc::Layout::from_size_align_unchecked(cap, T::align()); let new_base = alloc::alloc(layout).cast_const(); if new_base.is_null() { - panic!( - "failed to allocate in init_at_size for {}", - std::any::type_name::() - ); + return Err(AllocError); } self.base = new_base; self.top = self.base.add(cap); *self.ptr.get_mut() = self.base.cast_mut(); + Ok(()) } - pub unsafe fn grow(&mut self) -> bool { + pub unsafe fn grow(&mut self) -> Result<(), AllocError> { if self.base.is_null() { - self.init_at_size(T::init_size()); - true + self.init_at_size(T::init_size()) } else { let size = self.size(); let layout = alloc::Layout::from_size_align_unchecked(size, T::align()); let new_base = alloc::realloc(self.base.cast_mut(), layout, size * 2).cast_const(); if new_base.is_null() { - false + Err(AllocError) } else { self.base = new_base; self.top = self.base.add(size * 2); *self.ptr.get_mut() = self.base.add(size).cast_mut(); - true + Ok(()) } } } - pub unsafe fn grow_new(&self) -> Option { + pub unsafe fn grow_new(&self) -> Result { if self.base.is_null() { - Some(Self::new()) + Self::new() } else { let mut new_block = Self::empty_block(); - new_block.init_at_size(self.size() * 2); - if new_block.base.is_null() { - // allocation failed - None - } else { - let allocated = (*self.ptr.get()).addr() - self.base.addr(); - self.base.copy_to(new_block.base.cast_mut(), allocated); - *new_block.ptr.get_mut() = new_block.base.add(allocated).cast_mut(); - Some(new_block) - } + new_block.init_at_size(self.size() * 2)?; + let allocated = (*self.ptr.get()).addr() - self.base.addr(); + self.base.copy_to(new_block.base.cast_mut(), allocated); + *new_block.ptr.get_mut() = new_block.base.add(allocated).cast_mut(); + Ok(new_block) } } diff --git a/src/read.rs b/src/read.rs index e9603522..b8817aa6 100644 --- a/src/read.rs +++ b/src/read.rs @@ -381,7 +381,7 @@ impl<'a> TermWriter<'a> { fn push_cell(&mut self, cell: HeapCellValue) -> Result<(), CompilationError> { self.heap .push_cell(cell) - .map_err(CompilationError::FiniteMemoryInHeap) + .map_err(|err| err.to_compilation_error(self.heap)) } fn term_as_addr(&mut self, term: &TermRef, h: usize) -> HeapCellValue { @@ -478,7 +478,7 @@ impl<'a> TermWriter<'a> { let cell = self .heap .allocate_cstr(src) - .map_err(CompilationError::FiniteMemoryInHeap)?; + .map_err(|err| err.to_compilation_error(self.heap))?; let new_h = self.heap.cell_len(); self.push_cell(cell)?; @@ -499,7 +499,7 @@ impl<'a> TermWriter<'a> { let cell = self .heap .allocate_pstr(src) - .map_err(CompilationError::FiniteMemoryInHeap)?; + .map_err(|err| err.to_compilation_error(self.heap))?; let tail_h = self.heap.cell_len(); self.push_stub_addr()?; From 6063783e9dfcf15de007f15db20c1aeecee0a173 Mon Sep 17 00:00:00 2001 From: Skgland Date: Thu, 20 Nov 2025 00:19:24 +0100 Subject: [PATCH 09/14] propagate AllocErrro instead of unwrapping --- src/machine/copier.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/machine/copier.rs b/src/machine/copier.rs index 42359806..4de9a0c1 100644 --- a/src/machine/copier.rs +++ b/src/machine/copier.rs @@ -306,7 +306,7 @@ impl CopyTermState { let heap_loc = list_addr.get_value() as usize; let str_loc = self.target[heap_loc].get_value() as usize; let str_cell = self.target[str_loc]; - let mut writer = self.target.reserve(3).unwrap(); + let mut writer = self.target.reserve(3)?; writer.write_with(|section| { section.push_cell(heap_loc_as_cell!(threshold + 2)); @@ -435,7 +435,7 @@ impl CopyTermState { let str_cell = if get_structure_index(index_cell).is_some() { // copy the index pointer trailing this // inlined or expanded goal. - let mut writer = self.target.reserve(1).unwrap(); + let mut writer = self.target.reserve(1)?; writer.write_with(|section| { section.push_cell(index_cell); From 516848e214e7672adbcc1991d60a2df89f4fc0a4 Mon Sep 17 00:00:00 2001 From: Skgland Date: Thu, 20 Nov 2025 00:48:04 +0100 Subject: [PATCH 10/14] wrap resource_error into an error/2 functor and don't wrap it into a syntax_error functor --- src/machine/heap.rs | 13 +++++++------ src/machine/machine_errors.rs | 25 ++++++++++++++++++++++--- src/read.rs | 14 +++----------- 3 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/machine/heap.rs b/src/machine/heap.rs index 964d983e..3c92f29b 100644 --- a/src/machine/heap.rs +++ b/src/machine/heap.rs @@ -1,6 +1,5 @@ use crate::atom_table::*; use crate::functor_macro::*; -use crate::machine::machine_errors::CompilationError; use crate::machine::{ArenaHeaderTag, Fixnum, Integer}; use crate::types::*; @@ -16,10 +15,6 @@ const ALIGN: usize = Heap::heap_cell_alignment(); pub struct AllocError; impl AllocError { - pub(crate) fn to_compilation_error(&self, heap: &mut Heap) -> CompilationError { - CompilationError::FiniteMemoryInHeap(self.resource_error_offset(heap)) - } - pub(crate) fn resource_error_offset(&self, heap: &mut Heap) -> usize { heap.resource_error_offset() } @@ -706,7 +701,13 @@ impl Heap { pub(crate) fn store_resource_error(&mut self) { RESOURCE_ERROR_OFFSET_INIT.call_once(move || { - let stub = functor!(atom!("resource_error"), [atom_as_cell((atom!("memory")))]); + let stub = functor!( + atom!("error"), + [ + functor((atom!("resource_error")), [atom_as_cell((atom!("memory")))]), + atom_as_cell((atom!("[]"))) + ] + ); self.resource_err_loc = cell_index!(self.inner.byte_len); let mut writer = Heap::functor_writer(stub); diff --git a/src/machine/machine_errors.rs b/src/machine/machine_errors.rs index 4df767bf..fa6822d0 100644 --- a/src/machine/machine_errors.rs +++ b/src/machine/machine_errors.rs @@ -646,6 +646,19 @@ impl MachineState { return self.directive_error(err); } + if let CompilationError::FiniteMemoryInHeap(err) = err { + // err.resource_error_offset() should be the address of the error/2 functor in the pre-allocated term error(resource_error(memory), []) + let err_loc = err.resource_error_offset(&mut self.heap); + let stub = vec![FunctorElement::AbsoluteCell( + // err_loc + 1 should be the functors first argument which should be a str cell pointing at the resource_error/1 functor + self.heap[err_loc + 1], + )]; + return MachineError { + stub, + location: None, + }; + } + let location = err.line_and_col_num(); let stub = err.as_functor(); @@ -803,7 +816,13 @@ pub enum CompilationError { InvalidRuleHead, InvalidUseModuleDecl, InvalidModuleResolution(Atom), - FiniteMemoryInHeap(usize), + FiniteMemoryInHeap(AllocError), +} + +impl From for CompilationError { + fn from(value: AllocError) -> Self { + Self::FiniteMemoryInHeap(value) + } } #[derive(Debug)] @@ -879,8 +898,8 @@ impl CompilationError { CompilationError::ParserError(ref err) => { functor!(err.as_atom()) } - CompilationError::FiniteMemoryInHeap(h) => { - vec![FunctorElement::AbsoluteCell(str_loc_as_cell!(*h))] + CompilationError::FiniteMemoryInHeap(_) => { + functor!(atom!("resource_error")) } } } diff --git a/src/read.rs b/src/read.rs index b8817aa6..fe709c7e 100644 --- a/src/read.rs +++ b/src/read.rs @@ -379,9 +379,7 @@ impl<'a> TermWriter<'a> { #[inline] fn push_cell(&mut self, cell: HeapCellValue) -> Result<(), CompilationError> { - self.heap - .push_cell(cell) - .map_err(|err| err.to_compilation_error(self.heap)) + Ok(self.heap.push_cell(cell)?) } fn term_as_addr(&mut self, term: &TermRef, h: usize) -> HeapCellValue { @@ -475,10 +473,7 @@ impl<'a> TermWriter<'a> { self.push_stub_addr()?; } - let cell = self - .heap - .allocate_cstr(src) - .map_err(|err| err.to_compilation_error(self.heap))?; + let cell = self.heap.allocate_cstr(src)?; let new_h = self.heap.cell_len(); self.push_cell(cell)?; @@ -496,10 +491,7 @@ impl<'a> TermWriter<'a> { self.push_stub_addr()?; } - let cell = self - .heap - .allocate_pstr(src) - .map_err(|err| err.to_compilation_error(self.heap))?; + let cell = self.heap.allocate_pstr(src)?; let tail_h = self.heap.cell_len(); self.push_stub_addr()?; From e90c813528d27074fddbf8886167f4a0494a243b Mon Sep 17 00:00:00 2001 From: Skgland Date: Thu, 20 Nov 2025 21:13:17 +0100 Subject: [PATCH 11/14] return an AllocError instead of panicing in Heap::with_cell_capacity --- src/machine/heap.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/machine/heap.rs b/src/machine/heap.rs index 3c92f29b..18c7addf 100644 --- a/src/machine/heap.rs +++ b/src/machine/heap.rs @@ -596,7 +596,7 @@ impl Heap { self.resource_err_loc } - pub(crate) fn with_cell_capacity(cap: usize) -> Result { + pub(crate) fn with_cell_capacity(cap: usize) -> Result { let ptr = unsafe { let layout = alloc::Layout::from_size_align( cap * size_of::(), @@ -607,7 +607,7 @@ impl Heap { }; if ptr.is_null() { - panic!("could not allocate {} bytes for heap!", heap_index!(cap)) + Err(AllocError) } else { Ok(Self { inner: InnerHeap { From a05dc7950542ae1b2ae4b0925a0afcba843a2cc6 Mon Sep 17 00:00:00 2001 From: Skgland Date: Thu, 20 Nov 2025 21:24:47 +0100 Subject: [PATCH 12/14] fix unecessary parens in macro --- src/macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/macros.rs b/src/macros.rs index 92e2179e..cf5b1891 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -484,6 +484,6 @@ macro_rules! heap_index { macro_rules! cell_index { ($idx:expr) => { - (($idx) / std::mem::size_of::()) + ($idx) / std::mem::size_of::() }; } From d4f2f7ba2b79609046e51bbd41756d95b919d13a Mon Sep 17 00:00:00 2001 From: Skgland Date: Thu, 20 Nov 2025 21:44:29 +0100 Subject: [PATCH 13/14] remove RESOURCE_ERROR_OFFSET_INIT With it when using multiple Machine in one process only the first would store the pre-allocated error. Instead Heap.resource_err_loc is now Option> instead of usize using None for uninitialized. The cell at index 0 should alredy be used by a runtime reserved interstitial cell that is allocated prior. So requiring the offset to be non zero should be fine. --- src/machine/heap.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/machine/heap.rs b/src/machine/heap.rs index 18c7addf..c812b30b 100644 --- a/src/machine/heap.rs +++ b/src/machine/heap.rs @@ -5,9 +5,9 @@ use crate::types::*; use std::alloc; use std::convert::TryFrom; +use std::num::NonZero; use std::ops::{Bound, Index, IndexMut, Range, RangeBounds}; use std::ptr; -use std::sync::Once; const ALIGN: usize = Heap::heap_cell_alignment(); @@ -23,7 +23,7 @@ impl AllocError { #[derive(Debug)] pub struct Heap { inner: InnerHeap, - resource_err_loc: usize, + resource_err_loc: Option>, } impl Drop for Heap { @@ -94,8 +94,6 @@ impl InnerHeap { unsafe impl Send for Heap {} unsafe impl Sync for Heap {} -static RESOURCE_ERROR_OFFSET_INIT: Once = Once::new(); - #[derive(Debug)] pub struct HeapStringScan<'a> { pub string: &'a str, @@ -572,7 +570,7 @@ impl Heap { byte_len: 0, byte_cap: 0, }, - resource_err_loc: 0, + resource_err_loc: None, } } @@ -594,6 +592,8 @@ impl Heap { #[inline] fn resource_error_offset(&self) -> usize { self.resource_err_loc + .expect("`error(resource_error(memory), [])` should be stored at the start of the heap") + .get() } pub(crate) fn with_cell_capacity(cap: usize) -> Result { @@ -616,7 +616,7 @@ impl Heap { byte_cap: heap_index!(cap), }, // pstr_vec: bitvec![], - resource_err_loc: 0, + resource_err_loc: None, }) } } @@ -700,7 +700,7 @@ impl Heap { } pub(crate) fn store_resource_error(&mut self) { - RESOURCE_ERROR_OFFSET_INIT.call_once(move || { + if self.resource_err_loc.is_none() { let stub = functor!( atom!("error"), [ @@ -708,11 +708,14 @@ impl Heap { atom_as_cell((atom!("[]"))) ] ); - self.resource_err_loc = cell_index!(self.inner.byte_len); + + self.resource_err_loc = Some(NonZero::new(cell_index!(self.inner.byte_len)).expect( + "index 0 should already be taken by an interstitial cell reserved by the runtime", + )); let mut writer = Heap::functor_writer(stub); writer(self).unwrap(); - }); + } } #[inline] From 6ec8c9064f53fb6c308cdcb547c83057ee0bf65e Mon Sep 17 00:00:00 2001 From: Skgland Date: Thu, 20 Nov 2025 23:08:46 +0100 Subject: [PATCH 14/14] fix pstr_iter_tests test --- src/machine/heap.rs | 11 +---------- src/machine/partial_string.rs | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/machine/heap.rs b/src/machine/heap.rs index c812b30b..d9f1aa88 100644 --- a/src/machine/heap.rs +++ b/src/machine/heap.rs @@ -687,16 +687,7 @@ impl Heap { } pub(crate) fn clear(&mut self) { - unsafe { - let layout = - alloc::Layout::from_size_align(self.inner.byte_cap, size_of::()) - .unwrap(); - alloc::dealloc(self.inner.ptr, layout); - } - - self.inner.ptr = ptr::null_mut(); - self.inner.byte_len = 0; - self.inner.byte_cap = 0; + *self = Heap::new(); } pub(crate) fn store_resource_error(&mut self) { diff --git a/src/machine/partial_string.rs b/src/machine/partial_string.rs index 977c421c..80c3c9af 100644 --- a/src/machine/partial_string.rs +++ b/src/machine/partial_string.rs @@ -217,6 +217,8 @@ mod test { fn pstr_iter_tests() { let mut wam = MockWAM::new(); + let init_len = wam.machine_st.heap.cell_len(); + let pstr_cell = wam.machine_st.heap.allocate_pstr("abc ").unwrap(); wam.machine_st .heap @@ -233,7 +235,7 @@ mod test { assert_eq!( iter.next(), Some(PStrIteratee::PStrSlice { - slice_loc: heap_index!(1), + slice_loc: heap_index!(init_len), slice_len: "abc ".len() }), ); @@ -241,9 +243,9 @@ mod test { assert!(!iter.is_cyclic()); } - assert_eq!(wam.machine_st.heap[2], empty_list_as_cell!()); + assert_eq!(wam.machine_st.heap[init_len + 1], empty_list_as_cell!()); - wam.machine_st.heap[2] = pstr_loc_as_cell!(heap_index!(3)); + wam.machine_st.heap[init_len + 1] = pstr_loc_as_cell!(heap_index!(init_len + 2)); wam.machine_st.heap.allocate_pstr("def").unwrap(); let h = wam.machine_st.heap.cell_len(); @@ -256,14 +258,14 @@ mod test { assert_eq!( iter.next(), Some(PStrIteratee::PStrSlice { - slice_loc: heap_index!(1), + slice_loc: heap_index!(init_len), slice_len: "abc ".len() }) ); assert_eq!( iter.next(), Some(PStrIteratee::PStrSlice { - slice_loc: heap_index!(3), + slice_loc: heap_index!(init_len + 2), slice_len: "def".len(), }) ); @@ -282,14 +284,14 @@ mod test { assert_eq!( iter.next(), Some(PStrIteratee::PStrSlice { - slice_loc: heap_index!(1), + slice_loc: heap_index!(init_len), slice_len: "abc ".len() }) ); assert_eq!( iter.next(), Some(PStrIteratee::PStrSlice { - slice_loc: heap_index!(3), + slice_loc: heap_index!(init_len + 2), slice_len: "def".len(), }) ); @@ -298,7 +300,7 @@ mod test { assert!(!iter.is_cyclic()); } - wam.machine_st.heap[h] = pstr_loc_as_cell!(heap_index!(3)); + wam.machine_st.heap[h] = pstr_loc_as_cell!(heap_index!(init_len + 2)); { let mut iter = HeapPStrIter::new(&wam.machine_st.heap, 0);