Skip to content

Commit

Permalink
better mutability inheritance rules
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Apr 21, 2022
1 parent 7118e94 commit e9bd41e
Show file tree
Hide file tree
Showing 12 changed files with 555 additions and 142 deletions.
22 changes: 6 additions & 16 deletions guide/src/class.md
Original file line number Diff line number Diff line change
Expand Up @@ -956,13 +956,7 @@ unsafe impl ::pyo3::type_object::PyTypeInfo for MyClass {
}
}

impl ::pyo3::PyClass for MyClass {
type Dict = ::pyo3::impl_::pyclass::PyClassDummySlot;
type WeakRef = ::pyo3::impl_::pyclass::PyClassDummySlot;
type BaseNativeType = ::pyo3::PyAny;
}

unsafe impl ::pyo3::pyclass::MutablePyClass for MyClass {}
impl ::pyo3::PyClass for MyClass { }

impl<'a> ::pyo3::derive_utils::ExtractExt<'a> for &'a mut MyClass {
type Target = ::pyo3::PyRefMut<'a, MyClass>;
Expand All @@ -985,7 +979,11 @@ impl pyo3::impl_::pyclass::PyClassImpl for MyClass {
type Layout = PyCell<MyClass>;
type BaseType = PyAny;
type ThreadChecker = pyo3::impl_::pyclass::ThreadCheckerStub<MyClass>;
type Mutabilty = pyo3::pyclass::Mutable;
type Mutability = pyo3::pycell::Mutable;
type PyClassMutability = pyo3::pycell::MutableClass;
type Dict = ::pyo3::impl_::pyclass::PyClassDummySlot;
type WeakRef = ::pyo3::impl_::pyclass::PyClassDummySlot;
type BaseNativeType = ::pyo3::PyAny;

fn for_all_items(visitor: &mut dyn FnMut(&pyo3::impl_::pyclass::PyClassItems)) {
use pyo3::impl_::pyclass::*;
Expand All @@ -996,14 +994,6 @@ impl pyo3::impl_::pyclass::PyClassImpl for MyClass {
}
}

impl ::pyo3::impl_::pyclass::PyClassDescriptors<MyClass>
for ::pyo3::impl_::pyclass::PyClassImplCollector<MyClass>
{
fn py_class_descriptors(self) -> &'static [::pyo3::class::methods::PyMethodDefType] {
static METHODS: &[::pyo3::class::methods::PyMethodDefType] = &[];
METHODS
}
}
# Python::with_gil(|py| {
# let cls = py.get_type::<MyClass>();
# pyo3::py_run!(py, cls, "assert cls.__name__ == 'MyClass'")
Expand Down
60 changes: 36 additions & 24 deletions pyo3-macros-backend/src/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -689,32 +689,9 @@ impl<'a> PyClassImplsBuilder<'a> {

fn impl_pyclass(&self) -> TokenStream {
let cls = self.cls;
let attr = self.attr;
let dict = if attr.options.dict.is_some() {
quote! { _pyo3::impl_::pyclass::PyClassDictSlot }
} else {
quote! { _pyo3::impl_::pyclass::PyClassDummySlot }
};

// insert space for weak ref
let weakref = if attr.options.weakref.is_some() {
quote! { _pyo3::impl_::pyclass::PyClassWeakRefSlot }
} else {
quote! { _pyo3::impl_::pyclass::PyClassDummySlot }
};

let base_nativetype = if attr.options.extends.is_some() {
quote! { <Self::BaseType as _pyo3::impl_::pyclass::PyClassBaseType<Self::Mutability>>::BaseNativeType }
} else {
quote! { _pyo3::PyAny }
};

quote! {
impl _pyo3::PyClass for #cls {
type Dict = #dict;
type WeakRef = #weakref;
type BaseNativeType = #base_nativetype;
}
impl _pyo3::PyClass for #cls { }
}
}
fn impl_extractext(&self) -> TokenStream {
Expand Down Expand Up @@ -856,6 +833,37 @@ impl<'a> PyClassImplsBuilder<'a> {
}
};

let class_mutability = if self.attr.options.immutable.is_some() {
quote! {
ImmutableChild
}
} else {
quote! {
MutableChild
}
};

let cls = self.cls;
let attr = self.attr;
let dict = if attr.options.dict.is_some() {
quote! { _pyo3::impl_::pyclass::PyClassDictSlot }
} else {
quote! { _pyo3::impl_::pyclass::PyClassDummySlot }
};

// insert space for weak ref
let weakref = if attr.options.weakref.is_some() {
quote! { _pyo3::impl_::pyclass::PyClassWeakRefSlot }
} else {
quote! { _pyo3::impl_::pyclass::PyClassDummySlot }
};

let base_nativetype = if attr.options.extends.is_some() {
quote! { <Self::BaseType as _pyo3::impl_::pyclass::PyClassBaseType>::BaseNativeType }
} else {
quote! { _pyo3::PyAny }
};

