Skip to content

Commit

Permalink
docs: implement final missing safety docs
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Jun 23, 2021
1 parent 2cba18f commit 4f6e93f
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 23 deletions.
20 changes: 12 additions & 8 deletions src/class/impl_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
pycell::PyCellLayout,
pyclass_init::PyObjectInit,
type_object::{PyLayout, PyTypeObject},
PyCell, PyClass, PyMethodDefType, PyNativeType, PyTypeInfo, Python,
PyClass, PyMethodDefType, PyNativeType, PyTypeInfo, Python,
};
use std::{marker::PhantomData, os::raw::c_void, thread};

Expand Down Expand Up @@ -136,6 +136,10 @@ pub trait PyClassWithFreeList: PyClass {
}

/// Implementation of tp_alloc for `freelist` classes.
///
/// # Safety
/// - `subtype` must be a valid pointer to the type object of T or a subclass.
/// - The GIL must be held.
pub unsafe extern "C" fn alloc_with_freelist<T: PyClassWithFreeList>(
subtype: *mut ffi::PyTypeObject,
nitems: ffi::Py_ssize_t,
Expand All @@ -159,6 +163,10 @@ pub unsafe extern "C" fn alloc_with_freelist<T: PyClassWithFreeList>(
}

/// Implementation of tp_free for `freelist` classes.
///
/// # Safety
/// - `obj` must be a valid pointer to an instance of T (not a subclass).
/// - The GIL must be held.
#[allow(clippy::collapsible_if)] // for if cfg!
pub unsafe extern "C" fn free_with_freelist<T: PyClassWithFreeList>(obj: *mut c_void) {
let obj = obj as *mut ffi::PyObject;
Expand Down Expand Up @@ -392,8 +400,7 @@ impl<T: PyClass> PyClassBaseType for T {
type Initializer = crate::pyclass_init::PyClassInitializer<Self>;
}

// Default new implementation

/// Default new implementation
pub(crate) unsafe extern "C" fn fallback_new(
_subtype: *mut ffi::PyTypeObject,
_args: *mut ffi::PyObject,
Expand All @@ -406,10 +413,7 @@ pub(crate) unsafe extern "C" fn fallback_new(
})
}

/// Implementation of tp_dealloc for all pyclasses
pub(crate) unsafe extern "C" fn tp_dealloc<T: PyClass>(obj: *mut ffi::PyObject) {
crate::callback_body!(py, {
// Safety: Python will only call tp_dealloc when no references to the object remain.
let cell: &mut PyCell<T> = &mut *(obj as *mut _);
cell.tp_dealloc(py);
})
crate::callback_body!(py, T::Layout::tp_dealloc(obj, py))
}
1 change: 0 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#![cfg_attr(feature = "nightly", feature(specialization))]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![allow(clippy::missing_safety_doc)] // FIXME (#698)

//! Rust bindings to the Python interpreter.
//!
Expand Down
29 changes: 16 additions & 13 deletions src/pycell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -675,8 +675,11 @@ impl From<PyBorrowMutError> for PyErr {
pub trait PyCellLayout<T>: PyLayout<T> {
fn get_borrow_flag(&self) -> BorrowFlag;
fn set_borrow_flag(&self, flag: BorrowFlag);
/// Implementation of tp_dealloc. Do not attempt to use &self after calling this!
unsafe fn tp_dealloc(&mut self, py: Python);
/// Implementation of tp_dealloc.
/// # Safety
/// - slf must be a valid pointer to an instance of a T or a subclass.
/// - slf must not be used after this call (as it will be freed).
unsafe fn tp_dealloc(slf: *mut ffi::PyObject, py: Python);
}

impl<T, U> PyCellLayout<T> for PyCellBase<U>
Expand All @@ -690,21 +693,19 @@ where
fn set_borrow_flag(&self, flag: BorrowFlag) {
self.borrow_flag.set(flag)
}
unsafe fn tp_dealloc(&mut self, py: Python) {
let obj: *mut ffi::PyObject = self as *mut _ as *mut _;

unsafe fn tp_dealloc(slf: *mut ffi::PyObject, py: Python) {
// For `#[pyclass]` types which inherit from PyAny, we can just call tp_free
if T::type_object_raw(py) == &mut PyBaseObject_Type {
return get_tp_free(ffi::Py_TYPE(obj))(obj as _);
return get_tp_free(ffi::Py_TYPE(slf))(slf as _);
}

// More complex native types (e.g. `extends=PyDict`) require calling the base's dealloc.
#[cfg(not(Py_LIMITED_API))]
{
if let Some(dealloc) = (*T::type_object_raw(py)).tp_dealloc {
dealloc(obj as _);
dealloc(slf as _);
} else {
get_tp_free(ffi::Py_TYPE(obj))(obj as _);
get_tp_free(ffi::Py_TYPE(slf))(slf as _);
}
}

Expand All @@ -724,10 +725,12 @@ where
fn set_borrow_flag(&self, flag: BorrowFlag) {
self.ob_base.set_borrow_flag(flag)
}
unsafe fn tp_dealloc(&mut self, py: Python) {
ManuallyDrop::drop(&mut self.contents.value);
self.contents.dict.clear_dict(py);
self.contents.weakref.clear_weakrefs(self.as_ptr(), py);
self.ob_base.tp_dealloc(py);
unsafe fn tp_dealloc(slf: *mut ffi::PyObject, py: Python) {
// Safety: Python only calls tp_dealloc when no references to the object remain.
let cell = &mut *(slf as *mut PyCell<T>);
ManuallyDrop::drop(&mut cell.contents.value);
cell.contents.dict.clear_dict(py);
cell.contents.weakref.clear_weakrefs(slf, py);
<T::BaseType as PyClassBaseType>::LayoutAsBase::tp_dealloc(slf, py)
}
}
4 changes: 3 additions & 1 deletion src/pyclass_init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ use std::{
/// This trait is intended to use internally for distinguishing `#[pyclass]` and
/// Python native types.
pub trait PyObjectInit<T>: Sized {
/// # Safety
/// - `subtype` must be a valid pointer to a type object of T or a subclass.
unsafe fn into_new_object(
self,
py: Python,
Expand Down Expand Up @@ -185,7 +187,7 @@ impl<T: PyClass> PyClassInitializer<T> {
/// Called by our `tp_new` generated by the `#[new]` attribute.
///
/// # Safety
/// `subtype` must be a subclass of T.
/// `subtype` must be a valid pointer to the type object of T or a subclass.
#[doc(hidden)]
pub unsafe fn create_cell_from_subtype(
self,
Expand Down
3 changes: 3 additions & 0 deletions src/pyclass_slots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ pub trait PyClassDict {
pub trait PyClassWeakRef {
const IS_DUMMY: bool = true;
fn new() -> Self;
/// # Safety
/// - `_obj` must be a pointer to the pyclass instance which contains `self`.
/// - The GIL must be held.
#[inline]
unsafe fn clear_weakrefs(&mut self, _obj: *mut ffi::PyObject, _py: Python) {}
private_decl! {}
Expand Down

0 comments on commit 4f6e93f

Please sign in to comment.