From eb04f546831bce2adfe1f07a3b3263225d37763f Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Mon, 5 Jun 2023 09:42:40 +0200 Subject: [PATCH] Remove cfg min_const_generics made unnecessary by MSRV bump. --- build.rs | 2 +- pyo3-build-config/src/lib.rs | 5 - pyo3-ffi/build.rs | 2 +- src/conversions/std/array.rs | 442 ++++++++++++----------------------- 4 files changed, 155 insertions(+), 296 deletions(-) diff --git a/build.rs b/build.rs index 78cebf9a..207f122c 100644 --- a/build.rs +++ b/build.rs @@ -46,7 +46,7 @@ fn configure_pyo3() -> Result<()> { 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(); Ok(()) diff --git a/pyo3-build-config/src/lib.rs b/pyo3-build-config/src/lib.rs index 7ab24ee3..401f0ebd 100644 --- a/pyo3-build-config/src/lib.rs +++ b/pyo3-build-config/src/lib.rs @@ -142,11 +142,6 @@ pub fn print_feature_cfgs() { 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 if rustc_minor_version >= 51 { println!("cargo:rustc-cfg=addr_of"); diff --git a/pyo3-ffi/build.rs b/pyo3-ffi/build.rs index 1dd37820..c472003d 100644 --- a/pyo3-ffi/build.rs +++ b/pyo3-ffi/build.rs @@ -100,7 +100,7 @@ fn configure_pyo3() -> Result<()> { println!("{}", line); } - // Emit cfgs like `addr_of` and `min_const_generics` + // Emit cfgs like `thread_local_const_init` print_feature_cfgs(); Ok(()) diff --git a/src/conversions/std/array.rs b/src/conversions/std/array.rs index 6274bba7..0cbf3eb6 100644 --- a/src/conversions/std/array.rs +++ b/src/conversions/std/array.rs @@ -1,308 +1,118 @@ +use crate::conversion::{AsPyPointer, IntoPyPointer}; +use crate::types::PySequence; use crate::{exceptions, PyErr}; +use crate::{ + ffi, FromPyObject, IntoPy, Py, PyAny, PyDowncastError, PyObject, PyResult, Python, ToPyObject, +}; -#[cfg(min_const_generics)] -mod min_const_generics { - use super::invalid_sequence_length; - use crate::conversion::{AsPyPointer, IntoPyPointer}; - use crate::types::PySequence; - use crate::{ - ffi, FromPyObject, IntoPy, Py, PyAny, PyDowncastError, PyObject, PyResult, Python, - ToPyObject, - }; - - impl IntoPy for [T; N] - where - T: IntoPy, - { - 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 = 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 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 { - 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(mut cb: F) -> Result<[T; N], E> - where - F: FnMut(usize) -> Result, - { - // Helper to safely create arrays since the standard library doesn't - // provide one yet. Shouldn't be necessary in the future. - struct ArrayGuard { - dst: *mut T, - initialized: usize, - } - - impl Drop for ArrayGuard { - 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; 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 = ArrayGuard { - dst: array.as_mut_ptr() as _, - initialized: 0, - }; +impl IntoPy for [T; N] +where + T: IntoPy, +{ + fn into_py(self, py: Python<'_>) -> PyObject { unsafe { - let mut value_ptr = array.as_mut_ptr() as *mut T; - for i in 0..N { - core::ptr::write(value_ptr, cb(i)?); - value_ptr = value_ptr.offset(1); - guard.initialized += 1; + #[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 = 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)] - 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: F) -> std::thread::Result - 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 + list } } } -#[cfg(not(min_const_generics))] -mod array_impls { - use super::invalid_sequence_length; - use crate::conversion::{AsPyPointer, IntoPyPointer}; - use crate::types::PySequence; - use crate::{ - ffi, FromPyObject, IntoPy, Py, PyAny, PyDowncastError, PyObject, PyResult, Python, - ToPyObject, +impl 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 { + 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 { - ($($N:expr),+) => { - $( - impl IntoPy for [T; $N] - where - T: IntoPy - { - fn into_py(self, py: Python<'_>) -> PyObject { +// TODO use std::array::try_from_fn, if that stabilises: +// (https://github.com/rust-lang/rust/pull/75644) +fn array_try_from_fn(mut cb: F) -> Result<[T; N], E> +where + F: FnMut(usize) -> Result, +{ + // Helper to safely create arrays since the standard library doesn't + // provide one yet. Shouldn't be necessary in the future. + struct ArrayGuard { + dst: *mut T, + initialized: usize, + } - struct ArrayGuard { - elements: [ManuallyDrop; $N], - start: usize, - } - - impl Drop for ArrayGuard { - 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 = 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 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 { - let mut array = [T::default(); $N]; - extract_sequence_into_slice(obj, &mut array)?; - Ok(array) - } - } - )+ + impl Drop for ArrayGuard { + 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); + } } } - #[cfg(not(min_const_generics))] - array_impls!( - 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, - 25, 26, 27, 28, 29, 30, 31, 32 - ); - - #[cfg(not(min_const_generics))] - fn extract_sequence_into_slice<'s, T>(obj: &'s PyAny, slice: &mut [T]) -> PyResult<()> - 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 != slice.len() { - return Err(invalid_sequence_length(slice.len(), seq_len)); + // [MaybeUninit; 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 = ArrayGuard { + dst: array.as_mut_ptr() as _, + initialized: 0, + }; + unsafe { + let mut value_ptr = array.as_mut_ptr() as *mut T; + for i in 0..N { + core::ptr::write(value_ptr, cb(i)?); + value_ptr = value_ptr.offset(1); + guard.initialized += 1; } - for (value, item) in slice.iter_mut().zip(seq.iter()?) { - *value = item?.extract::()?; - } - Ok(()) + core::mem::forget(guard); + Ok(array.assume_init()) } } @@ -315,8 +125,50 @@ fn invalid_sequence_length(expected: usize, actual: usize) -> PyErr { #[cfg(test)] mod tests { + use std::{ + panic, + sync::atomic::{AtomicUsize, Ordering}, + }; + 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] fn test_extract_small_bytearray_to_array() { Python::with_gil(|py| { @@ -390,4 +242,16 @@ mod tests { let _cell: &crate::PyCell = list.get_item(4).unwrap().extract().unwrap(); }); } + + // https://stackoverflow.com/a/59211505 + fn catch_unwind_silent(f: F) -> std::thread::Result + 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 + } }