Skip to content

Commit

Permalink
rework create_cell to create_class_object
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Mar 1, 2024
1 parent 0b95215 commit ac28db3
Show file tree
Hide file tree
Showing 10 changed files with 68 additions and 81 deletions.
3 changes: 1 addition & 2 deletions pyo3-macros-backend/src/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -645,8 +645,7 @@ impl<'a> FnSpec<'a> {
#( #holders )*
let result = #call;
let initializer: _pyo3::PyClassInitializer::<#cls> = result.convert(py)?;
let cell = initializer.create_cell_from_subtype(py, _slf)?;
::std::result::Result::Ok(cell as *mut _pyo3::ffi::PyObject)
_pyo3::impl_::pymethods::tp_new_impl(py, initializer, _slf)
}
}
}
Expand Down
5 changes: 2 additions & 3 deletions src/impl_/coroutine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::{
pycell::impl_::PyClassBorrowChecker,
pyclass::boolean_struct::False,
types::{PyAnyMethods, PyString},
IntoPy, Py, PyAny, PyCell, PyClass, PyErr, PyObject, PyResult, Python,
IntoPy, Py, PyAny, PyClass, PyErr, PyObject, PyResult, Python,
};

pub fn new_coroutine<F, T, E>(
Expand All @@ -32,8 +32,7 @@ where
}

fn get_ptr<T: PyClass>(obj: &Py<T>) -> *mut T {
// SAFETY: Py<T> can be casted as *const PyCell<T>
unsafe { &*(obj.as_ptr() as *const PyCell<T>) }.get_ptr()
obj.get_class_object().get_ptr()
}

pub struct RefGuard<T: PyClass>(Py<T>);
Expand Down
8 changes: 4 additions & 4 deletions src/impl_/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use crate::{
pyclass_init::PyObjectInit,
types::any::PyAnyMethods,
types::PyBool,
Borrowed, Py, PyAny, PyCell, PyClass, PyErr, PyMethodDefType, PyNativeType, PyResult,
PyTypeInfo, Python,
Borrowed, Py, PyAny, PyClass, PyErr, PyMethodDefType, PyNativeType, PyResult, PyTypeInfo,
Python,
};
use std::{
borrow::Cow,
Expand All @@ -25,13 +25,13 @@ pub use lazy_type_object::LazyTypeObject;
/// Gets the offset of the dictionary from the start of the object in bytes.
#[inline]
pub fn dict_offset<T: PyClass>() -> ffi::Py_ssize_t {
PyCell::<T>::dict_offset()
PyClassObject::<T>::dict_offset()
}

/// Gets the offset of the weakref list from the start of the object in bytes.
#[inline]
pub fn weaklist_offset<T: PyClass>() -> ffi::Py_ssize_t {
PyCell::<T>::weaklist_offset()
PyClassObject::<T>::weaklist_offset()
}

/// Represents the `__dict__` field for `#[pyclass]`.
Expand Down
14 changes: 12 additions & 2 deletions src/impl_/pymethods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use crate::pycell::{PyBorrowError, PyBorrowMutError};
use crate::pyclass::boolean_struct::False;
use crate::types::{any::PyAnyMethods, PyModule, PyType};
use crate::{
ffi, Borrowed, Bound, DowncastError, Py, PyAny, PyCell, PyClass, PyErr, PyObject, PyRef,
PyRefMut, PyResult, PyTraverseError, PyTypeCheck, PyVisit, Python,
ffi, Borrowed, Bound, DowncastError, Py, PyAny, PyCell, PyClass, PyClassInitializer, PyErr,
PyObject, PyRef, PyRefMut, PyResult, PyTraverseError, PyTypeCheck, PyVisit, Python,
};
use std::borrow::Cow;
use std::ffi::CStr;
Expand Down Expand Up @@ -569,3 +569,13 @@ impl<'py, T> std::ops::Deref for BoundRef<'_, 'py, T> {
self.0
}
}

pub unsafe fn tp_new_impl<T: PyClass>(
py: Python<'_>,
initializer: PyClassInitializer<T>,
target_type: *mut ffi::PyTypeObject,
) -> PyResult<*mut ffi::PyObject> {
initializer
.create_class_object_of_type(py, target_type)
.map(Bound::into_ptr)
}
35 changes: 14 additions & 21 deletions src/instance.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::err::{self, PyDowncastError, PyErr, PyResult};
use crate::ffi_ptr_ext::FfiPtrExt;
use crate::impl_::pycell::PyClassObject;
use crate::pycell::{PyBorrowError, PyBorrowMutError, PyCell};
use crate::pyclass::boolean_struct::{False, True};
Expand Down Expand Up @@ -93,14 +92,7 @@ where
py: Python<'py>,
value: impl Into<PyClassInitializer<T>>,
) -> PyResult<Bound<'py, T>> {
let initializer = value.into();
let obj = initializer.create_cell(py)?;
let ob = unsafe {
obj.cast::<ffi::PyObject>()
.assume_owned(py)
.downcast_into_unchecked()
};
Ok(ob)
value.into().create_class_object(py)
}
}

Expand Down Expand Up @@ -338,10 +330,10 @@ where
}