quote! {
impl _pyo3::impl_::pyclass::PyClassImpl for #cls {
const DOC: &'static str = #doc;
Expand All @@ -868,6 +876,10 @@ impl<'a> PyClassImplsBuilder<'a> {
type ThreadChecker = #thread_checker;
#inventory
type Mutability = #mutability;
type PyClassMutability = <<#base as _pyo3::impl_::pyclass::PyClassBaseType>::PyClassMutability as _pyo3::pycell::PyClassMutability>::#class_mutability;
type Dict = #dict;
type WeakRef = #weakref;
type BaseNativeType = #base_nativetype;

fn for_all_items(visitor: &mut dyn ::std::ops::FnMut(& _pyo3::impl_::pyclass::PyClassItems)) {
use _pyo3::impl_::pyclass::*;
Expand Down
30 changes: 22 additions & 8 deletions src/impl_/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ use crate::{
exceptions::{PyAttributeError, PyNotImplementedError},
ffi,
impl_::freelist::FreeList,
pycell::{Mutability, Mutable, PyCellLayout},
pyclass::MutablePyClass,
pycell::{GetBorrowChecker, Mutability, PyCellLayout, PyClassMutability},
pyclass_init::PyObjectInit,
type_object::{PyLayout, PyTypeObject},
Py, PyAny, PyCell, PyClass, PyErr, PyMethodDefType, PyNativeType, PyResult, PyTypeInfo, Python,
Expand Down Expand Up @@ -163,11 +162,24 @@ pub trait PyClassImpl: Sized {
type Layout: PyLayout<Self>;

/// Base class
type BaseType: PyTypeInfo + PyTypeObject + PyClassBaseType<Self::Mutability>;
type BaseType: PyTypeInfo + PyTypeObject + PyClassBaseType;

/// Immutable or mutable
type Mutability: Mutability;

/// Immutable or mutable
type PyClassMutability: PyClassMutability + GetBorrowChecker<Self>;

/// Specify this class has `#[pyclass(dict)]` or not.
type Dict: PyClassDict;

/// Specify this class has `#[pyclass(weakref)]` or not.
type WeakRef: PyClassWeakRef;

/// The closest native ancestor. This is `PyAny` by default, and when you declare
/// `#[pyclass(extends=PyDict)]`, it's `PyDict`.
type BaseNativeType: PyTypeInfo + PyNativeType;

/// This handles following two situations:
/// 1. In case `T` is `Send`, stub `ThreadChecker` is used and does nothing.
/// This implementation is used by default. Compile fails if `T: !Send`.
Expand Down Expand Up @@ -870,12 +882,12 @@ impl<T> PyClassThreadChecker<T> for ThreadCheckerImpl<T> {
/// Thread checker for types that have `Send` and `extends=...`.
/// Ensures that `T: Send` and the parent is not accessed by another thread.
#[doc(hidden)]
pub struct ThreadCheckerInherited<T: PyClass + Send, U: PyClassBaseType<T::Mutability>>(
pub struct ThreadCheckerInherited<T: PyClass + Send, U: PyClassBaseType>(
PhantomData<T>,
U::ThreadChecker,
);

impl<T: PyClass + Send, U: PyClassBaseType<T::Mutability>> PyClassThreadChecker<T>
impl<T: PyClass + Send, U: PyClassBaseType> PyClassThreadChecker<T>
for ThreadCheckerInherited<T, U>
{
fn ensure(&self) {
Expand All @@ -888,21 +900,23 @@ impl<T: PyClass + Send, U: PyClassBaseType<T::Mutability>> PyClassThreadChecker<
}

/// Trait denoting that this class is suitable to be used as a base type for PyClass.
pub trait PyClassBaseType<M: Mutability>: Sized {
type LayoutAsBase: PyCellLayout<Self, M>;
pub trait PyClassBaseType: Sized {
type LayoutAsBase: PyCellLayout<Self>;
type BaseNativeType;
type ThreadChecker: PyClassThreadChecker<Self>;
type Initializer: PyObjectInit<Self>;
type PyClassMutability: PyClassMutability;
}

/// All mutable PyClasses can be used as a base type.
///
/// In the future this will be extended to immutable PyClasses too.
impl<T: MutablePyClass> PyClassBaseType<Mutable> for T {
impl<T: PyClass> PyClassBaseType for T {
type LayoutAsBase = crate::pycell::PyCell<T>;
type BaseNativeType = T::BaseNativeType;
type ThreadChecker = T::ThreadChecker;
type Initializer = crate::pyclass_init::PyClassInitializer<Self>;
type PyClassMutability = T::PyClassMutability;
}

/// Implementation of tp_dealloc for all pyclasses
Expand Down
Loading

0 comments on commit e9bd41e

Please sign in to comment.