Skip to content

Commit

Permalink
llvm-lines: use iterator to collect class items
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Jun 25, 2022
1 parent 3227b54 commit 926e889
Show file tree
Hide file tree
Showing 7 changed files with 270 additions and 71 deletions.
5 changes: 2 additions & 3 deletions guide/src/class.md
Original file line number Diff line number Diff line change
Expand Up @@ -1001,12 +1001,11 @@ impl pyo3::impl_::pyclass::PyClassImpl for MyClass {
type WeakRef = ::pyo3::impl_::pyclass::PyClassDummySlot;
type BaseNativeType = ::pyo3::PyAny;

fn for_all_items(visitor: &mut dyn FnMut(&pyo3::impl_::pyclass::PyClassItems)) {
fn items_iter() -> pyo3::impl_::pyclass::PyClassItemsIter {
use pyo3::impl_::pyclass::*;
let collector = PyClassImplCollector::<MyClass>::new();
static INTRINSIC_ITEMS: PyClassItems = PyClassItems { slots: &[], methods: &[] };
visitor(&INTRINSIC_ITEMS);
visitor(collector.py_methods());
PyClassItemsIter::new(&INTRINSIC_ITEMS, collector.py_methods())
}
}

Expand Down
41 changes: 22 additions & 19 deletions pyo3-macros-backend/src/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -859,9 +859,7 @@ impl<'a> PyClassImplsBuilder<'a> {
};

let (pymethods_items, inventory, inventory_class) = match self.methods_type {
PyClassMethodsType::Specialization => {
(quote! { visitor(collector.py_methods()); }, None, None)
}
PyClassMethodsType::Specialization => (quote! { collector.py_methods() }, None, None),
PyClassMethodsType::Inventory => {
// To allow multiple #[pymethods] block, we define inventory types.
let inventory_class_name = syn::Ident::new(
Expand All @@ -870,9 +868,12 @@ impl<'a> PyClassImplsBuilder<'a> {
);
(
quote! {
for inventory in _pyo3::inventory::iter::<<Self as _pyo3::impl_::pyclass::PyClassImpl>::Inventory>() {
visitor(_pyo3::impl_::pyclass::PyClassInventory::items(inventory));
}
::std::boxed::Box::new(
::std::iter::Iterator::map(
_pyo3::inventory::iter::<<Self as _pyo3::impl_::pyclass::PyClassImpl>::Inventory>(),
_pyo3::impl_::pyclass::PyClassInventory::items
)
)
},
Some(quote! { type Inventory = #inventory_class_name; }),
Some(define_inventory_class(&inventory_class_name)),
Expand All @@ -882,15 +883,15 @@ impl<'a> PyClassImplsBuilder<'a> {

let pyproto_items = if cfg!(feature = "pyproto") {
Some(quote! {
visitor(collector.object_protocol_items());
visitor(collector.number_protocol_items());
visitor(collector.iter_protocol_items());
visitor(collector.gc_protocol_items());
visitor(collector.descr_protocol_items());
visitor(collector.mapping_protocol_items());
visitor(collector.sequence_protocol_items());
visitor(collector.async_protocol_items());
visitor(collector.buffer_protocol_items());
collector.object_protocol_items(),
collector.number_protocol_items(),
collector.iter_protocol_items(),
collector.gc_protocol_items(),
collector.descr_protocol_items(),
collector.mapping_protocol_items(),
collector.sequence_protocol_items(),
collector.async_protocol_items(),
collector.buffer_protocol_items(),
})
} else {
None
Expand Down Expand Up @@ -959,17 +960,19 @@ impl<'a> PyClassImplsBuilder<'a> {
type WeakRef = #weakref;
type BaseNativeType = #base_nativetype;

fn for_all_items(visitor: &mut dyn ::std::ops::FnMut(& _pyo3::impl_::pyclass::PyClassItems)) {
fn items_iter() -> _pyo3::impl_::pyclass::PyClassItemsIter {
use _pyo3::impl_::pyclass::*;
let collector = PyClassImplCollector::<Self>::new();
#deprecations;
static INTRINSIC_ITEMS: PyClassItems = PyClassItems {
methods: &[#(#default_method_defs),*],
slots: &[#(#default_slot_defs),* #(#freelist_slots),*],
};
visitor(&INTRINSIC_ITEMS);
#pymethods_items
#pyproto_items
PyClassItemsIter::new(
&INTRINSIC_ITEMS,
#pymethods_items,
#pyproto_items
)
}

#dict_offset
Expand Down
208 changes: 203 additions & 5 deletions src/impl_/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ pub trait PyClassImpl: Sized {
#[cfg(feature = "multiple-pymethods")]
type Inventory: PyClassInventory;

fn for_all_items(visitor: &mut dyn FnMut(&PyClassItems));
fn items_iter() -> PyClassItemsIter;

#[inline]
fn dict_offset() -> Option<ffi::Py_ssize_t> {
Expand All @@ -189,6 +189,208 @@ pub trait PyClassImpl: Sized {
}
}

/// Iterator used to process all class items during type instantiation.
pub struct PyClassItemsIter {
/// Iteration state
idx: usize,
/// Items from the `#[pyclass]` macro
pyclass_items: &'static PyClassItems,
/// Items from the `#[pymethods]` macro
#[cfg(not(feature = "multiple-pymethods"))]
pymethods_items: &'static PyClassItems,
/// Items from the `#[pymethods]` macro with inventory
#[cfg(feature = "multiple-pymethods")]
pymethods_items: Box<dyn Iterator<Item = &'static PyClassItems>>,

// pyproto items, to be removed soon.
#[cfg(feature = "pyproto")]
object_protocol_items: &'static PyClassItems,
#[cfg(feature = "pyproto")]
descr_protocol_items: &'static PyClassItems,
#[cfg(feature = "pyproto")]
gc_protocol_items: &'static PyClassItems,
#[cfg(feature = "pyproto")]
iter_protocol_items: &'static PyClassItems,
#[cfg(feature = "pyproto")]
mapping_protocol_items: &'static PyClassItems,
#[cfg(feature = "pyproto")]
number_protocol_items: &'static PyClassItems,
#[cfg(feature = "pyproto")]
async_protocol_items: &'static PyClassItems,
#[cfg(feature = "pyproto")]
sequence_protocol_items: &'static PyClassItems,
#[cfg(feature = "pyproto")]
buffer_protocol_items: &'static PyClassItems,
}

impl PyClassItemsIter {
#[cfg_attr(feature = "pyproto", allow(clippy::too_many_arguments))]
pub fn new(
pyclass_items: &'static PyClassItems,
#[cfg(not(feature = "multiple-pymethods"))] pymethods_items: &'static PyClassItems,
#[cfg(feature = "multiple-pymethods")] pymethods_items: Box<
dyn Iterator<Item = &'static PyClassItems>,
>,
#[cfg(feature = "pyproto")] object_protocol_items: &'static PyClassItems,
#[cfg(feature = "pyproto")] descr_protocol_items: &'static PyClassItems,
#[cfg(feature = "pyproto")] gc_protocol_items: &'static PyClassItems,
#[cfg(feature = "pyproto")] iter_protocol_items: &'static PyClassItems,
#[cfg(feature = "pyproto")] mapping_protocol_items: &'static PyClassItems,
#[cfg(feature = "pyproto")] number_protocol_items: &'static PyClassItems,
#[cfg(feature = "pyproto")] async_protocol_items: &'static PyClassItems,
#[cfg(feature = "pyproto")] sequence_protocol_items: &'static PyClassItems,
#[cfg(feature = "pyproto")] buffer_protocol_items: &'static PyClassItems,
) -> Self {
Self {
idx: 0,
pyclass_items,
pymethods_items,
#[cfg(feature = "pyproto")]
object_protocol_items,
#[cfg(feature = "pyproto")]
descr_protocol_items,
#[cfg(feature = "pyproto")]
gc_protocol_items,
#[cfg(feature = "pyproto")]
iter_protocol_items,
#[cfg(feature = "pyproto")]
mapping_protocol_items,
#[cfg(feature = "pyproto")]
number_protocol_items,
#[cfg(feature = "pyproto")]
async_protocol_items,
#[cfg(feature = "pyproto")]
sequence_protocol_items,
#[cfg(feature = "pyproto")]
buffer_protocol_items,
}
}
}

impl Iterator for PyClassItemsIter {
type Item = &'static PyClassItems;

#[cfg(not(feature = "multiple-pymethods"))]
fn next(&mut self) -> Option<Self::Item> {
match self.idx {
0 => {
self.idx += 1;
Some(self.pyclass_items)
}
1 => {
self.idx += 1;
Some(self.pymethods_items)
}
#[cfg(feature = "pyproto")]
2 => {
self.idx += 1;
Some(self.object_protocol_items)
}
#[cfg(feature = "pyproto")]
3 => {
self.idx += 1;
Some(self.descr_protocol_items)
}
#[cfg(feature = "pyproto")]
4 => {
self.idx += 1;
Some(self.gc_protocol_items)
}
#[cfg(feature = "pyproto")]
5 => {
self.idx += 1;
Some(self.iter_protocol_items)
}
#[cfg(feature = "pyproto")]
6 => {
self.idx += 1;
Some(self.mapping_protocol_items)
}
#[cfg(feature = "pyproto")]
7 => {
self.idx += 1;
Some(self.number_protocol_items)
}
#[cfg(feature = "pyproto")]
8 => {
self.idx += 1;
Some(self.async_protocol_items)
}
#[cfg(feature = "pyproto")]
9 => {
self.idx += 1;
Some(self.sequence_protocol_items)
}
#[cfg(feature = "pyproto")]
10 => {
self.idx += 1;
Some(self.buffer_protocol_items)
}
// Termination clause
// NB self.idx reaches different final value (2 vs 11) depending on pyproto feature
_ => None,
}
}

#[cfg(feature = "multiple-pymethods")]
fn next(&mut self) -> Option<Self::Item> {
match self.idx {
0 => {
self.idx += 1;
Some(self.pyclass_items)
}
#[cfg(feature = "pyproto")]
1 => {
self.idx += 1;
Some(self.object_protocol_items)
}
#[cfg(feature = "pyproto")]
2 => {
self.idx += 1;
Some(self.descr_protocol_items)
}
#[cfg(feature = "pyproto")]
3 => {
self.idx += 1;
Some(self.gc_protocol_items)
}
#[cfg(feature = "pyproto")]
4 => {
self.idx += 1;
Some(self.iter_protocol_items)
}
#[cfg(feature = "pyproto")]
5 => {
self.idx += 1;
Some(self.mapping_protocol_items)
}
#[cfg(feature = "pyproto")]
6 => {
self.idx += 1;
Some(self.number_protocol_items)
}
#[cfg(feature = "pyproto")]
7 => {
self.idx += 1;
Some(self.async_protocol_items)
}
#[cfg(feature = "pyproto")]
8 => {
self.idx += 1;
Some(self.sequence_protocol_items)
}
#[cfg(feature = "pyproto")]
9 => {
self.idx += 1;
Some(self.buffer_protocol_items)
}
// Termination clause
// NB self.idx reaches different final value (1 vs 10) depending on pyproto feature
_ => self.pymethods_items.next(),
}
}
}

// Traits describing known special methods.

macro_rules! slot_fragment_trait {
Expand Down Expand Up @@ -809,10 +1011,6 @@ mod pyproto_traits {
#[cfg(feature = "pyproto")]
pub use pyproto_traits::*;

// Protocol slots from #[pymethods] if not using inventory.
#[cfg(not(feature = "multiple-pymethods"))]
items_trait!(PyMethodsProtocolItems, methods_protocol_items);

// Thread checkers

#[doc(hidden)]
Expand Down
21 changes: 9 additions & 12 deletions src/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
ffi,
impl_::pyclass::{
assign_sequence_item_from_mapping, get_sequence_item_from_mapping, tp_dealloc, PyClassImpl,
PyClassItems,
PyClassItemsIter,
},
IntoPy, IntoPyPointer, PyCell, PyErr, PyMethodDefType, PyObject, PyResult, PyTypeInfo, Python,
};
Expand Down Expand Up @@ -48,7 +48,7 @@ where
tp_dealloc::<T>,
T::dict_offset(),
T::weaklist_offset(),
&T::for_all_items,
T::items_iter,
T::IS_BASETYPE,
T::IS_MAPPING,
)
Expand All @@ -69,7 +69,7 @@ unsafe fn create_type_object_impl(
tp_dealloc: ffi::destructor,
dict_offset: Option<ffi::Py_ssize_t>,
weaklist_offset: Option<ffi::Py_ssize_t>,
for_all_items: &dyn Fn(&mut dyn FnMut(&PyClassItems)),
get_items_iter: fn() -> PyClassItemsIter,
is_basetype: bool,
is_mapping: bool,
) -> PyResult<*mut ffi::PyTypeObject> {
Expand Down Expand Up @@ -97,7 +97,7 @@ unsafe fn create_type_object_impl(
let PyClassInfo {
method_defs,
property_defs,
} = method_defs_to_pyclass_info(for_all_items, dict_offset.is_none());
} = method_defs_to_pyclass_info(get_items_iter(), dict_offset.is_none());

// normal methods
if !method_defs.is_empty() {
Expand All @@ -120,7 +120,7 @@ unsafe fn create_type_object_impl(
#[cfg(all(not(Py_3_9), not(Py_LIMITED_API)))]
let mut buffer_procs: ffi::PyBufferProcs = Default::default();

for_all_items(&mut |items| {
for items in get_items_iter() {
for slot in items.slots {
match slot.slot {
ffi::Py_tp_new => has_new = true,
Expand All @@ -142,7 +142,7 @@ unsafe fn create_type_object_impl(
}
}
slots.extend_from_slice(items.slots);
});
}

if !is_mapping {
// If mapping methods implemented, define sequence methods get implemented too.
Expand Down Expand Up @@ -320,14 +320,11 @@ struct PyClassInfo {
property_defs: Vec<ffi::PyGetSetDef>,
}

fn method_defs_to_pyclass_info(
for_all_items: &dyn Fn(&mut dyn FnMut(&PyClassItems)),
has_dict: bool,
) -> PyClassInfo {
fn method_defs_to_pyclass_info(items_iter: PyClassItemsIter, has_dict: bool) -> PyClassInfo {
let mut method_defs = Vec::new();
let mut property_defs_map = std::collections::HashMap::new();

for_all_items(&mut |items| {
for items in items_iter {
for def in items.methods {
match def {
PyMethodDefType::Getter(getter) => {
Expand All @@ -350,7 +347,7 @@ fn method_defs_to_pyclass_info(
PyMethodDefType::ClassAttribute(_) => {}
}
}
});
}

// TODO: use into_values when on MSRV Rust >= 1.54
let mut property_defs: Vec<_> = property_defs_map
Expand Down
Loading

0 comments on commit 926e889

Please sign in to comment.