pub(crate) fn get_class_object(&self) -> &PyClassObject<T> {
let cell = self.as_ptr().cast::<PyClassObject<T>>();
let class_object = self.as_ptr().cast::<PyClassObject<T>>();
// SAFETY: Bound<T> is known to contain an object which is laid out in memory as a
// PyClassObject<T>.
unsafe { &*cell }
unsafe { &*class_object }
}
}

Expand Down Expand Up @@ -884,10 +876,7 @@ where
/// # }
/// ```
pub fn new(py: Python<'_>, value: impl Into<PyClassInitializer<T>>) -> PyResult<Py<T>> {
let initializer = value.into();
let obj = initializer.create_cell(py)?;
let ob = unsafe { Py::from_owned_ptr(py, obj as _) };
Ok(ob)
Bound::new(py, value).map(Bound::unbind)
}
}

Expand Down Expand Up @@ -1191,12 +1180,16 @@ where
where
T: PyClass<Frozen = True> + Sync,
{
let any = self.as_ptr() as *const PyAny;
// SAFETY: The class itself is frozen and `Sync` and we do not access anything but `cell.contents.value`.
unsafe {
let cell: &PyCell<T> = PyNativeType::unchecked_downcast(&*any);
&*cell.get_ptr()
}
// SAFETY: The class itself is frozen and `Sync`
unsafe { &*self.get_class_object().get_ptr() }
}

/// Get a view on the underlying `PyClass` contents.
pub(crate) fn get_class_object(&self) -> &PyClassObject<T> {
let class_object = self.as_ptr().cast::<PyClassObject<T>>();
// SAFETY: Bound<T> is known to contain an object which is laid out in memory as a
// PyClassObject<T>.
unsafe { &*class_object }
}
}

Expand Down
19 changes: 1 addition & 18 deletions src/pycell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,6 @@
//! [guide]: https://pyo3.rs/latest/class.html#pycell-and-interior-mutability "PyCell and interior mutability"
//! [Interior Mutability]: https://doc.rust-lang.org/book/ch15-05-interior-mutability.html "RefCell<T> and the Interior Mutability Pattern - The Rust Programming Language"
#[allow(deprecated)]
use crate::conversion::FromPyPointer;
use crate::conversion::{AsPyPointer, ToPyObject};
use crate::exceptions::PyRuntimeError;
use crate::ffi_ptr_ext::FfiPtrExt;
Expand Down Expand Up @@ -272,12 +270,7 @@ impl<T: PyClass> PyCell<T> {
)
)]
pub fn new(py: Python<'_>, value: impl Into<PyClassInitializer<T>>) -> PyResult<&Self> {
unsafe {
let initializer = value.into();
let self_ = initializer.create_cell(py)?;
#[allow(deprecated)]
FromPyPointer::from_owned_ptr_or_err(py, self_ as _)
}
Bound::new(py, value).map(Bound::into_gil_ref)
}

/// Immutably borrows the value `T`. This borrow lasts as long as the returned `PyRef` exists.
Expand Down Expand Up @@ -483,16 +476,6 @@ impl<T: PyClass> PyCell<T> {
pub(crate) fn get_ptr(&self) -> *mut T {
self.0.get_ptr()
}

/// Gets the offset of the dictionary from the start of the struct in bytes.
pub(crate) fn dict_offset() -> ffi::Py_ssize_t {
PyClassObject::<T>::dict_offset()
}

/// Gets the offset of the weakref list from the start of the struct in bytes.
pub(crate) fn weaklist_offset() -> ffi::Py_ssize_t {
PyClassObject::<T>::weaklist_offset()
}
}

