diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e73f1ecb3d4..b9879187278 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -140,9 +140,8 @@ jobs: - name: run rust tests run: cargo test --workspace --exclude rustpython_wasm --verbose --features threading ${{ env.CARGO_ARGS }} if: runner.os != 'macOS' - # temp skip ssl linking for Mac to avoid CI failure - - name: run rust tests (MacOS no ssl) - run: cargo test --workspace --exclude rustpython_wasm --exclude rustpython-jit --verbose --no-default-features --features threading,stdlib,zlib,importlib,encodings + - name: run rust tests + run: cargo test --workspace --exclude rustpython_wasm --exclude rustpython-jit --verbose --features threading ${{ env.CARGO_ARGS }} if: runner.os == 'macOS' - name: check compilation without threading diff --git a/Cargo.lock b/Cargo.lock index 8974c0654f1..93348466e37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1149,9 +1149,9 @@ dependencies = [ [[package]] name = "libsqlite3-sys" -version = "0.25.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29f835d03d717946d28b1d1ed632eb6f0e24a299388ee623d0c23118d3e8a7fa" +checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index 5546d232b9d..e17e4789f88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -95,7 +95,7 @@ threading = ["rustpython-vm/threading", "rustpython-stdlib/threading"] zlib = ["stdlib", "rustpython-stdlib/zlib"] bz2 = ["stdlib", "rustpython-stdlib/bz2"] ssl = ["rustpython-stdlib/ssl"] -ssl-vendor = ["rustpython-stdlib/ssl-vendor"] +ssl-vendor = ["ssl", "rustpython-stdlib/ssl-vendor"] [dependencies] rustpython-compiler = { workspace = true } diff --git a/stdlib/Cargo.toml b/stdlib/Cargo.toml index 9a6be309877..3b620917ee0 100644 --- a/stdlib/Cargo.toml +++ b/stdlib/Cargo.toml @@ -104,7 +104,7 @@ openssl-probe = { version = "0.1.5", optional = true } foreign-types-shared = { version = "0.1.1", optional = true } [target.'cfg(not(any(target_os = "android", target_arch = "wasm32")))'.dependencies] -libsqlite3-sys = { version = "0.25", features = ["min_sqlite_version_3_7_16", "bundled"] } +libsqlite3-sys = { version = "0.28", features = ["bundled"] } [target.'cfg(windows)'.dependencies] junction = { workspace = true } diff --git a/vm/src/anystr.rs b/vm/src/anystr.rs index e1cd43dff96..be04cba6414 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 b0b3492ea9f..7c02d8d469e 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 719ea36350e..83c0ddcc64d 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 9cc3397cade..ddf2493ded4 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 750344c7810..79d4d962625 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 3faaf594fe5..e38fda03b7f 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 fa47c16f4a0..82479dbde89 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 614e72d52ee..7f47874dda9 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 00eaaca0209..5a9a172d53b 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 4181a65896c..e328da97eb5 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, };