Use RPITIT where applicable

This commit is contained in:
Noa
2024-01-02 15:55:28 -06:00
committed by Jeong, YunWon
parent 3e98e5e86c
commit 3fd0382a51
10 changed files with 70 additions and 84 deletions

View File

@@ -150,20 +150,14 @@ where
pub trait AnyStr {
type Char: Copy;
type Container: AnyStrContainer<Self> + Extend<Self::Char>;
type CharIter<'a>: Iterator<Item = char> + 'a
where
Self: 'a;
type ElementIter<'a>: Iterator<Item = Self::Char> + 'a
where
Self: 'a;
fn element_bytes_len(c: Self::Char) -> usize;
fn to_container(&self) -> Self::Container;
fn as_bytes(&self) -> &[u8];
fn as_utf8_str(&self) -> Result<&str, std::str::Utf8Error>;
fn chars(&self) -> Self::CharIter<'_>;
fn elements(&self) -> Self::ElementIter<'_>;
fn chars(&self) -> impl Iterator<Item = char>;
fn elements(&self) -> impl Iterator<Item = Self::Char>;
fn get_bytes(&self, range: std::ops::Range<usize>) -> &Self;
// FIXME: get_chars is expensive for str
fn get_chars(&self, range: std::ops::Range<usize>) -> &Self;

View File

@@ -49,7 +49,7 @@ impl PyNativeFunction {
)
}
pub fn as_func(&self) -> &'static PyNativeFn {
pub fn as_func(&self) -> &'static dyn PyNativeFn {
self.value.func
}
}

View File

