Remove cfg min_const_generics made unnecessary by MSRV bump.

This commit is contained in:
Adam Reichold 2023-06-05 09:42:40 +02:00
parent f7bd6ce224
commit eb04f54683
4 changed files with 155 additions and 296 deletions

View File

@ -46,7 +46,7 @@ fn configure_pyo3() -> Result<()> {
ensure_auto_initialize_ok(interpreter_config)?; ensure_auto_initialize_ok(interpreter_config)?;
// Emit cfgs like `addr_of` and `min_const_generics` // Emit cfgs like `thread_local_const_init`
print_feature_cfgs(); print_feature_cfgs();
Ok(()) Ok(())

View File

@ -142,11 +142,6 @@ pub fn print_feature_cfgs() {
let rustc_minor_version = rustc_minor_version().unwrap_or(0); let rustc_minor_version = rustc_minor_version().unwrap_or(0);
// Enable use of const generics on Rust 1.51 and greater
if rustc_minor_version >= 51 {
println!("cargo:rustc-cfg=min_const_generics");
}
// Enable use of std::ptr::addr_of! on Rust 1.51 and greater // Enable use of std::ptr::addr_of! on Rust 1.51 and greater
if rustc_minor_version >= 51 { if rustc_minor_version >= 51 {
println!("cargo:rustc-cfg=addr_of"); println!("cargo:rustc-cfg=addr_of");

View File

@ -100,7 +100,7 @@ fn configure_pyo3() -> Result<()> {
println!("{}", line); println!("{}", line);
} }
// Emit cfgs like `addr_of` and `min_const_generics` // Emit cfgs like `thread_local_const_init`
print_feature_cfgs(); print_feature_cfgs();
Ok(()) Ok(())

View File

@ -1,308 +1,118 @@
use crate::conversion::{AsPyPointer, IntoPyPointer};
use crate::types::PySequence;
use crate::{exceptions, PyErr}; use crate::{exceptions, PyErr};
use crate::{
ffi, FromPyObject, IntoPy, Py, PyAny, PyDowncastError, PyObject, PyResult, Python, ToPyObject,
};
#[cfg(min_const_generics)] impl<T, const N: usize> IntoPy<PyObject> for [T; N]
mod min_const_generics { where
use super::invalid_sequence_length; T: IntoPy<PyObject>,
use crate::conversion::{AsPyPointer, IntoPyPointer}; {
use crate::types::PySequence; fn into_py(self, py: Python<'_>) -> PyObject {
use crate::{
ffi, FromPyObject, IntoPy, Py, PyAny, PyDowncastError, PyObject, PyResult, Python,
ToPyObject,
};
impl<T, const N: usize> IntoPy<PyObject> for [T; N]
where
T: IntoPy<PyObject>,
{
fn into_py(self, py: Python<'_>) -> PyObject {
unsafe {
#[allow(deprecated)] // we're not on edition 2021 yet
let elements = std::array::IntoIter::new(self);
let len = N as ffi::Py_ssize_t;
let ptr = ffi::PyList_New(len);
// We create the `Py` pointer here for two reasons:
// - panics if the ptr is null
// - its Drop cleans up the list if user code panics.
let list: Py<PyAny> = Py::from_owned_ptr(py, ptr);
for (i, obj) in (0..len).zip(elements) {
let obj = obj.into_py(py).into_ptr();
#[cfg(not(Py_LIMITED_API))]
ffi::PyList_SET_ITEM(ptr, i, obj);
#[cfg(Py_LIMITED_API)]
ffi::PyList_SetItem(ptr, i, obj);
}
list
}
}
}
impl<T, const N: usize> ToPyObject for [T; N]
where
T: ToPyObject,
{
fn to_object(&self, py: Python<'_>) -> PyObject {
self.as_ref().to_object(py)
}
}
impl<'a, T, const N: usize> FromPyObject<'a> for [T; N]
where
T: FromPyObject<'a>,
{
fn extract(obj: &'a PyAny) -> PyResult<Self> {
create_array_from_obj(obj)
}
}
fn create_array_from_obj<'s, T, const N: usize>(obj: &'s PyAny) -> PyResult<[T; N]>
where
T: FromPyObject<'s>,
{
// Types that pass `PySequence_Check` usually implement enough of the sequence protocol
// to support this function and if not, we will only fail extraction safely.
let seq: &PySequence = unsafe {
if ffi::PySequence_Check(obj.as_ptr()) != 0 {
obj.downcast_unchecked()
} else {
return Err(PyDowncastError::new(obj, "Sequence").into());
}
};
let seq_len = seq.len()?;
if seq_len != N {
return Err(invalid_sequence_length(N, seq_len));
}
array_try_from_fn(|idx| seq.get_item(idx).and_then(PyAny::extract))
}
// TODO use std::array::try_from_fn, if that stabilises:
// (https://github.com/rust-lang/rust/pull/75644)
fn array_try_from_fn<E, F, T, const N: usize>(mut cb: F) -> Result<[T; N], E>
where
F: FnMut(usize) -> Result<T, E>,
{
// Helper to safely create arrays since the standard library doesn't
// provide one yet. Shouldn't be necessary in the future.
struct ArrayGuard<T, const N: usize> {
dst: *mut T,
initialized: usize,
}
impl<T, const N: usize> Drop for ArrayGuard<T, N> {
fn drop(&mut self) {
debug_assert!(self.initialized <= N);
let initialized_part =
core::ptr::slice_from_raw_parts_mut(self.dst, self.initialized);
unsafe {
core::ptr::drop_in_place(initialized_part);
}
}
}
// [MaybeUninit<T>; N] would be "nicer" but is actually difficult to create - there are nightly
// APIs which would make this easier.
let mut array: core::mem::MaybeUninit<[T; N]> = core::mem::MaybeUninit::uninit();
let mut guard: ArrayGuard<T, N> = ArrayGuard {
dst: array.as_mut_ptr() as _,
initialized: 0,
};
unsafe { unsafe {
let mut value_ptr = array.as_mut_ptr() as *mut T; #[allow(deprecated)] // we're not on edition 2021 yet
for i in 0..N { let elements = std::array::IntoIter::new(self);
core::ptr::write(value_ptr, cb(i)?); let len = N as ffi::Py_ssize_t;
value_ptr = value_ptr.offset(1);
guard.initialized += 1; let ptr = ffi::PyList_New(len);
// We create the `Py` pointer here for two reasons:
// - panics if the ptr is null
// - its Drop cleans up the list if user code panics.
let list: Py<PyAny> = Py::from_owned_ptr(py, ptr);
for (i, obj) in (0..len).zip(elements) {
let obj = obj.into_py(py).into_ptr();
#[cfg(not(Py_LIMITED_API))]
ffi::PyList_SET_ITEM(ptr, i, obj);
#[cfg(Py_LIMITED_API)]
ffi::PyList_SetItem(ptr, i, obj);
} }
core::mem::forget(guard);
Ok(array.assume_init())
}
}
#[cfg(test)] list
mod tests {
use super::*;
use std::{
panic,
sync::atomic::{AtomicUsize, Ordering},
};
#[test]
fn array_try_from_fn() {
static DROP_COUNTER: AtomicUsize = AtomicUsize::new(0);
struct CountDrop;
impl Drop for CountDrop {
fn drop(&mut self) {
DROP_COUNTER.fetch_add(1, Ordering::SeqCst);
}
}
let _ = catch_unwind_silent(move || {
let _: Result<[CountDrop; 4], ()> = super::array_try_from_fn(|idx| {
#[allow(clippy::manual_assert)]
if idx == 2 {
panic!("peek a boo");
}
Ok(CountDrop)
});
});
assert_eq!(DROP_COUNTER.load(Ordering::SeqCst), 2);
}
#[test]
fn test_extract_bytearray_to_array() {
Python::with_gil(|py| {
let v: [u8; 33] = py
.eval(
"bytearray(b'abcabcabcabcabcabcabcabcabcabcabc')",
None,
None,
)
.unwrap()
.extract()
.unwrap();
assert!(&v == b"abcabcabcabcabcabcabcabcabcabcabc");
})
}
// https://stackoverflow.com/a/59211505
fn catch_unwind_silent<F, R>(f: F) -> std::thread::Result<R>
where
F: FnOnce() -> R + panic::UnwindSafe,
{
let prev_hook = panic::take_hook();
panic::set_hook(Box::new(|_| {}));
let result = panic::catch_unwind(f);
panic::set_hook(prev_hook);
result
} }
} }
} }
#[cfg(not(min_const_generics))] impl<T, const N: usize> ToPyObject for [T; N]
mod array_impls { where
use super::invalid_sequence_length; T: ToPyObject,
use crate::conversion::{AsPyPointer, IntoPyPointer}; {
use crate::types::PySequence; fn to_object(&self, py: Python<'_>) -> PyObject {
use crate::{ self.as_ref().to_object(py)
ffi, FromPyObject, IntoPy, Py, PyAny, PyDowncastError, PyObject, PyResult, Python, }
ToPyObject, }
impl<'a, T, const N: usize> FromPyObject<'a> for [T; N]
where
T: FromPyObject<'a>,
{
fn extract(obj: &'a PyAny) -> PyResult<Self> {
create_array_from_obj(obj)
}
}
fn create_array_from_obj<'s, T, const N: usize>(obj: &'s PyAny) -> PyResult<[T; N]>
where
T: FromPyObject<'s>,
{
// Types that pass `PySequence_Check` usually implement enough of the sequence protocol
// to support this function and if not, we will only fail extraction safely.
let seq: &PySequence = unsafe {
if ffi::PySequence_Check(obj.as_ptr()) != 0 {
obj.downcast_unchecked()
} else {
return Err(PyDowncastError::new(obj, "Sequence").into());
}
}; };
use std::mem::{transmute_copy, ManuallyDrop}; let seq_len = seq.len()?;
if seq_len != N {
return Err(invalid_sequence_length(N, seq_len));
}
array_try_from_fn(|idx| seq.get_item(idx).and_then(PyAny::extract))
}
macro_rules! array_impls { // TODO use std::array::try_from_fn, if that stabilises:
($($N:expr),+) => { // (https://github.com/rust-lang/rust/pull/75644)
$( fn array_try_from_fn<E, F, T, const N: usize>(mut cb: F) -> Result<[T; N], E>
impl<T> IntoPy<PyObject> for [T; $N] where
where F: FnMut(usize) -> Result<T, E>,
T: IntoPy<PyObject> {
{ // Helper to safely create arrays since the standard library doesn't
fn into_py(self, py: Python<'_>) -> PyObject { // provide one yet. Shouldn't be necessary in the future.
struct ArrayGuard<T, const N: usize> {
dst: *mut T,
initialized: usize,
}
struct ArrayGuard<T> { impl<T, const N: usize> Drop for ArrayGuard<T, N> {
elements: [ManuallyDrop<T>; $N], fn drop(&mut self) {
start: usize, debug_assert!(self.initialized <= N);
} let initialized_part = core::ptr::slice_from_raw_parts_mut(self.dst, self.initialized);
unsafe {
impl<T> Drop for ArrayGuard<T> { core::ptr::drop_in_place(initialized_part);
fn drop(&mut self) { }
unsafe {
let needs_drop = self.elements.get_mut(self.start..).unwrap();
for item in needs_drop {
ManuallyDrop::drop(item);
}
}
}
}
unsafe {
let ptr = ffi::PyList_New($N as ffi::Py_ssize_t);
// We create the `Py` pointer here for two reasons:
// - panics if the ptr is null
// - its Drop cleans up the list if user code panics.
let list: Py<PyAny> = Py::from_owned_ptr(py, ptr);
let slf = ManuallyDrop::new(self);
let mut guard = ArrayGuard {
// the transmute size check is _very_ dumb around generics
elements: transmute_copy(&slf),
start: 0
};
for i in 0..$N {
let obj: T = ManuallyDrop::take(&mut guard.elements[i]);
guard.start += 1;
let obj = obj.into_py(py).into_ptr();
#[cfg(not(Py_LIMITED_API))]
ffi::PyList_SET_ITEM(ptr, i as ffi::Py_ssize_t, obj);
#[cfg(Py_LIMITED_API)]
ffi::PyList_SetItem(ptr, i as ffi::Py_ssize_t, obj);
}
std::mem::forget(guard);
list
}
}
}
impl<T> ToPyObject for [T; $N]
where
T: ToPyObject,
{
fn to_object(&self, py: Python<'_>) -> PyObject {
self.as_ref().to_object(py)
}
}
impl<'a, T> FromPyObject<'a> for [T; $N]
where
T: Copy + Default + FromPyObject<'a>,
{
fn extract(obj: &'a PyAny) -> PyResult<Self> {
let mut array = [T::default(); $N];
extract_sequence_into_slice(obj, &mut array)?;
Ok(array)
}
}
)+
} }
} }
#[cfg(not(min_const_generics))] // [MaybeUninit<T>; N] would be "nicer" but is actually difficult to create - there are nightly
array_impls!( // APIs which would make this easier.
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, let mut array: core::mem::MaybeUninit<[T; N]> = core::mem::MaybeUninit::uninit();
25, 26, 27, 28, 29, 30, 31, 32 let mut guard: ArrayGuard<T, N> = ArrayGuard {
); dst: array.as_mut_ptr() as _,
initialized: 0,
#[cfg(not(min_const_generics))] };
fn extract_sequence_into_slice<'s, T>(obj: &'s PyAny, slice: &mut [T]) -> PyResult<()> unsafe {
where let mut value_ptr = array.as_mut_ptr() as *mut T;
T: FromPyObject<'s>, for i in 0..N {
{ core::ptr::write(value_ptr, cb(i)?);
// Types that pass `PySequence_Check` usually implement enough of the sequence protocol value_ptr = value_ptr.offset(1);
// to support this function and if not, we will only fail extraction safely. guard.initialized += 1;
let seq: &PySequence = unsafe {
if ffi::PySequence_Check(obj.as_ptr()) != 0 {
obj.downcast_unchecked()
} else {
return Err(PyDowncastError::new(obj, "Sequence").into());
}
};
let seq_len = seq.len()?;
if seq_len != slice.len() {
return Err(invalid_sequence_length(slice.len(), seq_len));
} }
for (value, item) in slice.iter_mut().zip(seq.iter()?) { core::mem::forget(guard);
*value = item?.extract::<T>()?; Ok(array.assume_init())
}
Ok(())
} }
} }
@ -315,8 +125,50 @@ fn invalid_sequence_length(expected: usize, actual: usize) -> PyErr {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::{
panic,
sync::atomic::{AtomicUsize, Ordering},
};
use crate::{types::PyList, IntoPy, PyResult, Python, ToPyObject}; use crate::{types::PyList, IntoPy, PyResult, Python, ToPyObject};
#[test]
fn array_try_from_fn() {
static DROP_COUNTER: AtomicUsize = AtomicUsize::new(0);
struct CountDrop;
impl Drop for CountDrop {
fn drop(&mut self) {
DROP_COUNTER.fetch_add(1, Ordering::SeqCst);
}
}
let _ = catch_unwind_silent(move || {
let _: Result<[CountDrop; 4], ()> = super::array_try_from_fn(|idx| {
#[allow(clippy::manual_assert)]
if idx == 2 {
panic!("peek a boo");
}
Ok(CountDrop)
});
});
assert_eq!(DROP_COUNTER.load(Ordering::SeqCst), 2);
}
#[test]
fn test_extract_bytearray_to_array() {
Python::with_gil(|py| {
let v: [u8; 33] = py
.eval(
"bytearray(b'abcabcabcabcabcabcabcabcabcabcabc')",
None,
None,
)
.unwrap()
.extract()
.unwrap();
assert!(&v == b"abcabcabcabcabcabcabcabcabcabcabc");
})
}
#[test] #[test]
fn test_extract_small_bytearray_to_array() { fn test_extract_small_bytearray_to_array() {
Python::with_gil(|py| { Python::with_gil(|py| {
@ -390,4 +242,16 @@ mod tests {
let _cell: &crate::PyCell<Foo> = list.get_item(4).unwrap().extract().unwrap(); let _cell: &crate::PyCell<Foo> = list.get_item(4).unwrap().extract().unwrap();
}); });
} }
// https://stackoverflow.com/a/59211505
fn catch_unwind_silent<F, R>(f: F) -> std::thread::Result<R>
where
F: FnOnce() -> R + panic::UnwindSafe,
{
let prev_hook = panic::take_hook();
panic::set_hook(Box::new(|_| {}));
let result = panic::catch_unwind(f);
panic::set_hook(prev_hook);
result
}
} }