From cbdd0fbf150c68126d5f2cccf13badc3c38cc1ce Mon Sep 17 00:00:00 2001 From: =?utf8?q?Bennet=20Ble=C3=9Fmann?= Date: Wed, 22 Jan 2025 21:10:44 +0100 Subject: [PATCH] make Fixnum::build_with harder to accidentally misuse change trait bound order for better --- src/arena.rs | 54 +++--- src/arithmetic.rs | 20 ++- src/forms.rs | 4 +- src/functor_macro.rs | 2 +- src/heap_print.rs | 7 +- src/indexing.rs | 6 +- src/machine/arithmetic_ops.rs | 9 +- src/machine/attributed_variables.rs | 18 +- src/machine/dispatch.rs | 73 +++++--- src/machine/loader.rs | 4 +- src/machine/machine_state.rs | 5 +- src/machine/machine_state_impl.rs | 14 +- src/machine/mod.rs | 6 +- src/machine/system_calls.rs | 253 +++++++++++++++++----------- src/macros.rs | 6 - src/parser/ast.rs | 131 +++++++++++--- src/parser/lexer.rs | 17 +- src/parser/parser.rs | 6 +- src/types.rs | 10 ++ 19 files changed, 422 insertions(+), 223 deletions(-) diff --git a/src/arena.rs b/src/arena.rs index a9f9a76b..a6ccb921 100644 --- a/src/arena.rs +++ b/src/arena.rs @@ -784,7 +784,9 @@ mod tests { _ => { unreachable!() } ); - let fixnum_b_cell = fixnum_as_cell!(Fixnum::build_with(1 << 54)); + let fixnum_b_cell = fixnum_as_cell!( + Fixnum::build_with_checked(1i64 << 54).expect("1 << 54 fits in Fixnum") + ); assert_eq!(fixnum_b_cell.get_tag(), HeapCellValueTag::Fixnum); @@ -793,41 +795,29 @@ mod tests { None => unreachable!(), } - if Fixnum::build_with_checked(1 << 56).is_ok() { - unreachable!() - } - - if Fixnum::build_with_checked(i64::MAX).is_ok() { - unreachable!() - } - - if Fixnum::build_with_checked(i64::MIN).is_ok() { - unreachable!() - } - - match Fixnum::build_with_checked(-1) { - Ok(n) => assert_eq!(n.get_num(), -1), - _ => unreachable!(), - } + Fixnum::build_with_checked(1i64 << 56).expect_err("1 << 56 is too large for fixnum"); - match Fixnum::build_with_checked((1 << 55) - 1) { - Ok(n) => assert_eq!(n.get_num(), (1 << 55) - 1), - _ => unreachable!(), - } + Fixnum::build_with_checked(i64::MAX).expect_err("i64::MAX is too large for Fixnum"); + Fixnum::build_with_checked(i64::MIN).expect_err("i64::MIN is too small for Fixnum"); + assert_eq!( + Fixnum::build_with_checked(-1i64) + .expect("-1 fits in fixnum") + .get_num(), + -1 + ); - match Fixnum::build_with_checked(-(1 << 55)) { - Ok(n) => assert_eq!(n.get_num(), -(1 << 55)), - _ => unreachable!(), - } + Fixnum::build_with_checked((1i64 << 55) - 1) + .expect("(1 << 55) - 1 is the largest value that fits in Fixnum"); - if Fixnum::build_with_checked(-(1 << 55) - 1).is_ok() { - unreachable!() - } + Fixnum::build_with_checked(-(1i64 << 55)) + .expect("-(1 << 55) is the smallest value that fits in fixnum"); + Fixnum::build_with_checked(-(1i64 << 55) - 1) + .expect_err("-(1<<55) - 1 is too small for Fixnum"); - match Fixnum::build_with_checked(-1) { - Ok(n) => assert_eq!(-n, Fixnum::build_with(1)), - _ => unreachable!(), - } + assert_eq!( + -Fixnum::build_with_checked(-1i64).expect("-1 fits in Fixnum"), + Fixnum::build_with(1) + ); // float diff --git a/src/arithmetic.rs b/src/arithmetic.rs index 2bdcc315..2f0df28d 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -358,9 +358,8 @@ impl<'a> ArithmeticEvaluator<'a> { pub(crate) fn rnd_i(n: &'_ Number, arena: &mut Arena) -> Result { match n { &Number::Integer(i) => { - let result = (&*i).try_into(); - if let Ok(value) = result { - Ok(fixnum!(Number, value, arena)) + if let Ok(value) = Fixnum::build_with_checked(&*i) { + Ok(Number::Fixnum(value)) } else { Ok(*n) } @@ -369,11 +368,14 @@ pub(crate) fn rnd_i(n: &'_ Number, arena: &mut Arena) -> Result { let f = f.floor(); - const I64_MIN_TO_F: OrderedFloat = OrderedFloat(i64::MIN as f64); - const I64_MAX_TO_F: OrderedFloat = OrderedFloat(i64::MAX as f64); + const FIXNUM_MIN_TO_F: OrderedFloat = OrderedFloat(Fixnum::MIN as f64); + const FIXNUM_MAX_TO_F: OrderedFloat = OrderedFloat(Fixnum::MAX as f64); - if I64_MIN_TO_F <= f && f <= I64_MAX_TO_F { - Ok(fixnum!(Number, f.into_inner() as i64, arena)) + if (FIXNUM_MIN_TO_F..=FIXNUM_MAX_TO_F).contains(&f) { + Ok(Number::Fixnum( + // Safety: We checked that the value is in range + unsafe { Fixnum::build_with_unchecked(f.into_inner() as i64) }, + )) } else { Ok(Number::Integer(arena_alloc!( Integer::try_from(classify_float(f.0)?).unwrap_or_else(|_| { @@ -386,8 +388,8 @@ pub(crate) fn rnd_i(n: &'_ Number, arena: &mut Arena) -> Result { let floor = r.floor(); - if let Ok(value) = (&floor).try_into() { - Ok(fixnum!(Number, value, arena)) + if let Ok(value) = Fixnum::build_with_checked(&floor) { + Ok(Number::Fixnum(value)) } else { Ok(Number::Integer(arena_alloc!(floor, arena))) } diff --git a/src/forms.rs b/src/forms.rs index 21a5cc91..0779d5b8 100644 --- a/src/forms.rs +++ b/src/forms.rs @@ -697,14 +697,14 @@ impl ArenaFrom for Number { impl ArenaFrom for Number { #[inline] fn arena_from(value: u32, _arena: &mut Arena) -> Number { - Number::Fixnum(Fixnum::build_with(value as i64)) + Number::Fixnum(Fixnum::build_with(value)) } } impl ArenaFrom for Number { #[inline] fn arena_from(value: i32, _arena: &mut Arena) -> Number { - Number::Fixnum(Fixnum::build_with(value as i64)) + Number::Fixnum(Fixnum::build_with(value)) } } diff --git a/src/functor_macro.rs b/src/functor_macro.rs index 7cc48eaa..2725aa9d 100644 --- a/src/functor_macro.rs +++ b/src/functor_macro.rs @@ -78,7 +78,7 @@ macro_rules! build_functor { $res_len:expr, [$($subfunctor:expr),*]) => ({ build_functor!([$($dt($($value),*)),*], - [$($res, )* FunctorElement::Cell(fixnum_as_cell!(Fixnum::build_with($e as i64)))], + [$($res, )* FunctorElement::Cell(fixnum_as_cell!(/*FIXME this is not safe*/ unsafe{Fixnum::build_with_unchecked($e as i64)}))], 1 + $res_len, [$($subfunctor),*]) }); diff --git a/src/heap_print.rs b/src/heap_print.rs index 9f1efee7..c23344bf 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -1500,7 +1500,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { self.state_stack.push(TokenOrRedirect::NumberFocus( max_depth, - NumberFocus::Unfocused(Number::Fixnum(Fixnum::build_with(port as i64))), + NumberFocus::Unfocused(Number::Fixnum(Fixnum::build_with(port))), None, )); self.state_stack.push(TokenOrRedirect::Comma); @@ -1527,7 +1527,10 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { TokenOrRedirect::NumberFocus( max_depth, - NumberFocus::Unfocused(Number::Fixnum(Fixnum::build_with(idx_ptr_p))), + NumberFocus::Unfocused(Number::Fixnum( + /* FIXME this is not safe */ + unsafe { Fixnum::build_with_unchecked(idx_ptr_p) }, + )), None, ) }; diff --git a/src/indexing.rs b/src/indexing.rs index b9bdfc28..345533ad 100644 --- a/src/indexing.rs +++ b/src/indexing.rs @@ -1104,11 +1104,7 @@ pub(crate) fn constant_key_alternatives(constant: Literal) -> Option { _ => return None, }; - if let Ok(n) = n.try_into() { - Fixnum::build_with_checked(n).map(Literal::Fixnum).ok() - } else { - None - } + Fixnum::build_with_checked(n).map(Literal::Fixnum).ok() } #[derive(Debug)] diff --git a/src/machine/arithmetic_ops.rs b/src/machine/arithmetic_ops.rs index 43bb3723..d1667795 100644 --- a/src/machine/arithmetic_ops.rs +++ b/src/machine/arithmetic_ops.rs @@ -198,11 +198,10 @@ pub(crate) fn neg(n: Number, arena: &mut Arena) -> Number { pub(crate) fn abs(n: Number, arena: &mut Arena) -> Number { match n { Number::Fixnum(n) => { - if let Some(n) = n.get_num().checked_abs() { - fixnum!(Number, n, arena) + if let Some(n) = n.checked_abs() { + Number::Fixnum(n) } else { - let arena_int = Integer::from(n.get_num()); - Number::arena_from(arena_int.abs(), arena) + Number::arena_from(Integer::from(Fixnum::MAX + 1), arena) } } Number::Integer(n) => { @@ -1105,7 +1104,7 @@ pub(crate) fn round(num: Number, arena: &mut Arena) -> Result Result { match n1 { - Number::Fixnum(n) => Ok(Number::Fixnum(Fixnum::build_with(!n.get_num()))), + Number::Fixnum(n) => Ok(Number::Fixnum(!n)), Number::Integer(n1) => Ok(Number::arena_from(Integer::from(!&*n1), arena)), _ => { let stub_gen = || { diff --git a/src/machine/attributed_variables.rs b/src/machine/attributed_variables.rs index 34e4755d..4aaf45ee 100644 --- a/src/machine/attributed_variables.rs +++ b/src/machine/attributed_variables.rs @@ -120,9 +120,21 @@ impl MachineState { and_frame[i] = self.registers[i]; } - and_frame[arity + 1] = fixnum_as_cell!(Fixnum::build_with(self.b0 as i64)); - and_frame[arity + 2] = fixnum_as_cell!(Fixnum::build_with(self.num_of_args as i64)); - and_frame[arity + 3] = fixnum_as_cell!(Fixnum::build_with(self.attr_var_init.cp as i64)); + and_frame[arity + 1] = + fixnum_as_cell!( + /* FIXME this is not safe */ + unsafe { Fixnum::build_with_unchecked(self.b0 as i64) } + ); + and_frame[arity + 2] = + fixnum_as_cell!( + /* FIXME this is not safe */ + unsafe { Fixnum::build_with_unchecked(self.num_of_args as i64) } + ); + and_frame[arity + 3] = + fixnum_as_cell!( + /* FIXME this is not safe */ + unsafe { Fixnum::build_with_unchecked(self.attr_var_init.cp as i64) } + ); self.verify_attributes()?; diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index 3a88221a..20dfd8ee 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -1021,9 +1021,13 @@ impl Machine { match self.find_living_dynamic_else(p + next_i) { Some(_) => { self.machine_st.registers - [self.machine_st.num_of_args + 1] = fixnum_as_cell!( - Fixnum::build_with(self.machine_st.cc as i64) - ); + [self.machine_st.num_of_args + 1] = + fixnum_as_cell!(unsafe { + /* FIXME this is not safe */ + Fixnum::build_with_unchecked( + self.machine_st.cc as i64, + ) + }); self.machine_st.num_of_args += 1; self.try_me_else(next_i); @@ -1042,10 +1046,11 @@ impl Machine { .prelude .num_cells; - self.machine_st.cc = cell_as_fixnum!( + self.machine_st.cc = unsafe { self.machine_st.stack [stack_loc!(OrFrame, self.machine_st.b, n - 1)] - ) + .to_fixnum_or_cut_point_unchecked() + } .get_num() as usize; @@ -1095,7 +1100,12 @@ impl Machine { Some(_) => { self.machine_st.registers [self.machine_st.num_of_args + 1] = fixnum_as_cell!( - Fixnum::build_with(self.machine_st.cc as i64) + /* FIXME this is not safe */ + unsafe { + Fixnum::build_with_unchecked( + self.machine_st.cc as i64, + ) + } ); self.machine_st.num_of_args += 1; @@ -1115,10 +1125,11 @@ impl Machine { .prelude .num_cells; - self.machine_st.cc = cell_as_fixnum!( + self.machine_st.cc = unsafe { self.machine_st.stack [stack_loc!(OrFrame, self.machine_st.b, n - 1)] - ) + .to_fixnum_or_cut_point_unchecked() + } .get_num() as usize; @@ -1174,7 +1185,10 @@ impl Machine { &Instruction::GetLevel(r) => { let b0 = self.machine_st.b0; - self.machine_st[r] = fixnum_as_cell!(Fixnum::as_cutpoint(b0 as i64)); + self.machine_st[r] = fixnum_as_cell!( + /* FIXME this is not safe */ + unsafe { Fixnum::build_with_unchecked(b0 as i64) }.as_cutpoint() + ); self.machine_st.p += 1; } &Instruction::GetPrevLevel(r) => { @@ -1185,12 +1199,18 @@ impl Machine { .prelude .b; - self.machine_st[r] = fixnum_as_cell!(Fixnum::as_cutpoint(prev_b as i64)); + self.machine_st[r] = fixnum_as_cell!( + /* FIXME this is not safe */ + unsafe { Fixnum::build_with_unchecked(prev_b as i64) }.as_cutpoint() + ); self.machine_st.p += 1; } &Instruction::GetCutPoint(r) => { self.machine_st[r] = - fixnum_as_cell!(Fixnum::as_cutpoint(self.machine_st.b as i64)); + fixnum_as_cell!(/* FIXME this is not safe */ unsafe { + Fixnum::build_with_unchecked(self.machine_st.b as i64) + } + .as_cutpoint()); self.machine_st.p += 1; } &Instruction::Cut(r) => { @@ -3150,10 +3170,14 @@ impl Machine { match self.find_living_dynamic(oi, ii + 1) { Some(_) => { self.machine_st.registers - [self.machine_st.num_of_args + 1] = - fixnum_as_cell!(Fixnum::build_with( - self.machine_st.cc as i64 - )); + [self.machine_st.num_of_args + 1] = fixnum_as_cell!( + /* FIXME this is not safe */ + unsafe { + Fixnum::build_with_unchecked( + self.machine_st.cc as i64, + ) + } + ); self.machine_st.num_of_args += 1; self.indexed_try(offset); @@ -3175,10 +3199,11 @@ impl Machine { .prelude .num_cells; - self.machine_st.cc = cell_as_fixnum!( + self.machine_st.cc = unsafe { self.machine_st.stack [stack_loc!(OrFrame, b, n - 1)] - ) + .to_fixnum_or_cut_point_unchecked() + } .get_num() as usize; @@ -5256,8 +5281,11 @@ impl Machine { .machine_st .store(self.machine_st.deref(self.machine_st.registers[5])); - self.machine_st - .unify_fixnum(Fixnum::build_with(n as i64), r); + self.machine_st.unify_fixnum( + /* FIXME this is not safe */ + unsafe { Fixnum::build_with_unchecked(n as i64) }, + r, + ); } self.machine_st.call_at_index(2, p); @@ -5299,8 +5327,11 @@ impl Machine { .machine_st .store(self.machine_st.deref(self.machine_st.registers[5])); - self.machine_st - .unify_fixnum(Fixnum::build_with(n as i64), r); + self.machine_st.unify_fixnum( + /* FIXME this is not safe */ + unsafe { Fixnum::build_with_unchecked(n as i64) }, + r, + ); } self.machine_st.execute_at_index(2, p); diff --git a/src/machine/loader.rs b/src/machine/loader.rs index 89b83ed3..c18eceda 100644 --- a/src/machine/loader.rs +++ b/src/machine/loader.rs @@ -2344,7 +2344,9 @@ impl Machine { MetaSpec::Either => atom_as_cell!(atom!("?")), MetaSpec::Colon => atom_as_cell!(atom!(":")), MetaSpec::RequiresExpansionWithArgument(ref arg_num) => { - fixnum_as_cell!(Fixnum::build_with(*arg_num as i64)) + fixnum_as_cell!(/* FIXME this is not safe */ unsafe { + Fixnum::build_with_unchecked(*arg_num as i64) + }) } }); } diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index 941d9f4d..e3236483 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -1003,7 +1003,10 @@ impl MachineState { arity: HeapCellValue, ) -> (Atom, usize) { let name = cell_as_atom!(self.store(self.deref(name))); - let arity = cell_as_fixnum!(self.store(self.deref(arity))); + let arity = unsafe { + self.store(self.deref(arity)) + .to_fixnum_or_cut_point_unchecked() + }; (name, usize::try_from(arity.get_num()).unwrap()) } diff --git a/src/machine/machine_state_impl.rs b/src/machine/machine_state_impl.rs index 038a16d7..b72b18d2 100644 --- a/src/machine/machine_state_impl.rs +++ b/src/machine/machine_state_impl.rs @@ -940,7 +940,7 @@ impl MachineState { /* if pstr_atom.len() > offset as usize { self.heap.push(pstr_offset_as_cell!(h)); - self.heap.push(fixnum_as_cell!(Fixnum::build_with(offset))); + self.heap.push(fixnum_as_cell!(Fixnum::build_with_unchecked(offset as i64))); unify_fn!(*self, pstr_loc_as_cell!(h_len), a3); } else { @@ -973,13 +973,13 @@ impl MachineState { if n == 1 { self.unify_char(c, self.store(self.deref(self.registers[3]))); } else if n == 2 { - let offset = c.len_utf8() as i64; + let offset = c.len_utf8(); let h_len = self.heap.len(); - if cstr_atom.len() > offset as usize { + if cstr_atom.len() > offset{ self.heap.push(atom_as_cstr_cell!(cstr_atom)); self.heap.push(pstr_offset_as_cell!(h_len)); - self.heap.push(fixnum_as_cell!(Fixnum::build_with(offset))); + self.heap.push(fixnum_as_cell!(Fixnum::build_with_unchecked(offset as i64))); unify_fn!(*self, pstr_loc_as_cell!(h_len+1), self.registers[3]); } else { @@ -1027,7 +1027,11 @@ impl MachineState { if !self.fail { let a3 = self.store(self.deref(self.registers[3])); - self.unify_fixnum(Fixnum::build_with(arity as i64), a3); + self.unify_fixnum( + /* FIXME this is not safe */ + unsafe { Fixnum::build_with_unchecked(arity as i64) }, + a3, + ); } } diff --git a/src/machine/mod.rs b/src/machine/mod.rs index 8f70b96f..4c987373 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -1161,8 +1161,10 @@ impl Machine { let (idx, arity) = if self.machine_st.effective_block() > prev_block { (r_c_w_h, 0) } else { - self.machine_st.registers[1] = - fixnum_as_cell!(Fixnum::build_with(b_cutoff as i64)); + self.machine_st.registers[1] = fixnum_as_cell!( + /* FIXME this is not safe */ + unsafe { Fixnum::build_with_unchecked(b_cutoff as i64) } + ); (r_c_wo_h, 1) }; diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 6875edfb..3bee79d4 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -578,9 +578,11 @@ impl MachineState { while lh_offset + 4 < self.lifted_heap.cell_len() { let cell_threshold = - cell_as_fixnum!(self.lifted_heap[lh_offset + 3]).get_num() as usize; + unsafe { self.lifted_heap[lh_offset + 3].to_fixnum_or_cut_point_unchecked() } + .get_num() as usize; let pstr_upper_threshold = - cell_as_fixnum!(self.lifted_heap[lh_offset + 4]).get_num() as usize; + unsafe { self.lifted_heap[lh_offset + 4].to_fixnum_or_cut_point_unchecked() } + .get_num() as usize; for idx in lh_offset..cell_threshold { section.push_cell(self.lifted_heap[idx] + offset); @@ -740,7 +742,11 @@ impl MachineState { // self.heap.pop_cell(); let target_n = self.store(self.deref(self.registers[1])); - self.unify_fixnum(Fixnum::build_with(brent_st.num_steps() as i64), target_n); + self.unify_fixnum( + /* FIXME this is not safe */ + unsafe { Fixnum::build_with_unchecked(brent_st.num_steps() as i64) }, + target_n, + ); if !self.fail { unify!(self, self.registers[4], self.heap[prev_hare]); @@ -749,7 +755,10 @@ impl MachineState { fn finalize_skip_max_list(&mut self, n: i64, value: HeapCellValue) { let target_n = self.store(self.deref(self.registers[1])); - self.unify_fixnum(Fixnum::build_with(n), target_n); + self.unify_fixnum( + /* FIXME this is not safe */ unsafe { Fixnum::build_with_unchecked(n) }, + target_n, + ); if !self.fail { let xs = self.registers[4]; @@ -881,7 +890,11 @@ impl MachineState { let value = self.store(self.deref(value)); self.block = self.b; - self.unify_fixnum(Fixnum::build_with(self.block as i64), value); + self.unify_fixnum( + /* FIXME this is not safe */ + unsafe { Fixnum::build_with_unchecked(self.block as i64) }, + value, + ); self.block } @@ -959,7 +972,7 @@ impl MachineState { let mut tokens = vec![]; match lexer.next_number_token() { - Ok(token @ Token::Literal(Literal::Atom(atom!("-")) | Literal::Char('-'))) => { + Ok(token @ Token::Literal(Literal::Atom(atom!("-")))) => { tokens.push(token); if let Ok(token) = lexer.next_number_token() { @@ -1051,7 +1064,10 @@ impl MachineState { for index in s + 2..s + 2 + num_cells { if let HeapCellValueTag::CutPoint = self.heap[index].get_tag() { // adjust cut point to occur after call_continuation. - and_frame[index - (s + 1)] = fixnum_as_cell!(Fixnum::as_cutpoint(self.b as i64)); + and_frame[index - (s + 1)] = fixnum_as_cell!( + /* FIXME this is not safe */ + unsafe { Fixnum::build_with_unchecked(self.b as i64) }.as_cutpoint() + ); } else { and_frame[index - (s + 1)] = self.heap[index]; } @@ -2332,7 +2348,7 @@ impl Machine { (HeapCellValueTag::Char, c) => { let h = self.machine_st.heap.len(); - self.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(c as i64))); + self.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(u32::from(c)))); self.machine_st.heap.push(empty_list_as_cell!()); unify!(self.machine_st, list_loc_as_cell!(h), self.machine_st.registers[2]); @@ -2342,7 +2358,7 @@ impl Machine { debug_assert_eq!(arity, 0); let name = name.as_str(); - let iter = name.chars().map(|c| fixnum_as_cell!(Fixnum::build_with(c as i64))); + let iter = name.chars().map(|c| fixnum_as_cell!(Fixnum::build_with(c))); let list_cell = resource_error_call_result!( self.machine_st, @@ -2439,7 +2455,10 @@ impl Machine { ); let a2 = self.deref_register(2); - self.machine_st.unify_fixnum(Fixnum::build_with(len), a2); + self.machine_st.unify_fixnum( + /* FIXME this is not safe */ unsafe { Fixnum::build_with_unchecked(len) }, + a2, + ); } #[inline(always)] @@ -2593,7 +2612,7 @@ impl Machine { Ok(Number::Integer(n)) => { let result: Result = (&*n).try_into(); if let Ok(value) = result { - fixnum_as_cell!(Fixnum::build_with(value as i64)) + fixnum_as_cell!(Fixnum::build_with(value)) } else { let err = self.machine_st.type_error(ValidType::InByte, addr); return Err(self.machine_st.error_form(err, stub_gen())); @@ -2601,7 +2620,7 @@ impl Machine { } Ok(Number::Fixnum(n)) => { if let Ok(nb) = u8::try_from(n.get_num()) { - fixnum_as_cell!(Fixnum::build_with(nb as i64)) + fixnum_as_cell!(Fixnum::build_with(nb)) } else { let err = self.machine_st.type_error(ValidType::InByte, addr); return Err(self.machine_st.error_form(err, stub_gen())); @@ -2617,8 +2636,7 @@ impl Machine { loop { match stream.peek_byte().map_err(|e| e.kind()) { Ok(b) => { - self.machine_st - .unify_fixnum(Fixnum::build_with(b as i64), addr); + self.machine_st.unify_fixnum(Fixnum::build_with(b), addr); break; } Err(ErrorKind::PermissionDenied) => { @@ -2783,10 +2801,8 @@ impl Machine { Ok(Number::Integer(n)) => { let n: u32 = (&*n).try_into().unwrap(); - let n = std::char::from_u32(n).map(|_| n); - - if let Some(n) = n { - fixnum_as_cell!(Fixnum::build_with(n as i64)) + if std::char::from_u32(n).is_some() { + fixnum_as_cell!(Fixnum::build_with(n)) } else { let err = self.machine_st.representation_error(RepFlag::InCharacterCode); return Err(self.machine_st.error_form(err, stub_gen())); @@ -2798,7 +2814,7 @@ impl Machine { .and_then(|n| std::char::from_u32(n).map(|_| n)); if let Some(n) = n { - fixnum_as_cell!(Fixnum::build_with(n as i64)) + fixnum_as_cell!(Fixnum::build_with(n)) } else { let err = self.machine_st.representation_error(RepFlag::InCharacterCode); return Err(self.machine_st.error_form(err, stub_gen())); @@ -2818,7 +2834,7 @@ impl Machine { match result.map(|result| result.map_err(|e| e.kind())) { Some(Ok(c)) => { self.machine_st - .unify_fixnum(Fixnum::build_with(c as i64), addr); + .unify_fixnum(Fixnum::build_with(u32::from(c)), addr); break; } Some(Err(ErrorKind::PermissionDenied)) => { @@ -2896,7 +2912,7 @@ impl Machine { let codes = string .trim() .chars() - .map(|c| fixnum_as_cell!(Fixnum::build_with(c as i64))); + .map(|c| fixnum_as_cell!(Fixnum::build_with(u32::from(c)))); let list_cell = step_or_resource_error!( self.machine_st, @@ -2939,7 +2955,9 @@ impl Machine { #[inline(always)] pub(crate) fn lifted_heap_length(&mut self) { let a1 = self.machine_st.registers[1]; - let lh_len = Fixnum::build_with(self.machine_st.lifted_heap.cell_len() as i64); + /* FIXME this is not safe */ + let lh_len = + unsafe { Fixnum::build_with_unchecked(self.machine_st.lifted_heap.cell_len() as i64) }; self.machine_st.unify_fixnum(lh_len, a1); } @@ -3002,7 +3020,7 @@ impl Machine { ); self.machine_st - .unify_fixnum(Fixnum::build_with(c as i64), a2); + .unify_fixnum(Fixnum::build_with(u32::from(c)), a2); Ok(()) } @@ -3132,7 +3150,7 @@ impl Machine { #[inline(always)] pub(crate) fn check_cut_point(&mut self) { let addr = self.deref_register(1); - let old_b = cell_as_fixnum!(addr).get_num() as usize; + let old_b = unsafe { addr.to_fixnum_or_cut_point_unchecked() }.get_num() as usize; let prev_b = self .machine_st @@ -3442,7 +3460,7 @@ impl Machine { let n: Result = (&*n).try_into(); if let Ok(value) = n { - fixnum_as_cell!(Fixnum::build_with(value as i64)) + fixnum_as_cell!(Fixnum::build_with(value)) } else { let err = self.machine_st.type_error(ValidType::InByte, addr); return Err(self.machine_st.error_form(err, stub_gen())); @@ -3450,7 +3468,7 @@ impl Machine { } Ok(Number::Fixnum(n)) => { if let Ok(nb) = u8::try_from(n.get_num()) { - fixnum_as_cell!(Fixnum::build_with(nb as i64)) + fixnum_as_cell!(Fixnum::build_with(nb)) } else { let err = self.machine_st.type_error(ValidType::InByte, addr); return Err(self.machine_st.error_form(err, stub_gen())); @@ -3467,8 +3485,7 @@ impl Machine { match stream.read(&mut b) { Ok(1) => { - self.machine_st - .unify_fixnum(Fixnum::build_with(b[0] as i64), addr); + self.machine_st.unify_fixnum(Fixnum::build_with(b[0]), addr); } _ => { stream.set_past_end_of_stream(true); @@ -3693,7 +3710,7 @@ impl Machine { let n = std::char::from_u32(n); if let Some(n) = n { - fixnum_as_cell!(Fixnum::build_with(n as i64)) + fixnum_as_cell!(Fixnum::build_with(u32::from(n))) } else { let err = self .machine_st @@ -3737,7 +3754,7 @@ impl Machine { match result { Some(Ok(c)) => { self.machine_st - .unify_fixnum(Fixnum::build_with(c as i64), addr); + .unify_fixnum(Fixnum::build_with(u32::from(c)), addr); break; } _ => { @@ -3916,7 +3933,8 @@ impl Machine { #[inline(always)] pub(crate) fn copy_to_lifted_heap(&mut self) { - let lh_offset = cell_as_fixnum!(self.deref_register(1)).get_num() as usize; + let lh_offset = + unsafe { self.deref_register(1).to_fixnum_or_cut_point_unchecked() }.get_num() as usize; let copy_target = self.machine_st.registers[2]; let FindallCopyInfo { offset: old_threshold, @@ -3935,11 +3953,14 @@ impl Machine { self.machine_st.lifted_heap[idx] -= self.machine_st.heap.cell_len() + lh_offset; } - self.machine_st.lifted_heap[old_threshold + 1] = - fixnum_as_cell!(Fixnum::build_with(pstr_threshold as i64)); - self.machine_st.lifted_heap[old_threshold + 2] = fixnum_as_cell!(Fixnum::build_with( - self.machine_st.lifted_heap.cell_len() as i64 - )); + self.machine_st.lifted_heap[old_threshold + 1] = fixnum_as_cell!( + /* FIXME this is not safe */ + unsafe { Fixnum::build_with_unchecked(pstr_threshold as i64) } + ); + self.machine_st.lifted_heap[old_threshold + 2] = + fixnum_as_cell!(/* FIXME this is not safe */ unsafe { + Fixnum::build_with_unchecked(self.machine_st.lifted_heap.cell_len() as i64) + }); let mut pstr_threshold = heap_index!(pstr_threshold); @@ -3958,7 +3979,8 @@ impl Machine { pub(crate) fn lookup_db_ref(&mut self) { let module_name = self.deref_register(1); let name = cell_as_atom!(self.deref_register(2)); - let arity = cell_as_fixnum!(self.deref_register(3)).get_num() as usize; + let arity = + unsafe { self.deref_register(3).to_fixnum_or_cut_point_unchecked() }.get_num() as usize; let module_name = read_heap_cell!(module_name, (HeapCellValueTag::Atom, (module_name, _arity)) => { @@ -4285,7 +4307,12 @@ impl Machine { return; } let value = self.rng.gen_range(lower..upper); - Number::Fixnum(Fixnum::build_with(value)) + // Safety: + // - lower and uper bounds are Fixnum values + // - value is inbetween lower and upper + // - fixnums value range has no gaps + // so value is also a valid Fixnum value + Number::Fixnum(unsafe { Fixnum::build_with_unchecked(value) }) } (Ok(Number::Fixnum(lower)), Ok(Number::Integer(upper))) => { let lower = Integer::from(lower); @@ -4463,7 +4490,7 @@ impl Machine { // status code let status = resp.status().as_u16(); self.machine_st - .unify_fixnum(Fixnum::build_with(status as i64), address_status); + .unify_fixnum(Fixnum::build_with(status), address_status); // headers let mut headers: Vec = vec![]; @@ -5010,9 +5037,12 @@ impl Machine { { Ok(result) => { match result { - Value::Int(n) => self - .machine_st - .unify_fixnum(Fixnum::build_with(n), return_value), + Value::Int(n) => self.machine_st.unify_fixnum( + Fixnum::build_with_checked(n).unwrap_or_else(|_| { + todo!("handle integer values that don't fit in fixnum") + }), + return_value, + ), Value::Float(n) => { let n = float_alloc!(n, self.machine_st.arena); self.machine_st.unify_f64(n, return_value) @@ -5060,7 +5090,16 @@ impl Machine { for val in args { expanded_args.push(match val { - Value::Int(n) => fixnum_as_cell!(Fixnum::build_with(n)), + Value::Int(n) => { + if let Ok(fixnum) = Fixnum::build_with_checked(n) { + fixnum_as_cell!(fixnum) + } else { + integer_as_cell!(Number::Integer(arena_alloc!( + Integer::from(n), + &mut self.machine_st.arena + ))) + } + } Value::Float(n) => HeapCellValue::from(float_alloc!(n, self.machine_st.arena)), Value::CString(cstr) => atom_as_cell!(AtomTable::build_with( &self.machine_st.atom_tbl, @@ -5413,7 +5452,11 @@ impl Machine { #[inline(always)] pub(crate) fn get_attr_var_queue_delimiter(&mut self) { let addr = self.deref_register(1); - let value = Fixnum::build_with(self.machine_st.attr_var_init.attr_var_queue.len() as i64); + + /* FIXME this is not safe */ + let value = unsafe { + Fixnum::build_with_unchecked(self.machine_st.attr_var_init.attr_var_queue.len() as i64) + }; self.machine_st.unify_fixnum(value, addr); } @@ -5682,7 +5725,7 @@ impl Machine { #[inline(always)] pub(crate) fn get_continuation_chunk(&mut self) { let e = self.deref_register(1); - let e = cell_as_fixnum!(e).get_num() as usize; + let e = unsafe { e.to_fixnum_or_cut_point_unchecked() }.get_num() as usize; let h = self.machine_st.heap.cell_len(); let p_functor_cell = self.deref_register(2); @@ -5736,8 +5779,12 @@ impl Machine { #[inline(always)] pub(crate) fn get_lifted_heap_from_offset_diff(&mut self) { let lh_offset = self.machine_st.registers[1]; - let lh_offset = cell_as_fixnum!(self.machine_st.store(self.machine_st.deref(lh_offset))) - .get_num() as usize; + let lh_offset = unsafe { + self.machine_st + .store(self.machine_st.deref(lh_offset)) + .to_fixnum_or_cut_point_unchecked() + } + .get_num() as usize; if lh_offset >= self.machine_st.lifted_heap.cell_len() { let solutions = self.machine_st.registers[2]; @@ -5765,8 +5812,12 @@ impl Machine { #[inline(always)] pub(crate) fn get_lifted_heap_from_offset(&mut self) { let lh_offset = self.machine_st.registers[1]; - let lh_offset = cell_as_fixnum!(self.machine_st.store(self.machine_st.deref(lh_offset))) - .get_num() as usize; + let lh_offset = unsafe { + self.machine_st + .store(self.machine_st.deref(lh_offset)) + .to_fixnum_or_cut_point_unchecked() + } + .get_num() as usize; if lh_offset >= self.machine_st.lifted_heap.cell_len() { let solutions = self.machine_st.registers[2]; @@ -5888,7 +5939,7 @@ impl Machine { } }; - let bp = cell_as_fixnum!(a1).get_num() as usize; + let bp = unsafe { a1.to_fixnum_or_cut_point_unchecked() }.get_num() as usize; let a3 = self.deref_register(3); let count = self.machine_st.cwil.add_limit(n, bp).clone(); @@ -5899,9 +5950,11 @@ impl Machine { #[inline(always)] pub(crate) fn inference_count(&mut self, count_var: HeapCellValue, count: Integer) { - if let Ok(value) = <&Integer as TryInto>::try_into(&count) { - self.machine_st - .unify_fixnum(Fixnum::build_with(value), count_var); + if let Some(value) = <&Integer as TryInto>::try_into(&count) + .ok() + .and_then(|i| Fixnum::build_with_checked(i).ok()) + { + self.machine_st.unify_fixnum(value, count_var); } else { let count = arena_alloc!(count, &mut self.machine_st.arena); self.machine_st.unify_big_int(count, count_var); @@ -6028,7 +6081,8 @@ impl Machine { #[inline(always)] pub(crate) fn remove_call_policy_check(&mut self) { - let bp = cell_as_fixnum!(self.deref_register(1)).get_num() as usize; + let bp = + unsafe { self.deref_register(1).to_fixnum_or_cut_point_unchecked() }.get_num() as usize; if bp == self.machine_st.b && self.machine_st.cwil.is_empty() { self.machine_st.cwil.reset(); @@ -6040,12 +6094,10 @@ impl Machine { let a1 = self.deref_register(1); let a2 = self.deref_register(2); - let block = cell_as_fixnum!(a1).get_num() as usize; + let block = unsafe { a1.to_fixnum_or_cut_point_unchecked() }.get_num() as usize; let count = self.machine_st.cwil.remove_limit(block).clone(); - let result = count.clone().try_into(); - - if let Ok(value) = result { - self.machine_st.unify_fixnum(Fixnum::build_with(value), a2); + if let Ok(value) = Fixnum::build_with_checked(&count) { + self.machine_st.unify_fixnum(value, a2); } else { let count = arena_alloc!(count.clone(), &mut self.machine_st.arena); self.machine_st.unify_big_int(count, a2); @@ -6063,18 +6115,23 @@ impl Machine { self.machine_st.registers[i] = self.machine_st.stack[stack_loc!(AndFrame, e, i)]; } - self.machine_st.b0 = cell_as_fixnum!( + self.machine_st.b0 = unsafe { self.machine_st.stack[stack_loc!(AndFrame, e, frame_len - 2)] - ) + .to_fixnum_or_cut_point_unchecked() + } .get_num() as usize; - self.machine_st.num_of_args = cell_as_fixnum!( + self.machine_st.num_of_args = unsafe { self.machine_st.stack[stack_loc!(AndFrame, e, frame_len - 1)] - ) + .to_fixnum_or_cut_point_unchecked() + } .get_num() as usize; - let p = cell_as_fixnum!(self.machine_st.stack[stack_loc!(AndFrame, e, frame_len)]).get_num() - as usize; + let p = unsafe { + self.machine_st.stack[stack_loc!(AndFrame, e, frame_len)] + .to_fixnum_or_cut_point_unchecked() + } + .get_num() as usize; self.machine_st.deallocate(); self.machine_st.p = p; @@ -6191,7 +6248,7 @@ impl Machine { let a1 = self.deref_register(1); let a2 = self.deref_register(2); - let bp = cell_as_fixnum!(a2).get_num() as usize; + let bp = unsafe { a2.to_fixnum_or_cut_point_unchecked() }.get_num() as usize; let prev_b = self .machine_st .stack @@ -6214,7 +6271,7 @@ impl Machine { #[inline(always)] pub(crate) fn clean_up_block(&mut self) { let nb = self.deref_register(1); - let nb = cell_as_fixnum!(nb).get_num() as usize; + let nb = unsafe { nb.to_fixnum_or_cut_point_unchecked() }.get_num() as usize; let b = self.machine_st.b; @@ -6270,7 +6327,9 @@ impl Machine { #[inline(always)] pub(crate) fn get_current_block(&mut self) { let addr = self.machine_st.registers[1]; - let block = Fixnum::build_with(self.machine_st.block as i64); + + /* FIXME this is not safe */ + let block = unsafe { Fixnum::build_with_unchecked(self.machine_st.block as i64) }; self.machine_st.unify_fixnum(block, addr); } @@ -6278,21 +6337,27 @@ impl Machine { #[inline(always)] pub(crate) fn get_current_scc_block(&mut self) { let addr = self.machine_st.registers[1]; - let block = Fixnum::build_with(self.machine_st.scc_block as i64); + + /* FIXME this is not safe */ + let block = unsafe { Fixnum::build_with_unchecked(self.machine_st.scc_block as i64) }; self.machine_st.unify_fixnum(block, addr); } #[inline(always)] pub(crate) fn get_b_value(&mut self) { - let n = Fixnum::as_cutpoint(i64::try_from(self.machine_st.b).unwrap()); + /* FIXME this is not safe */ + let n = unsafe { Fixnum::build_with_unchecked(i64::try_from(self.machine_st.b).unwrap()) } + .as_cutpoint(); self.machine_st .unify_fixnum(n, self.machine_st.registers[1]); } #[inline(always)] pub(crate) fn get_cut_point(&mut self) { - let n = Fixnum::as_cutpoint(i64::try_from(self.machine_st.b0).unwrap()); + /* FIXME this is not safe */ + let n = unsafe { Fixnum::build_with_unchecked(i64::try_from(self.machine_st.b0).unwrap()) } + .as_cutpoint(); self.machine_st .unify_fixnum(n, self.machine_st.registers[1]); } @@ -6314,7 +6379,7 @@ impl Machine { let cp = and_frame.prelude.cp - 1; let e = and_frame.prelude.e; - let e = Fixnum::build_with(i64::try_from(e).unwrap()); + let e = Fixnum::build_with_checked(e).unwrap(); machine_st.unify_fixnum(e, machine_st.registers[2]); @@ -6363,7 +6428,7 @@ impl Machine { writer(&mut self.machine_st.heap) ); - let e = Fixnum::build_with(i64::try_from(and_frame.prelude.e).unwrap()); + let e = Fixnum::build_with_checked(and_frame.prelude.e).unwrap(); self.machine_st.unify_fixnum(e, self.machine_st.registers[2]); if !self.machine_st.fail { @@ -6787,10 +6852,7 @@ impl Machine { let port = tcp_listener.local_addr().map(|addr| addr.port()).ok(); if let Some(port) = port { - ( - arena_alloc!(tcp_listener, &mut self.machine_st.arena), - port as usize, - ) + (arena_alloc!(tcp_listener, &mut self.machine_st.arena), port) } else { self.machine_st.fail = true; return Ok(()); @@ -6817,7 +6879,7 @@ impl Machine { if had_zero_port { self.machine_st - .unify_fixnum(Fixnum::build_with(port as i64), self.deref_register(2)); + .unify_fixnum(Fixnum::build_with(port), self.deref_register(2)); } Ok(()) @@ -7266,7 +7328,8 @@ impl Machine { #[inline(always)] pub(crate) fn term_variables_under_max_depth(&mut self) { // Term, MaxDepth, VarList - let max_depth = cell_as_fixnum!(self.deref_register(2)).get_num() as usize; + let max_depth = + unsafe { self.deref_register(2).to_fixnum_or_cut_point_unchecked() }.get_num() as usize; self.machine_st.term_variables_under_max_depth( self.machine_st.registers[1], @@ -7278,7 +7341,7 @@ impl Machine { #[inline(always)] pub(crate) fn truncate_lifted_heap_to(&mut self) { let a1 = self.deref_register(1); - let lh_offset = cell_as_fixnum!(a1).get_num() as usize; + let lh_offset = unsafe { a1.to_fixnum_or_cut_point_unchecked() }.get_num() as usize; self.machine_st.lifted_heap.truncate(lh_offset); } @@ -7561,7 +7624,7 @@ impl Machine { } } - let byte = Fixnum::build_with(bytes[0] as i64); + let byte = Fixnum::build_with(bytes[0]); self.machine_st.unify_fixnum(byte, arg); } @@ -7587,7 +7650,7 @@ impl Machine { context_len, finalized_context .iter() - .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))) + .map(|b| fixnum_as_cell!(Fixnum::build_with(*b))) ) ) } @@ -7604,7 +7667,7 @@ impl Machine { context_len, finalized_context .iter() - .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))) + .map(|b| fixnum_as_cell!(Fixnum::build_with(*b))) ) ) } @@ -7621,7 +7684,7 @@ impl Machine { context_len, finalized_context .iter() - .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))) + .map(|b| fixnum_as_cell!(Fixnum::build_with(*b))) ) ) } @@ -7638,7 +7701,7 @@ impl Machine { context_len, finalized_context .iter() - .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))), + .map(|b| fixnum_as_cell!(Fixnum::build_with(*b))), ) ) } @@ -7655,7 +7718,7 @@ impl Machine { context_len, finalized_context .iter() - .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))), + .map(|b| fixnum_as_cell!(Fixnum::build_with(*b))), ) ) } @@ -7672,7 +7735,7 @@ impl Machine { context_len, finalized_context .iter() - .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))), + .map(|b| fixnum_as_cell!(Fixnum::build_with(*b))), ) ) } @@ -7689,7 +7752,7 @@ impl Machine { context_len, finalized_context .iter() - .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))) + .map(|b| fixnum_as_cell!(Fixnum::build_with(*b))) ) ) } @@ -7714,7 +7777,7 @@ impl Machine { ints.as_ref().len(), ints.as_ref() .iter() - .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))) + .map(|b| fixnum_as_cell!(Fixnum::build_with(*b))) ) ) } @@ -7754,7 +7817,7 @@ impl Machine { tag.as_ref().len(), tag.as_ref() .iter() - .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))) + .map(|b| fixnum_as_cell!(Fixnum::build_with(*b))) ) ); @@ -7825,7 +7888,7 @@ impl Machine { bytes.len(), bytes .iter() - .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))) + .map(|b| fixnum_as_cell!(Fixnum::build_with(*b))) ) ) }; @@ -7881,7 +7944,7 @@ impl Machine { bytes.len(), bytes .iter() - .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))) + .map(|b| fixnum_as_cell!(Fixnum::build_with(*b))) ) ) }; @@ -7928,7 +7991,7 @@ impl Machine { tag.as_ref().len(), tag.as_ref() .iter() - .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))) + .map(|b| fixnum_as_cell!(Fixnum::build_with(*b))) ) ); @@ -8068,7 +8131,7 @@ impl Machine { sig.as_ref().len(), sig.as_ref() .iter() - .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))) + .map(|b| fixnum_as_cell!(Fixnum::build_with(*b))) ) ); @@ -8482,7 +8545,7 @@ impl Machine { let number = self.deref_register(1); let pop_count = integer_as_cell!(match Number::try_from(number) { Ok(Number::Fixnum(n)) => { - Number::Fixnum(Fixnum::build_with(n.get_num().count_ones() as i64)) + Number::Fixnum(Fixnum::build_with(n.get_num().count_ones())) } Ok(Number::Integer(n)) => { let value: usize = if n.sign() == Sign::Positive { diff --git a/src/macros.rs b/src/macros.rs index 5347af3c..29e3369f 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -14,12 +14,6 @@ macro_rules! fixnum_as_cell { }; } -macro_rules! cell_as_fixnum { - ($cell:expr) => { - Fixnum::from_bytes($cell.into_bytes()) - }; -} - macro_rules! integer_as_cell { ($n: expr) => {{ match $n { diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 9f1d7382..d67d97ce 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -11,7 +11,10 @@ use std::cell::{Cell, Ref, RefCell, RefMut}; use std::fmt; use std::hash::Hash; use std::hash::Hasher; +use std::i64; use std::io::{Error as IOError, ErrorKind}; +use std::ops::Not; +use std::ops::RangeInclusive; use std::ops::{Deref, Neg}; use std::rc::Rc; use std::sync::Arc; @@ -550,9 +553,90 @@ pub struct Fixnum { tag: B6, } +mod private { + use dashu::Integer; + + pub(crate) trait FitsInFixnumSeal {} + pub(crate) trait MightNotFitInFixnumSeal {} + + macro_rules! impl_fits_in_fixnum { + ($t:ty) => { + impl $crate::parser::ast::private::FitsInFixnumSeal for $t {} + + impl $crate::parser::ast::FitsInFixnum for $t { + fn into_i56(self) -> i64 { + self.into() + } + } + }; + } + + impl_fits_in_fixnum!(u8); + impl_fits_in_fixnum!(i8); + impl_fits_in_fixnum!(u16); + impl_fits_in_fixnum!(i16); + impl_fits_in_fixnum!(u32); + impl_fits_in_fixnum!(i32); + + impl FitsInFixnumSeal for char {} + impl super::FitsInFixnum for char { + fn into_i56(self) -> i64 { + u32::from(self) as i64 + } + } + + impl MightNotFitInFixnumSeal for i64 {} + impl MightNotFitInFixnumSeal for &Integer {} + impl MightNotFitInFixnumSeal for Integer {} + impl MightNotFitInFixnumSeal for usize {} +} + +#[allow(private_bounds)] +pub trait FitsInFixnum: private::FitsInFixnumSeal { + fn into_i56(self) -> i64; +} + +#[allow(private_bounds)] +pub trait MightNotFitInFixnum: private::MightNotFitInFixnumSeal { + fn try_into_i56(self) -> Option; +} + +impl MightNotFitInFixnum for T +where + T: private::MightNotFitInFixnumSeal + TryInto, +{ + fn try_into_i56(self) -> Option { + let val = self.try_into().ok()?; + if Fixnum::RANGE.contains(&val) { + Some(val) + } else { + None + } + } +} + impl Fixnum { + pub(crate) const MIN: i64 = -(1 << 55); + pub(crate) const MAX: i64 = (1 << 55) - 1; + const RANGE: RangeInclusive = Self::MIN..=Self::MAX; + + // if you have a type that is not guaranteed to fit use `Fixnum::build_with_checked` or `Fixnum::build_with_unchecked` instead + #[inline] + pub fn build_with(num: impl FitsInFixnum) -> Self { + // Safety: FitsInFixnum is only implemented by types that only have valid values + // and FitsInFixnumSeal ensures no one outside this crate can violate that + unsafe { Self::build_with_unchecked(num.into_i56()) } + } + #[inline] - pub fn build_with(num: i64) -> Self { + pub unsafe fn build_with_unchecked(num: i64) -> Self { + debug_assert!( + Self::RANGE.contains(&num), + "{num} should be in the range {}..={}", + Self::MIN, + Self::MAX + ); + Fixnum::new() .with_num(u64::from_ne_bytes(num.to_ne_bytes()) & ((1 << 56) - 1)) .with_tag(HeapCellValueTag::Fixnum as u8) @@ -561,12 +645,8 @@ impl Fixnum { } #[inline] - pub fn as_cutpoint(num: i64) -> Self { - Fixnum::new() - .with_num(u64::from_ne_bytes(num.to_ne_bytes()) & ((1 << 56) - 1)) - .with_tag(HeapCellValueTag::CutPoint as u8) - .with_m(false) - .with_f(false) + pub fn as_cutpoint(self) -> Self { + self.with_tag(HeapCellValueTag::CutPoint as u8) } #[inline] @@ -575,20 +655,14 @@ impl Fixnum { HeapCellValueTag::from_bytes(self.tag()).unwrap() } + // if you have a type that is guaranteed to fit use `Fixnum::build_with` instead #[inline] - pub fn build_with_checked(num: i64) -> Result { - const UPPER_BOUND: i64 = (1 << 55) - 1; - const LOWER_BOUND: i64 = -(1 << 55); - - if (LOWER_BOUND..=UPPER_BOUND).contains(&num) { - Ok(Fixnum::new() - .with_m(false) - .with_f(false) - .with_tag(HeapCellValueTag::Fixnum as u8) - .with_num(u64::from_ne_bytes(num.to_ne_bytes()) & ((1 << 56) - 1))) - } else { - Err(OutOfBounds {}) - } + pub fn build_with_checked(num: impl MightNotFitInFixnum) -> Result { + Ok(unsafe { + // Safety: all MightNotFitInFixnum impls return None when the value is out-of-bounds + // and MightNotFitInFixnumSeal ensures no one outside this crate can violate that + Self::build_with_unchecked(num.try_into_i56().ok_or(OutOfBounds {})?) + }) } #[inline] @@ -598,6 +672,10 @@ impl Fixnum { debug_assert!(!overflowed); n } + + pub fn checked_abs(self) -> Option { + Self::build_with_checked(self.get_num().abs()).ok() + } } impl Neg for Fixnum { @@ -605,7 +683,18 @@ impl Neg for Fixnum { #[inline] fn neg(self) -> Self::Output { - Fixnum::build_with(-self.get_num()) + // Safety: the truncating behaviour is correct + unsafe { Self::build_with_unchecked(-self.get_num()) } + } +} + +impl Not for Fixnum { + type Output = Self; + + #[inline] + fn not(self) -> Self::Output { + // Safety: the truncating behaviour is correct + unsafe { Self::build_with_unchecked(!self.get_num()) } } } diff --git a/src/parser/lexer.rs b/src/parser/lexer.rs index 95162196..a1b8cb4a 100644 --- a/src/parser/lexer.rs +++ b/src/parser/lexer.rs @@ -1,9 +1,7 @@ -use crate::arena::F64Ptr; -use crate::arena::TypedArenaPtr; - use crate::arena::*; use crate::atom_table::*; pub use crate::machine::machine_state::*; +use crate::offset_table::F64Ptr; use crate::parser::ast::*; use crate::parser::char_reader::*; use crate::parser::dashu::Integer; @@ -662,15 +660,15 @@ impl<'a, R: CharRead> Lexer<'a, R> { } } - fn vacate_with_float(&mut self, mut token: String) -> Result { + fn vacate_with_float(&mut self, mut token: String) -> Result { self.return_char(token.pop().unwrap()); let n = parse_float_lossy(&token)?; - Ok(Token::Literal(Literal::from(float_alloc!( + Ok(Number::Float(float_alloc!( n, self.machine_st.arena - )))) + ))) } fn skip_underscore_in_number(&mut self) -> Result { @@ -797,7 +795,8 @@ impl<'a, R: CharRead> Lexer<'a, R> { } let n = parse_float_lossy(&token)?; - Ok(Token::Literal(Literal::from(float_alloc!( + + Ok(NumberToken::Number(Number::Float(float_alloc!( n, self.machine_st.arena )))) @@ -806,7 +805,7 @@ impl<'a, R: CharRead> Lexer<'a, R> { } } else { let n = parse_float_lossy(&token)?; - Ok(Token::Literal(Literal::from(float_alloc!( + Ok(NumberToken::Number(Number::Float(float_alloc!( n, self.machine_st.arena )))) @@ -859,7 +858,7 @@ impl<'a, R: CharRead> Lexer<'a, R> { } self.get_single_quoted_char() - .map(|c| NumberToken::Number(Number::Fixnum(Fixnum::build_with(c as i64)))) + .map(|c| NumberToken::Number(Number::Fixnum(Fixnum::build_with(c)))) .or_else(|err| { match err { ParserError::UnexpectedChar('\'', ..) => {} diff --git a/src/parser/parser.rs b/src/parser/parser.rs index b39fc557..10702109 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -389,7 +389,7 @@ impl<'a, R: CharRead> Parser<'a, R> { Cell::default(), Box::new(Term::Literal( Cell::default(), - Literal::Fixnum(Fixnum::build_with(c as i64)), + Literal::Fixnum(Fixnum::build_with(c)), )), Box::new(list), ); @@ -963,12 +963,12 @@ impl<'a, R: CharRead> Parser<'a, R> { Token::Literal(Literal::Rational(n)) => { self.negate_number(n, negate_rat_rc, |r, _| Literal::Rational(r)) } - Token::Literal(Literal::Float(n)) if n.as_ptr().is_infinite() => { + Token::Literal(Literal::Float(n)) if n.as_ptr().is_infinite() => { return Err(ParserError::InfiniteFloat( self.lexer.line_num, self.lexer.col_num, )); - } + } Token::Literal(Literal::Float(n)) => self.negate_number( **n.as_ptr(), |n, _| -n, diff --git a/src/types.rs b/src/types.rs index e6dda7d9..b4765930 100644 --- a/src/types.rs +++ b/src/types.rs @@ -588,6 +588,16 @@ impl HeapCellValue { } } + // FIXME: someone that knows this better should check if this can be split into `to_fixnum_unchecked` and `to_cut_point_unchecked` assuming thats always unambigusly knowable + #[inline] + pub unsafe fn to_fixnum_or_cut_point_unchecked(self) -> Fixnum { + debug_assert!(matches!( + self.get_tag(), + HeapCellValueTag::Fixnum | HeapCellValueTag::CutPoint + )); + Fixnum::from_bytes(self.into_bytes()) + } + #[inline] pub fn from_ptr_addr(ptr_bytes: usize) -> Self { HeapCellValue::from_bytes((ptr_bytes as u64).to_ne_bytes()) -- 2.54.0