Merge pull request #3164 from Skgland/alloc_errors2

handle machine heap/stack allocation error
This commit is contained in:
Mark Thom
2025-12-03 19:10:22 -07:00
committed by GitHub
30 changed files with 380 additions and 242 deletions

26
clippy.toml Normal file
View File

@@ -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"},
]

View File

@@ -1,7 +1,9 @@
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work #![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")] #[cfg(feature = "http")]
use crate::http::{HttpListener, HttpResponse}; use crate::http::{HttpListener, HttpResponse};
use crate::machine::heap::AllocError;
use crate::machine::loader::LiveLoadState; use crate::machine::loader::LiveLoadState;
use crate::machine::streams::*; use crate::machine::streams::*;
use crate::offset_table::*; use crate::offset_table::*;
@@ -485,12 +487,12 @@ unsafe impl Sync for Arena {}
#[allow(clippy::new_without_default)] #[allow(clippy::new_without_default)]
impl Arena { impl Arena {
#[inline] #[inline]
pub fn new() -> Self { pub fn new() -> Result<Self, AllocError> {
Arena { Ok(Arena {
base: None, base: None,
f64_tbl: F64Table::new(), f64_tbl: F64Table::new()?,
code_index_tbl: CodeIndexTable::new(), code_index_tbl: CodeIndexTable::new()?,
} })
} }
} }

View File

