make Fixnum::build_with harder to accidentally misuse
change trait bound order for better
This commit is contained in:
committed by
Mark Thom
parent
87ca083cfa
commit
cbdd0fbf15
@@ -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<i64>;
|
||||
}
|
||||
|
||||
impl<T> MightNotFitInFixnum for T
|
||||
where
|
||||
T: private::MightNotFitInFixnumSeal + TryInto<i64>,
|
||||
{
|
||||
fn try_into_i56(self) -> Option<i64> {
|
||||
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<i64> = 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: i64) -> Self {
|
||||
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 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<Self, OutOfBounds> {
|
||||
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<Self, OutOfBounds> {
|
||||
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> {
|
||||
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()) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Token, ParserError> {
|
||||
fn vacate_with_float(&mut self, mut token: String) -> Result<Number, ParserError> {
|
||||
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<char, ParserError> {
|
||||
@@ -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('\'', ..) => {}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user