diff --git a/vm/src/anystr.rs b/vm/src/anystr.rs index e1cd43dff9..be04cba641 100644 --- a/vm/src/anystr.rs +++ b/vm/src/anystr.rs @@ -150,20 +150,14 @@ where pub trait AnyStr { type Char: Copy; type Container: AnyStrContainer + Extend; - type CharIter<'a>: Iterator + 'a - where - Self: 'a; - type ElementIter<'a>: Iterator + '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; + fn elements(&self) -> impl Iterator; fn get_bytes(&self, range: std::ops::Range) -> &Self; // FIXME: get_chars is expensive for str fn get_chars(&self, range: std::ops::Range) -> &Self; diff --git a/vm/src/builtins/builtin_func.rs b/vm/src/builtins/builtin_func.rs index b0b3492ea9..7c02d8d469 100644 --- a/vm/src/builtins/builtin_func.rs +++ b/vm/src/builtins/builtin_func.rs @@ -49,7 +49,7 @@ impl PyNativeFunction { ) } - pub fn as_func(&self) -> &'static PyNativeFn { + pub fn as_func(&self) -> &'static dyn PyNativeFn { self.value.func } } diff --git a/vm/src/builtins/list.rs b/vm/src/builtins/list.rs index 719ea36350..83c0ddcc64 100644 --- a/vm/src/builtins/list.rs +++ b/vm/src/builtins/list.rs @@ -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 { self.borrow_vec() } } diff --git a/vm/src/builtins/str.rs b/vm/src/builtins/str.rs index 9cc3397cad..ddf2493ded 100644 --- a/vm/src/builtins/str.rs +++ b/vm/src/builtins/str.rs @@ -1692,8 +1692,6 @@ impl AnyStrContainer 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 { str::chars(self) } - fn elements(&self) -> Self::ElementIter<'_> { + fn elements(&self) -> impl Iterator { str::chars(self) } diff --git a/vm/src/bytesinner.rs b/vm/src/bytesinner.rs index 750344c781..79d4d96262 100644 --- a/vm/src/bytesinner.rs +++ b/vm/src/bytesinner.rs @@ -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; - type CharIter<'a> = bstr::Chars<'a>; - type ElementIter<'a> = std::iter::Copied>; 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 { bstr::ByteSlice::chars(self) } - fn elements(&self) -> Self::ElementIter<'_> { + fn elements(&self) -> impl Iterator { self.iter().copied() } diff --git a/vm/src/function/builtin.rs b/vm/src/function/builtin.rs index 3faaf594fe..e38fda03b7 100644 --- a/vm/src/function/builtin.rs +++ b/vm/src/function/builtin.rs @@ -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 PyResult + PyThreadingConstraint + 'static> PyNativeFn + for F +{ +} /// Implemented by types that are or can generate built-in functions. /// @@ -34,40 +41,44 @@ pub trait IntoPyNativeFn: 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::() == 0`. If it isn't, use of this constant will - /// raise a compile error. - const STATIC_FUNC: &'static PyNativeFn = { - if std::mem::size_of::() == 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::::uninit().assume_init() }; - f.call(vm, args) +const fn into_func, Kind>(f: F) -> impl PyNativeFn { + move |vm: &VirtualMachine, args| f.call(vm, args) +} + +const fn zst_ref_out_of_thin_air(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::() == 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::::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 Zst for T {} + ::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::() == 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>(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>(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); } } diff --git a/vm/src/function/method.rs b/vm/src/function/method.rs index fa47c16f4a..82479dbde8 100644 --- a/vm/src/function/method.rs +++ b/vm/src/function/method.rs @@ -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( - name: &'static str, - func: impl IntoPyNativeFn, - flags: PyMethodFlags, - doc: Option<&'static str>, - ) -> Self { - Self { - name, - func: func.into_func(), - flags, - doc, - } - } - #[inline] pub const fn new_const( name: &'static str, diff --git a/vm/src/sequence.rs b/vm/src/sequence.rs index 614e72d52e..7f47874dda 100644 --- a/vm/src/sequence.rs +++ b/vm/src/sequence.rs @@ -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; fn mut_count(&self, vm: &VirtualMachine, needle: &PyObject) -> PyResult { let mut count = 0; diff --git a/vm/src/stdlib/collections.rs b/vm/src/stdlib/collections.rs index 00eaaca020..5a9a172d53 100644 --- a/vm/src/stdlib/collections.rs +++ b/vm/src/stdlib/collections.rs @@ -416,13 +416,13 @@ mod _collections { } impl MutObjectSequenceOp for PyDeque { - type Guard<'a> = PyRwLockReadGuard<'a, VecDeque>; + type Inner = VecDeque; - 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 { self.borrow_deque() } } diff --git a/vm/src/vm/context.rs b/vm/src/vm/context.rs index 4181a65896..e328da97eb 100644 --- a/vm/src/vm/context.rs +++ b/vm/src/vm/context.rs @@ -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, };