unsafe impl<T: PyClassImpl> PyLayout<T> for PyCell<T> {}
Expand Down
1 change: 0 additions & 1 deletion src/pycell/impl_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,6 @@ mod tests {

use crate::prelude::*;
use crate::pyclass::boolean_struct::{False, True};
use crate::PyClass;

#[pyclass(crate = "crate", subclass)]
struct MutableBase;
Expand Down
5 changes: 3 additions & 2 deletions src/pyclass/create_type_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use pyo3_ffi::PyType_IS_GC;
use crate::{
exceptions::PyTypeError,
ffi,
impl_::pycell::PyClassObject,
impl_::pyclass::{
assign_sequence_item_from_mapping, get_sequence_item_from_mapping, tp_dealloc,
tp_dealloc_with_gc, PyClassItemsIter,
Expand All @@ -13,7 +14,7 @@ use crate::{
},
types::typeobject::PyTypeMethods,
types::PyType,
Py, PyCell, PyClass, PyGetterDef, PyMethodDefType, PyResult, PySetterDef, PyTypeInfo, Python,
Py, PyClass, PyGetterDef, PyMethodDefType, PyResult, PySetterDef, PyTypeInfo, Python,
};
use std::{
borrow::Cow,
Expand Down Expand Up @@ -94,7 +95,7 @@ where
T::items_iter(),
T::NAME,
T::MODULE,
std::mem::size_of::<PyCell<T>>(),
std::mem::size_of::<PyClassObject<T>>(),
)
}
}
Expand Down
57 changes: 30 additions & 27 deletions src/pyclass_init.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
//! Contains initialization utilities for `#[pyclass]`.
use crate::callback::IntoPyCallbackOutput;
use crate::ffi_ptr_ext::FfiPtrExt;
use crate::impl_::pyclass::{PyClassBaseType, PyClassDict, PyClassThreadChecker, PyClassWeakRef};
use crate::{ffi, Py, PyCell, PyClass, PyErr, PyResult, Python};
use crate::types::PyAnyMethods;
use crate::{ffi, Bound, Py, PyClass, PyErr, PyResult, Python};
use crate::{
ffi::PyTypeObject,
pycell::impl_::{PyClassBorrowChecker, PyClassMutability, PyClassObjectContents},
Expand Down Expand Up @@ -204,56 +206,43 @@ impl<T: PyClass> PyClassInitializer<T> {
}

/// Creates a new PyCell and initializes it.
#[doc(hidden)]
pub fn create_cell(self, py: Python<'_>) -> PyResult<*mut PyCell<T>>
pub(crate) fn create_class_object(self, py: Python<'_>) -> PyResult<Bound<'_, T>>
where
T: PyClass,
{
unsafe { self.create_cell_from_subtype(py, T::type_object_raw(py)) }
unsafe { self.create_class_object_of_type(py, T::type_object_raw(py)) }
}

/// Creates a new PyCell and initializes it given a typeobject `subtype`.
/// Called by the Python `tp_new` implementation generated by a `#[new]` function in a `#[pymethods]` block.
/// Creates a new class object and initializes it given a typeobject `subtype`.
///
/// # Safety
/// `subtype` must be a valid pointer to the type object of T or a subclass.
#[doc(hidden)]
pub unsafe fn create_cell_from_subtype(
pub(crate) unsafe fn create_class_object_of_type(
self,
py: Python<'_>,
subtype: *mut crate::ffi::PyTypeObject,
) -> PyResult<*mut PyCell<T>>
target_type: *mut crate::ffi::PyTypeObject,
) -> PyResult<Bound<'_, T>>
where
T: PyClass,
{
self.into_new_object(py, subtype).map(|obj| obj as _)
}
}

impl<T: PyClass> PyObjectInit<T> for PyClassInitializer<T> {
unsafe fn into_new_object(
self,
py: Python<'_>,
subtype: *mut PyTypeObject,
) -> PyResult<*mut ffi::PyObject> {
/// Layout of a PyCell after base new has been called, but the contents have not yet been
/// Layout of a PyClassObject after base new has been called, but the contents have not yet been
/// written.
#[repr(C)]
struct PartiallyInitializedPyCell<T: PyClass> {
struct PartiallyInitializedClassObject<T: PyClass> {
_ob_base: <T::BaseType as PyClassBaseType>::LayoutAsBase,
contents: MaybeUninit<PyClassObjectContents<T>>,
}

let (init, super_init) = match self.0 {
PyClassInitializerImpl::Existing(value) => return Ok(value.into_ptr()),
PyClassInitializerImpl::Existing(value) => return Ok(value.into_bound(py)),
PyClassInitializerImpl::New { init, super_init } => (init, super_init),
};

let obj = super_init.into_new_object(py, subtype)?;
let obj = super_init.into_new_object(py, target_type)?;

let cell: *mut PartiallyInitializedPyCell<T> = obj as _;
let part_init: *mut PartiallyInitializedClassObject<T> = obj as _;
std::ptr::write(
(*cell).contents.as_mut_ptr(),
(*part_init).contents.as_mut_ptr(),
PyClassObjectContents {
value: ManuallyDrop::new(UnsafeCell::new(init)),
borrow_checker: <T::PyClassMutability as PyClassMutability>::Storage::new(),
Expand All @@ -262,7 +251,21 @@ impl<T: PyClass> PyObjectInit<T> for PyClassInitializer<T> {
weakref: T::WeakRef::INIT,
},
);
Ok(obj)

// Safety: obj is a valid pointer to an object of type `target_type`, which` is a known
// subclass of `T`
Ok(obj.assume_owned(py).downcast_into_unchecked())
}
}

impl<T: PyClass> PyObjectInit<T> for PyClassInitializer<T> {
unsafe fn into_new_object(
self,
py: Python<'_>,
subtype: *mut PyTypeObject,
) -> PyResult<*mut ffi::PyObject> {
self.create_class_object_of_type(py, subtype)
.map(Bound::into_ptr)
}

private_impl! {}
Expand Down
2 changes: 1 addition & 1 deletion src/type_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::types::{PyAny, PyType};
use crate::{ffi, Bound, PyNativeType, Python};

/// `T: PyLayout<U>` represents that `T` is a concrete representation of `U` in the Python heap.
/// E.g., `PyCell` is a concrete representation of all `pyclass`es, and `ffi::PyObject`
/// E.g., `PyClassObject` is a concrete representation of all `pyclass`es, and `ffi::PyObject`
/// is of `PyAny`.
///
/// This trait is intended to be used internally.
Expand Down

0 comments on commit ac28db3

Please sign in to comment.