@@ -358,13 +358,13 @@ where
}
impl MutObjectSequenceOp for PyList {
type Guard<'a> = PyMappedRwLockReadGuard<'a, [PyObjectRef]>;
type Inner = [PyObjectRef];
fn do_get<'a>(index: usize, guard: &'a Self::Guard<'_>) -> Option<&'a PyObjectRef> {
guard.get(index)
fn do_get(index: usize, inner: &[PyObjectRef]) -> Option<&PyObjectRef> {
inner.get(index)
}
fn do_lock(&self) -> Self::Guard<'_> {
fn do_lock(&self) -> impl std::ops::Deref<Target = [PyObjectRef]> {
self.borrow_vec()
}
}

View File

@@ -1692,8 +1692,6 @@ impl AnyStrContainer<str> for String {
impl AnyStr for str {
type Char = char;
type Container = String;
type CharIter<'a> = std::str::Chars<'a>;
type ElementIter<'a> = std::str::Chars<'a>;
fn element_bytes_len(c: char) -> usize {
c.len_utf8()
@@ -1711,11 +1709,11 @@ impl AnyStr for str {
Ok(self)
}
fn chars(&self) -> Self::CharIter<'_> {
fn chars(&self) -> impl Iterator<Item = char> {
str::chars(self)
}
fn elements(&self) -> Self::ElementIter<'_> {
fn elements(&self) -> impl Iterator<Item = char> {
str::chars(self)
}

View File

@@ -1024,8 +1024,6 @@ const ASCII_WHITESPACES: [u8; 6] = [0x20, 0x09, 0x0a, 0x0c, 0x0d, 0x0b];
impl AnyStr for [u8] {
type Char = u8;
type Container = Vec<u8>;
type CharIter<'a> = bstr::Chars<'a>;
type ElementIter<'a> = std::iter::Copied<std::slice::Iter<'a, u8>>;
fn element_bytes_len(_: u8) -> usize {
1
@@ -1043,11 +1041,11 @@ impl AnyStr for [u8] {
std::str::from_utf8(self)
}
fn chars(&self) -> Self::CharIter<'_> {
fn chars(&self) -> impl Iterator<Item = char> {
bstr::ByteSlice::chars(self)
}
fn elements(&self) -> Self::ElementIter<'_> {
fn elements(&self) -> impl Iterator<Item = u8> {
self.iter().copied()
}

View File

@@ -7,7 +7,14 @@ use std::marker::PhantomData;
/// A built-in Python function.
// PyCFunction in CPython
pub type PyNativeFn = py_dyn_fn!(dyn Fn(&VirtualMachine, FuncArgs) -> PyResult);
pub trait PyNativeFn:
Fn(&VirtualMachine, FuncArgs) -> PyResult + PyThreadingConstraint + 'static
{
}
impl<F: Fn(&VirtualMachine, FuncArgs) -> PyResult + PyThreadingConstraint + 'static> PyNativeFn
for F
{
}
/// Implemented by types that are or can generate built-in functions.
///
@@ -34,40 +41,44 @@ pub trait IntoPyNativeFn<Kind>: Sized + PyThreadingConstraint + 'static {
/// `IntoPyNativeFn::into_func()` generates a PyNativeFn that performs the
/// appropriate type and arity checking, any requested conversions, and then if
/// successful calls the function with the extracted parameters.
fn into_func(self) -> &'static PyNativeFn {
let boxed = Box::new(move |vm: &VirtualMachine, args| self.call(vm, args));
Box::leak(boxed)
fn into_func(self) -> impl PyNativeFn {
into_func(self)
}
}
/// Equivalent to `into_func()`, but accessible as a constant. This is only
/// valid if this function is zero-sized, i.e. that
/// `std::mem::size_of::<F>() == 0`. If it isn't, use of this constant will
/// raise a compile error.
const STATIC_FUNC: &'static PyNativeFn = {
if std::mem::size_of::<Self>() == 0 {
&|vm, args| {
// SAFETY: we just confirmed that Self is zero-sized, so there
// aren't any bytes in it that could be uninit.
#[allow(clippy::uninit_assumed_init)]
let f = unsafe { std::mem::MaybeUninit::<Self>::uninit().assume_init() };
f.call(vm, args)
const fn into_func<F: IntoPyNativeFn<Kind>, Kind>(f: F) -> impl PyNativeFn {
move |vm: &VirtualMachine, args| f.call(vm, args)
}
const fn zst_ref_out_of_thin_air<T: 'static>(x: T) -> &'static T {
// if T is zero-sized, there's no issue forgetting it - even if it does have a Drop impl, it
// would never get called anyway if we consider this semantically a Box::leak(Box::new(x))-type
// operation. if T isn't zero-sized, we don't have to worry about it because we'll fail to compile.
std::mem::forget(x);
trait Zst: Sized + 'static {
const THIN_AIR: &'static Self = {
if std::mem::size_of::<Self>() == 0 {
// SAFETY: we just confirmed that Self is zero-sized, so we can
// pull a value of it out of thin air.
unsafe { std::ptr::NonNull::<Self>::dangling().as_ref() }
} else {
panic!("can't use a non-zero-sized type here")
}
} else {
panic!("function must be zero-sized to access STATIC_FUNC")
}
};
};
}
impl<T: 'static> Zst for T {}
<T as Zst>::THIN_AIR
}
/// Get the [`STATIC_FUNC`](IntoPyNativeFn::STATIC_FUNC) of the passed function. The same
/// requirements of zero-sizedness apply, see that documentation for details.
///
/// Equivalent to [`IntoPyNativeFn::into_func()`], but usable in a const context. This is only
/// valid if the function is zero-sized, i.e. that `std::mem::size_of::<F>() == 0`. If you call
/// this function with a non-zero-sized function, it will raise a compile error.
#[inline(always)]
pub const fn static_func<Kind, F: IntoPyNativeFn<Kind>>(f: F) -> &'static PyNativeFn {
// if f is zero-sized, there's no issue forgetting it - even if a capture of f does have a Drop
// impl, it would never get called anyway. If you passed it to into_func, it would just get
// Box::leak'd, and as a 'static reference it'll never be dropped. and if f isn't zero-sized,
// we'll never reach this point anyway because we'll fail to compile.
std::mem::forget(f);
F::STATIC_FUNC
pub const fn static_func<Kind, F: IntoPyNativeFn<Kind>>(f: F) -> &'static dyn PyNativeFn {
zst_ref_out_of_thin_air(into_func(f))
}
// TODO: once higher-rank trait bounds are stabilized, remove the `Kind` type
@@ -207,16 +218,16 @@ into_py_native_fn_tuple!(
#[cfg(test)]
mod tests {
use super::*;
use std::mem::size_of_val;
#[test]
fn test_into_native_fn_noalloc() {
let check_zst = |f: &'static PyNativeFn| assert_eq!(std::mem::size_of_val(f), 0);
fn py_func(_b: bool, _vm: &crate::VirtualMachine) -> i32 {
1
}
check_zst(py_func.into_func());
assert_eq!(size_of_val(&py_func.into_func()), 0);
let empty_closure = || "foo".to_owned();
check_zst(empty_closure.into_func());
check_zst(static_func(empty_closure));
assert_eq!(size_of_val(&empty_closure.into_func()), 0);
assert_eq!(size_of_val(static_func(empty_closure)), 0);
}
}

View File

@@ -56,7 +56,7 @@ macro_rules! define_methods {
($($name:literal => $func:ident as $flags:ident),+) => {
vec![ $( $crate::function::PyMethodDef {
name: $name,
func: $crate::function::IntoPyNativeFn::into_func($func),
func: $crate::function::static_func($func),
flags: $crate::function::PyMethodFlags::$flags,
doc: None,
}),+ ]
@@ -66,27 +66,12 @@ macro_rules! define_methods {
#[derive(Clone)]
pub struct PyMethodDef {
pub name: &'static str, // TODO: interned
pub func: &'static PyNativeFn,
pub func: &'static dyn PyNativeFn,
pub flags: PyMethodFlags,
pub doc: Option<&'static str>, // TODO: interned
}
impl PyMethodDef {
#[inline]
pub fn new<Kind>(
name: &'static str,
func: impl IntoPyNativeFn<Kind>,
flags: PyMethodFlags,
doc: Option<&'static str>,
) -> Self {
Self {
name,
func: func.into_func(),
flags,
doc,
}
}
#[inline]
pub const fn new_const<Kind>(
name: &'static str,

View File

@@ -3,13 +3,13 @@ use crate::{
vm::VirtualMachine, AsObject, PyObject, PyObjectRef, PyResult,
};
use optional::Optioned;
use std::ops::Range;
use std::ops::{Deref, Range};
pub trait MutObjectSequenceOp {
type Guard<'a>: 'a;
type Inner: ?Sized;
fn do_get<'a>(index: usize, guard: &'a Self::Guard<'_>) -> Option<&'a PyObjectRef>;
fn do_lock(&self) -> Self::Guard<'_>;
fn do_get(index: usize, inner: &Self::Inner) -> Option<&PyObjectRef>;
fn do_lock(&self) -> impl Deref<Target = Self::Inner>;
fn mut_count(&self, vm: &VirtualMachine, needle: &PyObject) -> PyResult<usize> {
let mut count = 0;

View File

@@ -416,13 +416,13 @@ mod _collections {
}
impl MutObjectSequenceOp for PyDeque {
type Guard<'a> = PyRwLockReadGuard<'a, VecDeque<PyObjectRef>>;
type Inner = VecDeque<PyObjectRef>;
fn do_get<'a>(index: usize, guard: &'a Self::Guard<'_>) -> Option<&'a PyObjectRef> {
guard.get(index)
fn do_get(index: usize, inner: &Self::Inner) -> Option<&PyObjectRef> {
inner.get(index)
}
fn do_lock(&self) -> Self::Guard<'_> {
fn do_lock(&self) -> impl std::ops::Deref<Target = Self::Inner> {
self.borrow_deque()
}
}

View File

@@ -291,12 +291,12 @@ impl Context {
let string_pool = StringPool::default();
let names = unsafe { ConstName::new(&string_pool, &types.str_type.to_owned()) };
let slot_new_wrapper = PyMethodDef {
name: names.__new__.as_str(),
func: PyType::__new__.into_func(),
flags: PyMethodFlags::METHOD,
doc: None,
};
let slot_new_wrapper = PyMethodDef::new_const(
names.__new__.as_str(),
PyType::__new__,
PyMethodFlags::METHOD,
None,
);
let empty_str = unsafe { string_pool.intern("", types.str_type.to_owned()) };
let empty_bytes = create_object(PyBytes::from(Vec::new()), types.bytes_type);
@@ -499,7 +499,7 @@ impl Context {
{
let def = PyMethodDef {
name,
func: f.into_func(),
func: Box::leak(Box::new(f.into_func())),
flags,
doc,
};