@@ -1,5 +1,7 @@
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work #![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::parser::ast::MAX_ARITY;
use crate::raw_block::*; use crate::raw_block::*;
use crate::types::*; use crate::types::*;
@@ -436,21 +438,21 @@ impl InnerAtomTable {
impl AtomTable { impl AtomTable {
#[inline] #[inline]
pub fn new() -> Arc<Self> { pub fn new() -> Result<Arc<Self>, AllocError> {
let upgraded = global_atom_table().read().unwrap().upgrade(); let upgraded = global_atom_table().read().unwrap().upgrade();
// don't inline upgraded, otherwise temporary will be dropped too late in case of None // don't inline upgraded, otherwise temporary will be dropped too late in case of None
if let Some(atom_table) = upgraded { if let Some(atom_table) = upgraded {
atom_table Ok(atom_table)
} else { } else {
let mut guard = global_atom_table().write().unwrap(); let mut guard = global_atom_table().write().unwrap();
// try to upgrade again in case we lost the race on the write lock // try to upgrade again in case we lost the race on the write lock
if let Some(atom_table) = guard.upgrade() { if let Some(atom_table) = guard.upgrade() {
atom_table Ok(atom_table)
} else { } else {
let atom_table = Arc::new(Self { let atom_table = Arc::new(Self {
inner: Arcu::new( inner: Arcu::new(
InnerAtomTable { InnerAtomTable {
block: RawBlock::new(), block: RawBlock::new()?,
table: Arcu::new(IndexSet::new(), GlobalEpochCounterPool), table: Arcu::new(IndexSet::new(), GlobalEpochCounterPool),
}, },
GlobalEpochCounterPool, GlobalEpochCounterPool,
@@ -458,11 +460,16 @@ impl AtomTable {
update: Mutex::new(()), update: Mutex::new(()),
}); });
*guard = Arc::downgrade(&atom_table); *guard = Arc::downgrade(&atom_table);
atom_table Ok(atom_table)
} }
} }
} }
#[inline]
pub fn retrieve() -> Arc<Self> {
global_atom_table().read().unwrap().upgrade().unwrap()
}
pub fn active_table(&self) -> RcuRef<IndexSet<Atom>, IndexSet<Atom>> { pub fn active_table(&self) -> RcuRef<IndexSet<Atom>, IndexSet<Atom>> {
self.inner.read().table.read() self.inner.read().table.read()
} }

View File

@@ -551,7 +551,7 @@ mod tests {
#[test] #[test]
fn inlined_atoms() { fn inlined_atoms() {
let atom_table = AtomTable::new(); let atom_table = AtomTable::new().unwrap();
let inlined = AtomTable::build_with(&atom_table, "inline"); let inlined = AtomTable::build_with(&atom_table, "inline");
assert!(inlined.is_inlined()); assert!(inlined.is_inlined());

View File

@@ -1,4 +1,5 @@
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work #![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)] #[cfg(test)]
pub(crate) use crate::machine::gc::StacklessPreOrderHeapIter; pub(crate) use crate::machine::gc::StacklessPreOrderHeapIter;

View File

@@ -1328,9 +1328,7 @@ impl Indexer for DynamicCodeIndices {
for (key, code) in indices.into_iter() { for (key, code) in indices.into_iter() {
if code.len() > 1 { if code.len() > 1 {
index_locs.insert(key, IndexingCodePtr::Internal(prelude.len() + 1)); index_locs.insert(key, IndexingCodePtr::Internal(prelude.len() + 1));
prelude.push_back(IndexingLine::DynamicIndexedChoice( prelude.push_back(IndexingLine::DynamicIndexedChoice(code));
code.into_iter().collect(),
));
} else if let Some(i) = code.front() { } else if let Some(i) = code.front() {
index_locs.insert(key, IndexingCodePtr::DynamicExternal(*i)); index_locs.insert(key, IndexingCodePtr::DynamicExternal(*i));
} }
@@ -1369,9 +1367,7 @@ impl Indexer for DynamicCodeIndices {
) -> IndexingCodePtr { ) -> IndexingCodePtr {
if lists.len() > 1 { if lists.len() > 1 {
let lists = std::mem::take(lists); let lists = std::mem::take(lists);
prelude.push_back(IndexingLine::DynamicIndexedChoice( prelude.push_back(IndexingLine::DynamicIndexedChoice(lists));
lists.into_iter().collect(),
));
IndexingCodePtr::Internal(1) IndexingCodePtr::Internal(1)
} else { } else {
lists lists
@@ -1548,6 +1544,6 @@ impl<I: Indexer> CodeOffsets<I> {
str_loc, str_loc,
))); )));
prelude.into_iter().collect() prelude.into()
} }
} }

View File

@@ -1,4 +1,3 @@
use std::collections::BTreeSet;
use std::env; use std::env;
#[derive(Debug)] #[derive(Debug)]
@@ -8,9 +7,8 @@ pub struct MachineArgs {
impl MachineArgs { impl MachineArgs {
pub fn new() -> Self { pub fn new() -> Self {
let args: BTreeSet<String> = env::args().collect();
Self { Self {
add_history: !args.contains("--no-add-history"), add_history: env::args().all(|arg| arg != "--no-add-history"),
} }
} }
} }

View File

@@ -54,7 +54,9 @@ impl MachineState {
self.attr_var_init.bindings.push((h, addr)); 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 size = self.attr_var_init.bindings.len();
let iter = self let iter = self
@@ -70,7 +72,7 @@ impl MachineState {
Ok((var_list_addr, value_list_addr)) 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 { for (h, _) in &self.attr_var_init.bindings {
self.heap[*h] = attr_var_as_cell!(*h); self.heap[*h] = attr_var_as_cell!(*h);
} }
@@ -110,8 +112,12 @@ impl MachineState {
attr_vars attr_vars
} }
pub(super) fn verify_attr_interrupt(&mut self, p: usize, arity: usize) -> Result<(), usize> { pub(super) fn verify_attr_interrupt(
self.allocate(arity + 3); &mut self,
p: usize,
arity: usize,
) -> Result<(), AllocError> {
self.allocate(arity + 3)?;
let e = self.e; let e = self.e;
let and_frame = self.stack.index_and_frame_mut(e); let and_frame = self.stack.index_and_frame_mut(e);

View File

@@ -1,3 +1,5 @@
#![allow(unused_parens)] // see mthom/scryer-prolog#3092 and rust-lang/rust#147126
use fxhash::FxBuildHasher; use fxhash::FxBuildHasher;
use indexmap::IndexSet; use indexmap::IndexSet;
@@ -81,16 +83,16 @@ pub trait CopierTarget: IndexMut<usize, Output = HeapCellValue> {
fn threshold(&self) -> usize; fn threshold(&self) -> usize;
// returns the tail location of the pstr on success // returns the tail location of the pstr on success
fn as_slice_from<'a>(&'a self, from: usize) -> Box<dyn Iterator<Item = u8> + 'a>; fn as_slice_from<'a>(&'a self, from: usize) -> Box<dyn Iterator<Item = u8> + 'a>;
fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result<usize, usize>; fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result<usize, AllocError>;
fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, usize>; fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, AllocError>;
fn copy_slice_to_end(&mut self, bounds: Range<usize>) -> Result<(), usize>; fn copy_slice_to_end(&mut self, bounds: Range<usize>) -> Result<(), AllocError>;
} }
pub(crate) fn copy_term<T: CopierTarget>( pub(crate) fn copy_term<T: CopierTarget>(
target: T, target: T,
addr: HeapCellValue, addr: HeapCellValue,
attr_var_policy: AttrVarPolicy, attr_var_policy: AttrVarPolicy,
) -> Result<usize, usize> { ) -> Result<usize, AllocError> {
let mut copy_term_state = CopyTermState::new(target, attr_var_policy); let mut copy_term_state = CopyTermState::new(target, attr_var_policy);
let old_threshold = copy_term_state.target.threshold(); let old_threshold = copy_term_state.target.threshold();
@@ -147,7 +149,7 @@ impl<T: CopierTarget> CopyTermState<T> {
self.trail.push((TrailRef::heap_cell(addr), trail_item)); 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 { for offset in 0..2 {
read_heap_cell!(self.target[addr + offset], read_heap_cell!(self.target[addr + offset],
(HeapCellValueTag::Lis, h) => { (HeapCellValueTag::Lis, h) => {
@@ -192,7 +194,7 @@ impl<T: CopierTarget> CopyTermState<T> {
Ok(()) 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() { match self.pstr_loc_locs.range_mut(..=pstr_loc).next_back() {
Some(( Some((
_prev_pstr_loc, _prev_pstr_loc,
@@ -279,7 +281,7 @@ impl<T: CopierTarget> CopyTermState<T> {
Ok(()) 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() { while !self.attr_var_list_locs.is_empty() {
let mut list_loc_vec = std::mem::take(&mut self.attr_var_list_locs); let mut list_loc_vec = std::mem::take(&mut self.attr_var_list_locs);
@@ -298,13 +300,13 @@ impl<T: CopierTarget> CopyTermState<T> {
* structure which is ensured by this function and not at all by * structure which is ensured by this function and not at all by
* the vanilla copier. * 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() { while let HeapCellValueTag::Lis = list_addr.get_tag() {
let threshold = self.target.threshold(); let threshold = self.target.threshold();
let heap_loc = list_addr.get_value() as usize; let heap_loc = list_addr.get_value() as usize;
let str_loc = self.target[heap_loc].get_value() as usize; let str_loc = self.target[heap_loc].get_value() as usize;
let str_cell = self.target[str_loc]; 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| { writer.write_with(|section| {
section.push_cell(heap_loc_as_cell!(threshold + 2)); section.push_cell(heap_loc_as_cell!(threshold + 2));
@@ -328,7 +330,11 @@ impl<T: CopierTarget> CopyTermState<T> {
Ok(()) 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, read_heap_cell!(addr,
(HeapCellValueTag::Var, h) => { (HeapCellValueTag::Var, h) => {
self.target[frontier] = heap_loc_as_cell!(frontier); self.target[frontier] = heap_loc_as_cell!(frontier);
@@ -379,7 +385,7 @@ impl<T: CopierTarget> CopyTermState<T> {
Ok(()) 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 index = addr.get_value() as usize;
let rd = self.target.deref(addr); let rd = self.target.deref(addr);
let ra = self.target.store(rd); let ra = self.target.store(rd);
@@ -419,7 +425,7 @@ impl<T: CopierTarget> CopyTermState<T> {
Ok(()) 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], read_heap_cell!(self.target[addr],
(HeapCellValueTag::Atom, (_name, arity)) => { (HeapCellValueTag::Atom, (_name, arity)) => {
let threshold = self.target.threshold(); let threshold = self.target.threshold();
@@ -429,7 +435,7 @@ impl<T: CopierTarget> CopyTermState<T> {
let str_cell = if get_structure_index(index_cell).is_some() { let str_cell = if get_structure_index(index_cell).is_some() {
// copy the index pointer trailing this // copy the index pointer trailing this
// inlined or expanded goal. // inlined or expanded goal.
let mut writer = self.target.reserve(1).unwrap(); let mut writer = self.target.reserve(1)?;
writer.write_with(|section| { writer.write_with(|section| {
section.push_cell(index_cell); section.push_cell(index_cell);
@@ -462,7 +468,7 @@ impl<T: CopierTarget> CopyTermState<T> {
Ok(()) 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(); self.scan = self.target.threshold();
let mut writer = self.target.reserve(1)?; let mut writer = self.target.reserve(1)?;
@@ -501,7 +507,7 @@ impl<T: CopierTarget> CopyTermState<T> {
Ok(()) 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() { while let Some((least_pstr_loc, pstr_data)) = self.pstr_loc_locs.pop_first() {
let threshold = heap_index!(self.target.threshold()); let threshold = heap_index!(self.target.threshold());

View File

@@ -1051,7 +1051,10 @@ impl Machine {
}); });
self.machine_st.num_of_args += 1; 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; self.machine_st.num_of_args -= 1;
} }
None => { None => {
@@ -1130,7 +1133,10 @@ impl Machine {
); );
self.machine_st.num_of_args += 1; 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; self.machine_st.num_of_args -= 1;
} }
None => { None => {
@@ -1183,7 +1189,7 @@ impl Machine {
} }
} }
&Instruction::TryMeElse(offset) => { &Instruction::TryMeElse(offset) => {
self.try_me_else(offset); backtrack_on_resource_error!(self.machine_st, self.try_me_else(offset));
} }
&Instruction::DefaultRetryMeElse(offset) => { &Instruction::DefaultRetryMeElse(offset) => {
self.retry_me_else(offset); self.retry_me_else(offset);
@@ -1265,7 +1271,10 @@ impl Machine {
self.machine_st.p += 1; self.machine_st.p += 1;
} }
&Instruction::Allocate(num_cells) => { &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 => { &Instruction::DefaultCallAcyclicTerm => {
let addr = self.deref_register(1); let addr = self.deref_register(1);
@@ -3155,7 +3164,10 @@ impl Machine {
IndexingLine::IndexedChoice(ref indexed_choice) => { IndexingLine::IndexedChoice(ref indexed_choice) => {
match indexed_choice[self.machine_st.iip as usize] { match indexed_choice[self.machine_st.iip as usize] {
IndexedChoiceInstruction::Try(offset) => { IndexedChoiceInstruction::Try(offset) => {
self.indexed_try(offset); backtrack_on_resource_error!(
self.machine_st,
self.indexed_try(offset)
);
} }
IndexedChoiceInstruction::Retry(l) => { IndexedChoiceInstruction::Retry(l) => {
self.retry(l); self.retry(l);
@@ -3208,7 +3220,10 @@ impl Machine {
); );
self.machine_st.num_of_args += 1; 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; self.machine_st.num_of_args -= 1;
} }
None => { None => {

View File

@@ -5,16 +5,25 @@ use crate::types::*;
use std::alloc; use std::alloc;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::num::NonZero;
use std::ops::{Bound, Index, IndexMut, Range, RangeBounds}; use std::ops::{Bound, Index, IndexMut, Range, RangeBounds};
use std::ptr; use std::ptr;
use std::sync::Once;
const ALIGN: usize = Heap::heap_cell_alignment(); const ALIGN: usize = Heap::heap_cell_alignment();
#[derive(Debug, Clone)]
pub struct AllocError;
impl AllocError {
pub(crate) fn resource_error_offset(&self, heap: &mut Heap) -> usize {
heap.resource_error_offset()
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct Heap { pub struct Heap {
inner: InnerHeap, inner: InnerHeap,
resource_err_loc: usize, resource_err_loc: Option<NonZero<usize>>,
} }
impl Drop for Heap { impl Drop for Heap {
@@ -85,8 +94,6 @@ impl InnerHeap {
unsafe impl Send for Heap {} unsafe impl Send for Heap {}
unsafe impl Sync for Heap {} unsafe impl Sync for Heap {}
static RESOURCE_ERROR_OFFSET_INIT: Once = Once::new();
#[derive(Debug)] #[derive(Debug)]
pub struct HeapStringScan<'a> { pub struct HeapStringScan<'a> {
pub string: &'a str, pub string: &'a str,
@@ -563,7 +570,7 @@ impl Heap {
byte_len: 0, byte_len: 0,
byte_cap: 0, byte_cap: 0,
}, },
resource_err_loc: 0, resource_err_loc: None,
} }
} }
@@ -585,9 +592,11 @@ impl Heap {
#[inline] #[inline]
fn resource_error_offset(&self) -> usize { fn resource_error_offset(&self) -> usize {
self.resource_err_loc 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<Self, usize> { pub(crate) fn with_cell_capacity(cap: usize) -> Result<Self, AllocError> {
let ptr = unsafe { let ptr = unsafe {
let layout = alloc::Layout::from_size_align( let layout = alloc::Layout::from_size_align(
cap * size_of::<HeapCellValue>(), cap * size_of::<HeapCellValue>(),
@@ -598,7 +607,7 @@ impl Heap {
}; };
if ptr.is_null() { if ptr.is_null() {
panic!("could not allocate {} bytes for heap!", heap_index!(cap)) Err(AllocError)
} else { } else {
Ok(Self { Ok(Self {
inner: InnerHeap { inner: InnerHeap {
@@ -607,12 +616,12 @@ impl Heap {
byte_cap: heap_index!(cap), byte_cap: heap_index!(cap),
}, },
// pstr_vec: bitvec![], // pstr_vec: bitvec![],
resource_err_loc: 0, resource_err_loc: None,
}) })
} }
} }
pub fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, usize> { pub fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, AllocError> {
let section; let section;
let len = heap_index!(num_cells); let len = heap_index!(num_cells);
@@ -625,7 +634,7 @@ impl Heap {
}; };
break; break;
} else if !self.grow() { } else if !self.grow() {
return Err(self.resource_error_offset()); return Err(AllocError);
} }
} }
} }
@@ -649,7 +658,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()); let other_len = heap_index!(other_heap.cell_len());
loop { loop {
@@ -665,7 +674,7 @@ impl Heap {
self.inner.byte_len += heap_index!(other_heap.cell_len()); self.inner.byte_len += heap_index!(other_heap.cell_len());
break; break;
} else if unsafe { !self.grow() } { } else if unsafe { !self.grow() } {
return Err(self.resource_error_offset()); return Err(AllocError);
} }
} }
@@ -678,26 +687,26 @@ impl Heap {
} }
pub(crate) fn clear(&mut self) { pub(crate) fn clear(&mut self) {
unsafe { *self = Heap::new();
let layout =
alloc::Layout::from_size_align(self.inner.byte_cap, size_of::<HeapCellValue>())
.unwrap();
alloc::dealloc(self.inner.ptr, layout);
}
self.inner.ptr = ptr::null_mut();
self.inner.byte_len = 0;
self.inner.byte_cap = 0;
} }
pub(crate) fn store_resource_error(&mut self) { 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!("resource_error"), [atom_as_cell((atom!("memory")))]); let stub = functor!(
self.resource_err_loc = cell_index!(self.inner.byte_len); atom!("error"),
[
functor((atom!("resource_error")), [atom_as_cell((atom!("memory")))]),
atom_as_cell((atom!("[]")))
]
);
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); let mut writer = Heap::functor_writer(stub);
writer(self).unwrap(); writer(self).unwrap();
}); }
} }
#[inline] #[inline]
@@ -742,10 +751,10 @@ impl Heap {
// either succeed & return nothing or fail & return an offset into // either succeed & return nothing or fail & return an offset into
// the heap to a pre-allocated resource error // 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 { unsafe {
if self.inner.byte_len == self.inner.byte_cap && !self.grow() { if self.inner.byte_len == self.inner.byte_cap && !self.grow() {
return Err(self.resource_error_offset()); return Err(AllocError);
} }
// SAFETY: // SAFETY:
@@ -779,7 +788,7 @@ impl Heap {
Range { start, end } Range { start, end }
} }
pub fn allocate_pstr(&mut self, src: &str) -> Result<HeapCellValue, usize> { pub fn allocate_pstr(&mut self, src: &str) -> Result<HeapCellValue, AllocError> {
let size_in_heap = Self::compute_pstr_size(src); let size_in_heap = Self::compute_pstr_size(src);
let mut writer = self.reserve(size_in_heap)?; let mut writer = self.reserve(size_in_heap)?;
let HeapSectionWriteResult { result, .. } = let HeapSectionWriteResult { result, .. } =
@@ -794,7 +803,7 @@ impl Heap {
// note that allocate_cstr emits a tail cell to the string (completing it with the empty list) // note that allocate_cstr emits a tail cell to the string (completing it with the empty list)
// unlike any version of allocate_pstr. // unlike any version of allocate_pstr.
pub fn allocate_cstr(&mut self, src: &str) -> Result<HeapCellValue, usize> { pub fn allocate_cstr(&mut self, src: &str) -> Result<HeapCellValue, AllocError> {
let size_in_heap = Self::compute_pstr_size(src); let size_in_heap = Self::compute_pstr_size(src);
let mut writer = self.reserve(size_in_heap + 1)?; let mut writer = self.reserve(size_in_heap + 1)?;
let HeapSectionWriteResult { result, .. } = let HeapSectionWriteResult { result, .. } =
@@ -849,7 +858,7 @@ impl Heap {
// copies only the string, not its tail. returns the cell index of // copies only the string, not its tail. returns the cell index of
// the tail location // the tail location
pub(crate) fn copy_pstr_within(&mut self, pstr_loc: usize) -> Result<usize, usize> { pub(crate) fn copy_pstr_within(&mut self, pstr_loc: usize) -> Result<usize, AllocError> {
let HeapStringScan { string, tail_idx } = self.scan_slice_to_str(pstr_loc); let HeapStringScan { string, tail_idx } = self.scan_slice_to_str(pstr_loc);
let s_len = string.len(); let s_len = string.len();
@@ -884,7 +893,7 @@ impl Heap {
break; break;
} else if !self.grow() { } else if !self.grow() {
return Err(self.resource_error_offset()); return Err(AllocError);
} }
} }
} }
@@ -893,7 +902,10 @@ impl Heap {
} }
// src is a cell-indexed range. // src is a cell-indexed range.
pub(crate) fn copy_slice_to_end<R: RangeBounds<usize>>(&mut self, src: R) -> Result<(), usize> { pub(crate) fn copy_slice_to_end<R: RangeBounds<usize>>(
&mut self,
src: R,
) -> Result<(), AllocError> {
let range = self.slice_range(src); let range = self.slice_range(src);
let len = range.end - range.start; let len = range.end - range.start;
@@ -911,7 +923,7 @@ impl Heap {
break; break;
} else if !self.grow() { } else if !self.grow() {
return Err(self.resource_error_offset()); return Err(AllocError);
} }
} }
} }
@@ -970,7 +982,7 @@ impl Heap {
pub(crate) fn functor_writer( pub(crate) fn functor_writer(
functor: Vec<FunctorElement>, functor: Vec<FunctorElement>,
) -> impl FnMut(&mut Heap) -> Result<HeapCellValue, usize> { ) -> impl FnMut(&mut Heap) -> Result<HeapCellValue, AllocError> {
let size = Heap::compute_functor_byte_size(&functor); let size = Heap::compute_functor_byte_size(&functor);
let mut functor_writer = ReservedHeapSection::functor_writer(functor); let mut functor_writer = ReservedHeapSection::functor_writer(functor);
@@ -1133,7 +1145,7 @@ pub fn sized_iter_to_heap_list<SrcT: Into<HeapCellValue>>(
heap: &mut Heap, heap: &mut Heap,
size: usize, size: usize,
values: impl Iterator<Item = SrcT>, values: impl Iterator<Item = SrcT>,
) -> Result<HeapCellValue, usize> { ) -> Result<HeapCellValue, AllocError> {
if size > 0 { if size > 0 {
let h = heap.cell_len(); let h = heap.cell_len();
let mut writer = heap.reserve(1 + 2 * size)?; let mut writer = heap.reserve(1 + 2 * size)?;

View File

@@ -4,6 +4,7 @@ use std::rc::Rc;
use crate::atom_table; use crate::atom_table;
use crate::heap_iter::{stackful_post_order_iter, NonListElider}; use crate::heap_iter::{stackful_post_order_iter, NonListElider};
use crate::machine::heap::AllocError;
use crate::machine::machine_indices::VarKey; use crate::machine::machine_indices::VarKey;
use crate::machine::mock_wam::CompositeOpDir; use crate::machine::mock_wam::CompositeOpDir;
use crate::machine::{ use crate::machine::{
@@ -128,9 +129,11 @@ impl Term {
pub fn try_conjunction(value: impl IntoIterator<Item = Term>) -> Option<Self> { pub fn try_conjunction(value: impl IntoIterator<Item = Term>) -> Option<Self> {
let mut iter = value.into_iter(); let mut iter = value.into_iter();
iter.next().map(|first| { iter.next().map(|first| {
Term::try_conjunction(iter) if let Some(rest) = Term::try_conjunction(iter) {
.map(|rest| Term::compound(",", [first.clone(), rest])) Term::compound(",", [first, rest])
.unwrap_or(first) } else {
first
}
}) })
} }
@@ -143,9 +146,11 @@ impl Term {
pub fn try_disjunction(value: impl IntoIterator<Item = Term>) -> Option<Self> { pub fn try_disjunction(value: impl IntoIterator<Item = Term>) -> Option<Self> {
let mut iter = value.into_iter(); let mut iter = value.into_iter();
iter.next().map(|first| { iter.next().map(|first| {
Term::try_disjunction(iter) if let Some(rest) = Term::try_disjunction(iter) {
.map(|rest| Term::compound(";", [first.clone(), rest])) Term::compound(";", [first, rest])
.unwrap_or(first) } else {
first
}
}) })
} }
} }
@@ -155,9 +160,14 @@ impl Term {
fn count_to_letter_code(mut count: usize) -> String { fn count_to_letter_code(mut count: usize) -> String {
let mut letters = Vec::new(); 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 { loop {
let letter_idx = (count % 26) as u32; let letter_idx = (count % 26) as u8;
letters.push(char::from_u32('A' as u32 + letter_idx).unwrap()); letters.push(b'A' + letter_idx);
count /= 26; count /= 26;
if count == 0 { if count == 0 {
@@ -165,7 +175,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 { impl Term {
@@ -207,14 +225,14 @@ impl Term {
let list = match tail { let list = match tail {
Term::Atom(atom) if atom == "[]" => match head { Term::Atom(atom) if atom == "[]" => match head {
Term::Atom(ref a) if a.chars().collect::<Vec<_>>().len() == 1 => { Term::Atom(ref a) if a.chars().count() == 1 => {
// Handle lists of char as strings // Handle lists of char as strings
Term::String(a.to_string()) Term::String(a.to_string())
} }
_ => Term::List(vec![head]), _ => Term::List(vec![head]),
}, },
Term::List(elems) if elems.is_empty() => match head { Term::List(elems) if elems.is_empty() => match head {
Term::Atom(ref a) if a.chars().collect::<Vec<_>>().len() == 1 => { Term::Atom(ref a) if a.chars().count() == 1 => {
// Handle lists of char as strings // Handle lists of char as strings
Term::String(a.to_string()) Term::String(a.to_string())
}, },
@@ -225,7 +243,7 @@ impl Term {
Term::List(elems) Term::List(elems)
}, },
Term::String(mut elems) => match head { Term::String(mut elems) => match head {
Term::Atom(ref a) if a.chars().collect::<Vec<_>>().len() == 1 => { Term::Atom(ref a) if a.chars().count() == 1 => {
// Handle lists of char as strings // Handle lists of char as strings
elems.insert(0, a.chars().next().unwrap()); elems.insert(0, a.chars().next().unwrap());
Term::String(elems) Term::String(elems)
@@ -428,14 +446,15 @@ impl Iterator for QueryState<'_> {
// contained in self.machine_st.ball. // contained in self.machine_st.ball.
let h = machine.machine_st.heap.cell_len(); let h = machine.machine_st.heap.cell_len();
if let Err(resource_err_loc) = machine if let Err(err) = machine
.machine_st .machine_st
.heap .heap
.append(&machine.machine_st.ball.stub) .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( return Some(Err(Term::from_heapcell(
machine, machine,
machine.machine_st.heap[resource_err_loc], machine.machine_st.heap[resource_error_offset],
&mut IndexMap::new(), &mut IndexMap::new(),
))); )));
} }
@@ -536,11 +555,11 @@ impl Machine {
self.run_module_predicate(atom!("loader"), (atom!("consult_stream"), 2)); 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 // NOTE: create a choice point to terminate the dispatch_loop
// if an exception is thrown. // 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); let or_frame = self.machine_st.stack.index_or_frame_mut(stub_b);
or_frame.prelude.num_cells = 0; or_frame.prelude.num_cells = 0;
@@ -558,6 +577,8 @@ impl Machine {
self.machine_st.b = stub_b; self.machine_st.b = stub_b;
self.machine_st.hb = self.machine_st.heap.cell_len(); self.machine_st.hb = self.machine_st.heap.cell_len();
self.machine_st.block = stub_b; self.machine_st.block = stub_b;
Ok(())
} }
/// Runs a query. /// Runs a query.
@@ -571,7 +592,8 @@ impl Machine {
.read_term(&op_dir, Tokens::Default) .read_term(&op_dir, Tokens::Default)
.expect("Failed to parse query"); .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 // Write parsed term to heap
let term_write_result = write_term_to_heap(&term, &mut self.machine_st.heap) let term_write_result = write_term_to_heap(&term, &mut self.machine_st.heap)
@@ -619,3 +641,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);
}
}

View File

@@ -1842,9 +1842,7 @@ impl Machine {
let err = self.machine_st.permission_error( let err = self.machine_st.permission_error(
Permission::Modify, Permission::Modify,
atom!("static_procedure"), atom!("static_procedure"),
functor_stub(atom!(":"), 2) functor_stub(atom!(":"), 2),
.into_iter()
.collect::<MachineStub>(),
); );
self.machine_st self.machine_st
@@ -2361,8 +2359,8 @@ impl Machine {
let mut writer = match self.machine_st.heap.reserve(3 + meta_specs.len()) { let mut writer = match self.machine_st.heap.reserve(3 + meta_specs.len()) {
Ok(writer) => writer, Ok(writer) => writer,
Err(err_loc) => { Err(err) => {
self.machine_st.throw_resource_error(err_loc); self.machine_st.throw_resource_error(err);
return; return;
} }
}; };

View File

@@ -6,6 +6,7 @@ use crate::parser::ast::*;
use crate::ffi::{self, FfiError}; use crate::ffi::{self, FfiError};
use crate::forms::*; use crate::forms::*;
use crate::functor_macro::*; use crate::functor_macro::*;
use crate::machine::heap::AllocError;
use crate::machine::heap::*; use crate::machine::heap::*;
use crate::machine::loader::CompilationTarget; use crate::machine::loader::CompilationTarget;
use crate::machine::machine_state::*; use crate::machine::machine_state::*;
@@ -645,6 +646,19 @@ impl MachineState {
return self.directive_error(err); 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 location = err.line_and_col_num();
let stub = err.as_functor(); let stub = err.as_functor();
@@ -763,8 +777,8 @@ impl MachineState {
} }
// throw an error pre-allocated in the heap // throw an error pre-allocated in the heap
pub(super) fn throw_resource_error(&mut self, err_loc: usize) { pub(super) fn throw_resource_error(&mut self, err: AllocError) {
self.registers[1] = str_loc_as_cell!(err_loc); self.registers[1] = str_loc_as_cell!(err.resource_error_offset(&mut self.heap));
self.set_ball(); self.set_ball();
self.unwind_stack(); self.unwind_stack();
} }
@@ -777,8 +791,8 @@ impl MachineState {
self.registers[1] = match writer(&mut self.heap) { self.registers[1] = match writer(&mut self.heap) {
Ok(loc) => loc, Ok(loc) => loc,
Err(resource_err_loc) => { Err(err) => {
self.throw_resource_error(resource_err_loc); self.throw_resource_error(err);
return; return;
} }
}; };
@@ -802,7 +816,13 @@ pub enum CompilationError {
InvalidRuleHead, InvalidRuleHead,
InvalidUseModuleDecl, InvalidUseModuleDecl,
InvalidModuleResolution(Atom), InvalidModuleResolution(Atom),
FiniteMemoryInHeap(usize), FiniteMemoryInHeap(AllocError),
}
impl From<AllocError> for CompilationError {
fn from(value: AllocError) -> Self {
Self::FiniteMemoryInHeap(value)
}
} }
#[derive(Debug)] #[derive(Debug)]
@@ -878,8 +898,8 @@ impl CompilationError {
CompilationError::ParserError(ref err) => { CompilationError::ParserError(ref err) => {
functor!(err.as_atom()) functor!(err.as_atom())
} }
CompilationError::FiniteMemoryInHeap(h) => { CompilationError::FiniteMemoryInHeap(_) => {
vec![FunctorElement::AbsoluteCell(str_loc_as_cell!(*h))] functor!(atom!("resource_error"))
} }
} }
} }

View File

@@ -1,4 +1,5 @@
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work #![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::*; use crate::parser::ast::*;

View File

@@ -5,6 +5,7 @@ use crate::heap_iter::*;
use crate::heap_print::*; use crate::heap_print::*;
use crate::machine::attributed_variables::*; use crate::machine::attributed_variables::*;
use crate::machine::copier::*; use crate::machine::copier::*;
use crate::machine::heap::AllocError;
use crate::machine::heap::*; use crate::machine::heap::*;
use crate::machine::machine_errors::*; use crate::machine::machine_errors::*;
use crate::machine::machine_indices::*; use crate::machine::machine_indices::*;
@@ -192,7 +193,7 @@ fn push_var_eq_functors<'a>(
size: usize, size: usize,
iter: impl Iterator<Item = (&'a VarKey, &'a HeapCellValue)>, iter: impl Iterator<Item = (&'a VarKey, &'a HeapCellValue)>,
atom_tbl: &AtomTable, atom_tbl: &AtomTable,
) -> Result<HeapCellValue, usize> { ) -> Result<HeapCellValue, AllocError> {
let src_h = heap.cell_len(); let src_h = heap.cell_len();
let true_size = if size > 0 { let true_size = if size > 0 {
@@ -257,7 +258,7 @@ impl Ball {
self.stub.clear(); self.stub.clear();
} }
pub(super) fn copy_and_align_to(&self, dest: &mut Heap) -> Result<usize, usize> { pub(super) fn copy_and_align_to(&self, dest: &mut Heap) -> Result<usize, AllocError> {
let h = dest.cell_len(); let h = dest.cell_len();
let diff = self.boundary as i64 - h as i64; let diff = self.boundary as i64 - h as i64;
@@ -346,17 +347,17 @@ impl<'a> CopierTarget for CopyTerm<'a> {
} }
#[inline(always)] #[inline(always)]
fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result<usize, usize> { fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result<usize, AllocError> {
self.state.heap.copy_pstr_within(pstr_loc) self.state.heap.copy_pstr_within(pstr_loc)
} }
#[inline(always)] #[inline(always)]
fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, usize> { fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, AllocError> {
self.state.heap.reserve(num_cells) self.state.heap.reserve(num_cells)
} }
#[inline(always)] #[inline(always)]
fn copy_slice_to_end(&mut self, bounds: Range<usize>) -> Result<(), usize> { fn copy_slice_to_end(&mut self, bounds: Range<usize>) -> Result<(), AllocError> {
self.state.heap.copy_slice_to_end(bounds) self.state.heap.copy_slice_to_end(bounds)
} }
} }
@@ -455,7 +456,7 @@ impl<'a> CopierTarget for CopyBallTerm<'a> {
self.stack self.stack
} }
fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result<usize, usize> { fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result<usize, AllocError> {
debug_assert!(pstr_loc < self.heap.byte_len()); debug_assert!(pstr_loc < self.heap.byte_len());
let HeapStringScan { string, tail_idx } = self.heap.scan_slice_to_str(pstr_loc); let HeapStringScan { string, tail_idx } = self.heap.scan_slice_to_str(pstr_loc);
@@ -477,11 +478,11 @@ impl<'a> CopierTarget for CopyBallTerm<'a> {
} }
#[inline] #[inline]
fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, usize> { fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, AllocError> {
self.stub.reserve(num_cells) self.stub.reserve(num_cells)
} }
fn copy_slice_to_end(&mut self, bounds: Range<usize>) -> Result<(), usize> { fn copy_slice_to_end(&mut self, bounds: Range<usize>) -> Result<(), AllocError> {
let len = bounds.end - bounds.start; let len = bounds.end - bounds.start;
let mut stub_writer = self.stub.reserve(len)?; let mut stub_writer = self.stub.reserve(len)?;

View File

@@ -4,6 +4,7 @@ use crate::forms::*;
use crate::heap_iter::*; use crate::heap_iter::*;
use crate::machine::attributed_variables::*; use crate::machine::attributed_variables::*;
use crate::machine::copier::*; use crate::machine::copier::*;
use crate::machine::heap::AllocError;
use crate::machine::heap::*; use crate::machine::heap::*;
use crate::machine::machine_errors::*; use crate::machine::machine_errors::*;
use crate::machine::machine_indices::*; use crate::machine::machine_indices::*;
@@ -30,8 +31,8 @@ impl MachineState {
heap.store_resource_error(); heap.store_resource_error();
MachineState { MachineState {
arena: Arena::new(), arena: Arena::new().unwrap(),
atom_tbl: AtomTable::new(), atom_tbl: AtomTable::new().unwrap(),
pdl: Vec::with_capacity(1024), pdl: Vec::with_capacity(1024),
s: HeapPtr::default(), s: HeapPtr::default(),
s_offset: 0, s_offset: 0,
@@ -47,7 +48,7 @@ impl MachineState {
fail: false, fail: false,
heap, heap,
mode: MachineMode::Write, 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. registers: [heap_loc_as_cell!(0); MAX_ARITY + 1], // self.registers[0] is never used.
trail: vec![], trail: vec![],
tr: 0, tr: 0,
@@ -174,8 +175,8 @@ impl MachineState {
} }
} }
pub fn allocate(&mut self, num_cells: usize) { pub fn allocate(&mut self, num_cells: usize) -> Result<(), AllocError> {
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); let and_frame = self.stack.index_and_frame_mut(e);
and_frame.prelude.e = self.e; and_frame.prelude.e = self.e;
@@ -183,6 +184,8 @@ impl MachineState {
self.e = e; self.e = e;
self.p += 1; self.p += 1;
Ok(())
} }
pub fn bind(&mut self, r1: Ref, a2: HeapCellValue) { pub fn bind(&mut self, r1: Ref, a2: HeapCellValue) {
@@ -922,7 +925,7 @@ impl MachineState {
name: Atom, name: Atom,
arity: usize, arity: usize,
r: Ref, r: Ref,
) -> Result<(), usize> { ) -> Result<(), AllocError> {
let h = self.heap.cell_len(); let h = self.heap.cell_len();
let mut writer = self.heap.reserve(arity + 1)?; let mut writer = self.heap.reserve(arity + 1)?;

View File

@@ -168,7 +168,7 @@ impl<'a> CopierTarget for TermCopyingMockWAM<'a> {
} }
#[inline(always)] #[inline(always)]
fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result<usize, usize> { fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result<usize, AllocError> {
self.wam.machine_st.heap.copy_pstr_within(pstr_loc) self.wam.machine_st.heap.copy_pstr_within(pstr_loc)
} }
@@ -178,12 +178,12 @@ impl<'a> CopierTarget for TermCopyingMockWAM<'a> {
} }
#[inline(always)] #[inline(always)]
fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, usize> { fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, AllocError> {
self.wam.machine_st.heap.reserve(num_cells) self.wam.machine_st.heap.reserve(num_cells)
} }
#[inline(always)] #[inline(always)]
fn copy_slice_to_end(&mut self, bounds: Range<usize>) -> Result<(), usize> { fn copy_slice_to_end(&mut self, bounds: Range<usize>) -> Result<(), AllocError> {
self.wam.machine_st.heap.copy_slice_to_end(bounds) self.wam.machine_st.heap.copy_slice_to_end(bounds)
} }
} }

View File

@@ -38,6 +38,7 @@ use crate::instructions::*;
use crate::machine::args::*; use crate::machine::args::*;
use crate::machine::compile::*; use crate::machine::compile::*;
use crate::machine::copier::*; use crate::machine::copier::*;
use crate::machine::heap::AllocError;
use crate::machine::heap::*; use crate::machine::heap::*;
use crate::machine::loader::*; use crate::machine::loader::*;
use crate::machine::machine_errors::*; use crate::machine::machine_errors::*;
@@ -60,6 +61,7 @@ use std::cmp::Ordering;
use std::env; use std::env;
use std::io::Read; use std::io::Read;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::ExitCode;
use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicBool;
use std::sync::OnceLock; use std::sync::OnceLock;
@@ -261,7 +263,9 @@ impl Machine {
let p = index_ptr.local().unwrap(); let p = index_ptr.local().unwrap();
// Leave a halting choice point to backtrack to in case the predicate fails or throws. // 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.cp = BREAK_FROM_DISPATCH_LOOP_LOC;
self.machine_st.p = p; self.machine_st.p = p;
@@ -729,10 +733,10 @@ impl Machine {
} }
#[inline(always)] #[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) { if let Some(offset) = self.next_applicable_clause(offset) {
let n = self.machine_st.num_of_args; 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); let or_frame = self.machine_st.stack.index_or_frame_mut(b);
or_frame.prelude.num_cells = n; or_frame.prelude.num_cells = n;
@@ -758,13 +762,15 @@ impl Machine {
} }
self.machine_st.p += 1; self.machine_st.p += 1;
Ok(())
} }
#[inline(always)] #[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() { if let Some(iip_offset) = self.next_inner_applicable_clause() {
let n = self.machine_st.num_of_args; 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); let or_frame = self.machine_st.stack.index_or_frame_mut(b);
or_frame.prelude.num_cells = n; or_frame.prelude.num_cells = n;
@@ -793,6 +799,7 @@ impl Machine {
} }
self.machine_st.p += offset; self.machine_st.p += offset;
Ok(())
} }
#[inline(always)] #[inline(always)]

View File

@@ -217,6 +217,8 @@ mod test {
fn pstr_iter_tests() { fn pstr_iter_tests() {
let mut wam = MockWAM::new(); 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(); let pstr_cell = wam.machine_st.heap.allocate_pstr("abc ").unwrap();
wam.machine_st wam.machine_st
.heap .heap
@@ -233,7 +235,7 @@ mod test {
assert_eq!( assert_eq!(
iter.next(), iter.next(),
Some(PStrIteratee::PStrSlice { Some(PStrIteratee::PStrSlice {
slice_loc: heap_index!(1), slice_loc: heap_index!(init_len),
slice_len: "abc ".len() slice_len: "abc ".len()
}), }),
); );
@@ -241,9 +243,9 @@ mod test {
assert!(!iter.is_cyclic()); 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(); wam.machine_st.heap.allocate_pstr("def").unwrap();
let h = wam.machine_st.heap.cell_len(); let h = wam.machine_st.heap.cell_len();
@@ -256,14 +258,14 @@ mod test {
assert_eq!( assert_eq!(
iter.next(), iter.next(),
Some(PStrIteratee::PStrSlice { Some(PStrIteratee::PStrSlice {
slice_loc: heap_index!(1), slice_loc: heap_index!(init_len),
slice_len: "abc ".len() slice_len: "abc ".len()
}) })
); );
assert_eq!( assert_eq!(
iter.next(), iter.next(),
Some(PStrIteratee::PStrSlice { Some(PStrIteratee::PStrSlice {
slice_loc: heap_index!(3), slice_loc: heap_index!(init_len + 2),
slice_len: "def".len(), slice_len: "def".len(),
}) })
); );
@@ -282,14 +284,14 @@ mod test {
assert_eq!( assert_eq!(
iter.next(), iter.next(),
Some(PStrIteratee::PStrSlice { Some(PStrIteratee::PStrSlice {
slice_loc: heap_index!(1), slice_loc: heap_index!(init_len),
slice_len: "abc ".len() slice_len: "abc ".len()
}) })
); );
assert_eq!( assert_eq!(
iter.next(), iter.next(),
Some(PStrIteratee::PStrSlice { Some(PStrIteratee::PStrSlice {
slice_loc: heap_index!(3), slice_loc: heap_index!(init_len + 2),
slice_len: "def".len(), slice_len: "def".len(),
}) })
); );
@@ -298,7 +300,7 @@ mod test {
assert!(!iter.is_cyclic()); 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); let mut iter = HeapPStrIter::new(&wam.machine_st.heap, 0);

View File

@@ -1,5 +1,7 @@
use core::marker::PhantomData; use core::marker::PhantomData;
use std::ptr::NonNull;
use crate::machine::heap::AllocError;
use crate::raw_block::*; use crate::raw_block::*;
use crate::types::*; use crate::types::*;
@@ -159,45 +161,42 @@ impl OrFrame {
} }
impl Stack { impl Stack {
pub(crate) fn new() -> Self { pub(crate) fn new() -> Result<Self, AllocError> {
Stack { Ok(Stack {
buf: RawBlock::new(), buf: RawBlock::new()?,
_marker: PhantomData, _marker: PhantomData,
} })
} }
#[inline(always)] #[inline(always)]
unsafe fn alloc(&mut self, frame_size: usize) -> *mut u8 { unsafe fn alloc(&mut self, frame_size: usize) -> Result<NonNull<u8>, AllocError> {
loop { loop {
let ptr = self.buf.alloc(frame_size); let ptr = self.buf.alloc(frame_size);
if let Some(ptr) = NonNull::new(ptr) {
if ptr.is_null() { return Ok(ptr);
if !self.buf.grow() {
panic!("growing the stack failed")
}
} else {
return 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<usize, AllocError> {
let frame_size = AndFrame::size_of(num_cells); let frame_size = AndFrame::size_of(num_cells);
unsafe { unsafe {
let e = (*self.buf.ptr.get_mut()).addr() - self.buf.base.addr(); 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::<AndFramePrelude>(); let mut offset = prelude_size::<AndFramePrelude>();
for idx in 0..num_cells { for idx in 0..num_cells {
let cell_ptr = new_ptr.add(offset) as *mut HeapCellValue; let cell_ptr = new_ptr.add(offset).cast::<HeapCellValue>();
ptr::write(cell_ptr, stack_loc_as_cell!(AndFrame, e, idx + 1)); 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 // 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 // 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` // 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. // 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::<HeapCellValue>(); offset += mem::size_of::<HeapCellValue>();
} }
@@ -205,7 +204,7 @@ impl Stack {
let and_frame = self.index_and_frame_mut(e); let and_frame = self.index_and_frame_mut(e);
and_frame.prelude.num_cells = num_cells; 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() } 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<usize, AllocError> {
let frame_size = OrFrame::size_of(num_cells); let frame_size = OrFrame::size_of(num_cells);
unsafe { unsafe {
let b = (*self.buf.ptr.get_mut()).addr() - self.buf.base.addr(); 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::<OrFramePrelude>(); let mut offset = prelude_size::<OrFramePrelude>();
for idx in 0..num_cells { for idx in 0..num_cells {
let cell_ptr = new_ptr.byte_add(offset) as *mut HeapCellValue; let cell_ptr = new_ptr.byte_add(offset).cast::<HeapCellValue>();
ptr::write(cell_ptr, stack_loc_as_cell!(OrFrame, b, idx)); 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 // 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 // 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` // 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. // 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::<HeapCellValue>(); offset += mem::size_of::<HeapCellValue>();
} }
@@ -237,7 +237,7 @@ impl Stack {
let or_frame = self.index_or_frame_mut(b); let or_frame = self.index_or_frame_mut(b);
or_frame.prelude.num_cells = num_cells; or_frame.prelude.num_cells = num_cells;
b Ok(b)
} }
} }
@@ -285,7 +285,7 @@ mod tests {
fn stack_tests() { fn stack_tests() {
let mut wam = MockWAM::new(); 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); let and_frame = wam.machine_st.stack.index_and_frame_mut(e);
assert_eq!( assert_eq!(
@@ -303,7 +303,7 @@ mod tests {
assert_eq!(and_frame[5], empty_list_as_cell!()); 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); 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)); 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); let and_frame = wam.machine_st.stack.index_and_frame_mut(next_e);
for idx in 0..9 { for idx in 0..9 {

View File

@@ -1,3 +1,5 @@
#![allow(unused_parens)] // see mthom/scryer-prolog#3092 and rust-lang/rust#147126
use crate::arena::*; use crate::arena::*;
use crate::atom_table::*; use crate::atom_table::*;
use crate::functor_macro::*; use crate::functor_macro::*;

View File

@@ -65,6 +65,8 @@ mod lt_1_87_0 {
#[cfg(rust_version = "1.87.0")] #[cfg(rust_version = "1.87.0")]
mod ge_1_87_0 { mod ge_1_87_0 {
#![allow(clippy::incompatible_msrv)]
pub type PipeReader = std::io::PipeReader; pub type PipeReader = std::io::PipeReader;
pub type PipeWriter = std::io::PipeWriter; pub type PipeWriter = std::io::PipeWriter;
} }

View File

@@ -17,6 +17,7 @@ use crate::instructions::*;
use crate::machine; use crate::machine;
use crate::machine::code_walker::*; use crate::machine::code_walker::*;
use crate::machine::copier::*; use crate::machine::copier::*;
use crate::machine::heap::AllocError;
use crate::machine::heap::*; use crate::machine::heap::*;
use crate::machine::machine_errors::*; use crate::machine::machine_errors::*;
use crate::machine::machine_indices::*; use crate::machine::machine_indices::*;
@@ -635,7 +636,7 @@ impl MachineState {
pub(crate) fn get_attr_var_list( pub(crate) fn get_attr_var_list(
&mut self, &mut self,
attr_var: HeapCellValue, attr_var: HeapCellValue,
) -> Result<Option<usize>, usize> { ) -> Result<Option<usize>, AllocError> {
read_heap_cell!(attr_var, read_heap_cell!(attr_var,
(HeapCellValueTag::AttrVar, h) => { (HeapCellValueTag::AttrVar, h) => {
Ok(Some(h + 1)) Ok(Some(h + 1))
@@ -903,7 +904,7 @@ impl MachineState {
&mut self, &mut self,
lh_offset: usize, lh_offset: usize,
copy_target: HeapCellValue, copy_target: HeapCellValue,
) -> Result<FindallCopyInfo, usize> { ) -> Result<FindallCopyInfo, AllocError> {
let threshold = self.lifted_heap.cell_len() - lh_offset; let threshold = self.lifted_heap.cell_len() - lh_offset;
let mut writer = self.lifted_heap.reserve(5)?; let mut writer = self.lifted_heap.reserve(5)?;
@@ -1041,7 +1042,7 @@ impl MachineState {
&mut self, &mut self,
chunk: HeapCellValue, chunk: HeapCellValue,
return_p: usize, return_p: usize,
) -> usize { ) -> Result<usize, AllocError> {
let chunk = self.store(self.deref(chunk)); let chunk = self.store(self.deref(chunk));
let s = chunk.get_value() as usize; 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 cp = to_local_code_ptr(&self.heap, p_functor).unwrap();
let prev_e = self.e; 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); let and_frame = self.stack.index_and_frame_mut(e);
and_frame.prelude.e = prev_e; and_frame.prelude.e = prev_e;
@@ -1084,7 +1085,7 @@ impl MachineState {
} }
self.e = e; self.e = e;
self.p Ok(self.p)
} }
pub fn value_to_str_like(&mut self, value: HeapCellValue) -> Option<AtomOrString> { pub fn value_to_str_like(&mut self, value: HeapCellValue) -> Option<AtomOrString> {
@@ -2458,7 +2459,14 @@ impl Machine {
self.machine_st.p = return_p; self.machine_st.p = return_p;
for chunk in cont_chunks.into_iter().rev() { 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(()) Ok(())
@@ -3303,7 +3311,7 @@ impl Machine {
bytes.push(c as u8); bytes.push(c as u8);
} }
} else { } else {
bytes = string.as_str().bytes().collect(); bytes = string.as_str().as_bytes().to_vec();
} }
match stream.write_all(&bytes) { match stream.write_all(&bytes) {
@@ -4092,7 +4100,7 @@ impl Machine {
fn write_op_functors_to_heap( fn write_op_functors_to_heap(
heap: &mut Heap, heap: &mut Heap,
op_descs: impl Iterator<Item = (Atom, OpDesc)>, op_descs: impl Iterator<Item = (Atom, OpDesc)>,
) -> Result<usize, usize> { ) -> Result<usize, AllocError> {
let mut num_functors = 0; let mut num_functors = 0;
for (name, op_desc) in op_descs { for (name, op_desc) in op_descs {
@@ -4420,7 +4428,7 @@ impl Machine {
let address_data = self.deref_register(5); let address_data = self.deref_register(5);
let mut bytes: Vec<u8> = Vec::new(); let mut bytes: Vec<u8> = Vec::new();
if let Some(string) = self.machine_st.value_to_str_like(address_data) { 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); let stub_gen = || functor_stub(atom!("http_open"), 3);
@@ -5130,7 +5138,11 @@ impl Machine {
} }
#[cfg(feature = "ffi")] #[cfg(feature = "ffi")]
fn build_struct(&mut self, name: Atom, mut args: Vec<Value>) -> Result<HeapCellValue, usize> { fn build_struct(
&mut self,
name: Atom,
mut args: Vec<Value>,
) -> Result<HeapCellValue, AllocError> {
args.insert(0, Value::CString(CString::new(&*name.as_str()).unwrap())); args.insert(0, Value::CString(CString::new(&*name.as_str()).unwrap()));
let cells: Vec<_> = args let cells: Vec<_> = args
@@ -5150,7 +5162,7 @@ impl Machine {
Value::Struct(name, struct_args) => self.build_struct(name, struct_args)?, Value::Struct(name, struct_args) => self.build_struct(name, struct_args)?,
}) })
}) })
.collect::<Result<_, usize>>()?; .collect::<Result<_, AllocError>>()?;
sized_iter_to_heap_list(&mut self.machine_st.heap, cells.len(), cells.into_iter()) sized_iter_to_heap_list(&mut self.machine_st.heap, cells.len(), cells.into_iter())
} }
@@ -7579,7 +7591,7 @@ impl Machine {
false false
} }
fn walk_code_at_ptr(&mut self, index_ptr: usize) -> Result<HeapCellValue, usize> { fn walk_code_at_ptr(&mut self, index_ptr: usize) -> Result<HeapCellValue, AllocError> {
let orig_h = self.machine_st.heap.cell_len(); let orig_h = self.machine_st.heap.cell_len();
let mut h = orig_h; let mut h = orig_h;
@@ -8400,7 +8412,7 @@ impl Machine {
} }
#[inline(always)] #[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 if let Some(string) = self
.machine_st .machine_st
.value_to_str_like(self.machine_st.registers[1]) .value_to_str_like(self.machine_st.registers[1])
@@ -8429,7 +8441,7 @@ impl Machine {
} }
#[inline(always)] #[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 if let Some(string) = self
.machine_st .machine_st
.value_to_str_like(self.machine_st.registers[1]) .value_to_str_like(self.machine_st.registers[1])
@@ -8965,8 +8977,8 @@ impl Machine {
Ok(loc) => { Ok(loc) => {
unify!(self.machine_st, status_r, loc); unify!(self.machine_st, status_r, loc);
} }
Err(resource_err_loc) => { Err(err) => {
self.machine_st.throw_resource_error(resource_err_loc); self.machine_st.throw_resource_error(err);
} }
} }
Ok(()) Ok(())
@@ -8983,8 +8995,8 @@ impl Machine {
Ok(loc) => { Ok(loc) => {
unify!(self.machine_st, status_r, loc); unify!(self.machine_st, status_r, loc);
} }
Err(resource_err_loc) => { Err(err) => {
self.machine_st.throw_resource_error(resource_err_loc); self.machine_st.throw_resource_error(err);
} }
} }
Ok(()) Ok(())
@@ -9309,7 +9321,7 @@ impl Machine {
let data = self.machine_st.value_to_str_like(data_arg).unwrap(); let data = self.machine_st.value_to_str_like(data_arg).unwrap();
match encoding { 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(), atom!("octet") => data.as_str().chars().map(|c| c as u8).collect(),
_ => { _ => {
unreachable!() unreachable!()
@@ -9320,7 +9332,7 @@ impl Machine {
pub(super) fn xml_node_to_term( pub(super) fn xml_node_to_term(
&mut self, &mut self,
node: roxmltree::Node, node: roxmltree::Node,
) -> Result<HeapCellValue, usize> { ) -> Result<HeapCellValue, AllocError> {
if node.is_text() { if node.is_text() {
self.machine_st.heap.allocate_cstr(node.text().unwrap()) self.machine_st.heap.allocate_cstr(node.text().unwrap())
} else { } else {
@@ -9371,7 +9383,7 @@ impl Machine {
pub(super) fn html_node_to_term( pub(super) fn html_node_to_term(
&mut self, &mut self,
node: ego_tree::NodeRef<'_, scraper::Node>, node: ego_tree::NodeRef<'_, scraper::Node>,
) -> Result<HeapCellValue, usize> { ) -> Result<HeapCellValue, AllocError> {
match node.value() { match node.value() {
scraper::Node::Document | scraper::Node::Fragment => { scraper::Node::Document | scraper::Node::Fragment => {
unreachable!("we never iterate the root itself only its children") 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<HeapCellValue, usize> { pub(super) fn u8s_to_string(&mut self, data: &[u8]) -> Result<HeapCellValue, AllocError> {
let buffer = String::from_iter(data.iter().map(|b| *b as char)); let buffer = String::from_iter(data.iter().map(|b| *b as char));
if buffer.is_empty() { if buffer.is_empty() {

View File

@@ -451,8 +451,8 @@ macro_rules! step_or_resource_error {
($machine_st:expr, $val:expr) => {{ ($machine_st:expr, $val:expr) => {{
match $val { match $val {
Ok(r) => r, Ok(r) => r,
Err(err_loc) => { Err(err) => {
$machine_st.throw_resource_error(err_loc); $machine_st.throw_resource_error(err);
return; return;
} }
} }
@@ -460,8 +460,8 @@ macro_rules! step_or_resource_error {
($machine_st:expr, $val:expr, $fail:block) => {{ ($machine_st:expr, $val:expr, $fail:block) => {{
match $val { match $val {
Ok(r) => r, Ok(r) => r,
Err(err_loc) => { Err(err) => {
$machine_st.throw_resource_error(err_loc); $machine_st.throw_resource_error(err);
$fail $fail
} }
} }
@@ -484,6 +484,6 @@ macro_rules! heap_index {
macro_rules! cell_index { macro_rules! cell_index {
($idx:expr) => { ($idx:expr) => {
(($idx) / std::mem::size_of::<HeapCellValue>()) ($idx) / std::mem::size_of::<HeapCellValue>()
}; };
} }

View File

@@ -10,6 +10,7 @@ use fxhash::FxBuildHasher;
use indexmap::IndexMap; use indexmap::IndexMap;
use parking_lot::{Mutex, RwLock}; use parking_lot::{Mutex, RwLock};
use crate::machine::heap::AllocError;
use crate::machine::machine_indices::IndexPtr; use crate::machine::machine_indices::IndexPtr;
use crate::raw_block::RawBlock; use crate::raw_block::RawBlock;
use crate::raw_block::RawBlockTraits; use crate::raw_block::RawBlockTraits;
@@ -58,8 +59,10 @@ impl<T: RawBlockTraits> From<Arc<ConcurrentOffsetTable<T>>> for OffsetTableImpl<
impl<T: fmt::Debug + RawBlockTraits> OffsetTableImpl<T> { impl<T: fmt::Debug + RawBlockTraits> OffsetTableImpl<T> {
#[inline(always)] #[inline(always)]
pub fn new() -> Self { pub fn new() -> Result<Self, AllocError> {
Self(InnerOffsetTableImpl::Serial(SerialOffsetTable::new())) Ok(Self(
InnerOffsetTableImpl::Serial(SerialOffsetTable::new()?),
))
} }
#[must_use = "the returned concurrent table must be absorbed into the owned OffsetTable"] #[must_use = "the returned concurrent table must be absorbed into the owned OffsetTable"]
@@ -116,7 +119,7 @@ impl<T: fmt::Debug + RawBlockTraits> OffsetTableImpl<T> {
impl<T: fmt::Debug + RawBlockTraits> Default for OffsetTableImpl<T> { impl<T: fmt::Debug + RawBlockTraits> Default for OffsetTableImpl<T> {
fn default() -> Self { fn default() -> Self {
Self::new() Self::new().unwrap()
} }
} }
@@ -201,10 +204,10 @@ impl OffsetTable<IndexPtr> for OffsetTableImpl<IndexPtr> {
impl<T: RawBlockTraits> SerialOffsetTable<T> { impl<T: RawBlockTraits> SerialOffsetTable<T> {
#[inline] #[inline]
fn new() -> Self { fn new() -> Result<Self, AllocError> {
Self { Ok(Self {
block: RawBlock::new(), block: RawBlock::new()?,
} })
} }
unsafe fn build_with(&mut self, value: T) -> usize { unsafe fn build_with(&mut self, value: T) -> usize {
@@ -374,11 +377,11 @@ pub enum F64Table {
} }
impl F64Table { impl F64Table {
pub fn new() -> Self { pub fn new() -> Result<Self, AllocError> {
Self::Serial(SerialF64Table { Ok(Self::Serial(SerialF64Table {
indirection_tbl: IndexMap::with_hasher(FxBuildHasher::new()), indirection_tbl: IndexMap::with_hasher(FxBuildHasher::new()),
offset_tbl: SerialOffsetTable::new(), offset_tbl: SerialOffsetTable::new()?,
}) }))
} }
pub fn build_with(&mut self, value: OrderedFloat<f64>) -> F64Offset { pub fn build_with(&mut self, value: OrderedFloat<f64>) -> F64Offset {

View File

@@ -1,4 +1,5 @@
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work #![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::arena::*;
use crate::atom_table::*; use crate::atom_table::*;

View File

@@ -4,6 +4,8 @@ use std::alloc;
use std::cell::UnsafeCell; use std::cell::UnsafeCell;
use std::ptr; use std::ptr;
use crate::machine::heap::AllocError;
pub trait RawBlockTraits { pub trait RawBlockTraits {
fn init_size() -> usize; fn init_size() -> usize;
fn align() -> usize; fn align() -> usize;
@@ -29,65 +31,57 @@ impl<T: RawBlockTraits> RawBlock<T> {
} }
#[allow(clippy::new_without_default)] #[allow(clippy::new_without_default)]
pub fn new() -> Self { pub fn new() -> Result<Self, AllocError> {
let mut block = Self::empty_block(); let mut block = Self::empty_block();
unsafe { 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 layout = alloc::Layout::from_size_align_unchecked(cap, T::align());
let new_base = alloc::alloc(layout).cast_const(); let new_base = alloc::alloc(layout).cast_const();
if new_base.is_null() { if new_base.is_null() {
panic!( return Err(AllocError);
"failed to allocate in init_at_size for {}",
std::any::type_name::<Self>()
);
} }
self.base = new_base; self.base = new_base;
self.top = self.base.add(cap); self.top = self.base.add(cap);
*self.ptr.get_mut() = self.base.cast_mut(); *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() { if self.base.is_null() {
self.init_at_size(T::init_size()); self.init_at_size(T::init_size())
true
} else { } else {
let size = self.size(); let size = self.size();
let layout = alloc::Layout::from_size_align_unchecked(size, T::align()); 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(); let new_base = alloc::realloc(self.base.cast_mut(), layout, size * 2).cast_const();
if new_base.is_null() { if new_base.is_null() {
false Err(AllocError)
} else { } else {
self.base = new_base; self.base = new_base;
self.top = self.base.add(size * 2); self.top = self.base.add(size * 2);
*self.ptr.get_mut() = self.base.add(size).cast_mut(); *self.ptr.get_mut() = self.base.add(size).cast_mut();
true Ok(())
} }
} }
} }
pub unsafe fn grow_new(&self) -> Option<Self> { pub unsafe fn grow_new(&self) -> Result<Self, AllocError> {
if self.base.is_null() { if self.base.is_null() {
Some(Self::new()) Self::new()
} else { } else {
let mut new_block = Self::empty_block(); let mut new_block = Self::empty_block();
new_block.init_at_size(self.size() * 2); new_block.init_at_size(self.size() * 2)?;
if new_block.base.is_null() { let allocated = (*self.ptr.get()).addr() - self.base.addr();
// allocation failed self.base.copy_to(new_block.base.cast_mut(), allocated);
None *new_block.ptr.get_mut() = new_block.base.add(allocated).cast_mut();
} else { Ok(new_block)
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)
}
} }
} }

View File

@@ -379,9 +379,7 @@ impl<'a> TermWriter<'a> {
#[inline] #[inline]
fn push_cell(&mut self, cell: HeapCellValue) -> Result<(), CompilationError> { fn push_cell(&mut self, cell: HeapCellValue) -> Result<(), CompilationError> {
self.heap Ok(self.heap.push_cell(cell)?)
.push_cell(cell)
.map_err(CompilationError::FiniteMemoryInHeap)
} }
fn term_as_addr(&mut self, term: &TermRef, h: usize) -> HeapCellValue { fn term_as_addr(&mut self, term: &TermRef, h: usize) -> HeapCellValue {
@@ -475,10 +473,7 @@ impl<'a> TermWriter<'a> {
self.push_stub_addr()?; self.push_stub_addr()?;
} }
let cell = self let cell = self.heap.allocate_cstr(src)?;
.heap
.allocate_cstr(src)
.map_err(CompilationError::FiniteMemoryInHeap)?;
let new_h = self.heap.cell_len(); let new_h = self.heap.cell_len();
self.push_cell(cell)?; self.push_cell(cell)?;
@@ -496,10 +491,7 @@ impl<'a> TermWriter<'a> {
self.push_stub_addr()?; self.push_stub_addr()?;
} }
let cell = self let cell = self.heap.allocate_pstr(src)?;
.heap
.allocate_pstr(src)
.map_err(CompilationError::FiniteMemoryInHeap)?;
let tail_h = self.heap.cell_len(); let tail_h = self.heap.cell_len();
self.push_stub_addr()?; self.push_stub_addr()?;

View File

@@ -1,4 +1,5 @@
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work #![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::arena::*;
use crate::atom_table::*; use crate::atom_table::*;