Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
2 changes: 1 addition & 1 deletion stdlib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
10 changes: 2 additions & 8 deletions vm/src/anystr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion vm/src/builtins/builtin_func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ impl PyNativeFunction {
)
}

pub fn as_func(&self) -> &'static PyNativeFn {
pub fn as_func(&self) -> &'static dyn PyNativeFn {
self.value.func
}
}
Expand Down
8 changes: 4 additions & 4 deletions vm/src/builtins/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}
Expand Down
6 changes: 2 additions & 4 deletions vm/src/builtins/str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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)
}

Expand Down
6 changes: 2 additions & 4 deletions vm/src/bytesinner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()
}

Expand Down
73 changes: 42 additions & 31 deletions vm/src/function/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand All @@ -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
Expand Down Expand Up @@ -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);
}
}
19 changes: 2 additions & 17 deletions vm/src/function/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}),+ ]
Expand All @@ -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,
Expand Down
8 changes: 4 additions & 4 deletions vm/src/sequence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
8 changes: 4 additions & 4 deletions vm/src/stdlib/collections.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}
Expand Down
14 changes: 7 additions & 7 deletions vm/src/vm/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -499,7 +499,7 @@ impl Context {
{
let def = PyMethodDef {
name,
func: f.into_func(),
func: Box::leak(Box::new(f.into_func())),
flags,
doc,
};
Expand Down