This commit is contained in:
mejrs 2021-11-27 22:50:01 +01:00
parent 63eda2f5c9
commit 64d85e5ebf
6 changed files with 88 additions and 73 deletions

View File

@ -637,6 +637,7 @@ impl<'a> PyClassImplsBuilder<'a> {
type Dict = #dict;
type WeakRef = #weakref;
type BaseNativeType = #base_nativetype;
type Mutability = ::pyo3::pycell::Immutable;
}
}
}
@ -763,8 +764,8 @@ impl<'a> PyClassImplsBuilder<'a> {
let mutability = if self.attr.is_immutable {
quote! {
::pyo3::pycell::Immutable
}
::pyo3::pycell::Immutable
}
} else {
quote! {
::pyo3::pycell::Mutable
@ -781,7 +782,6 @@ impl<'a> PyClassImplsBuilder<'a> {
type Layout = ::pyo3::PyCell<Self>;
type BaseType = #base;
type ThreadChecker = #thread_checker;
type Mutability= #mutability;
fn for_each_method_def(visitor: &mut dyn ::std::ops::FnMut(&[::pyo3::class::PyMethodDefType])) {
use ::pyo3::class::impl_::*;
@ -839,8 +839,8 @@ impl<'a> PyClassImplsBuilder<'a> {
let cls = self.cls;
if self.attr.is_immutable {
quote! {
unsafe impl ::pyo3::pyclass::ImmutablePyClass for #cls {}
}
unsafe impl ::pyo3::pyclass::ImmutablePyClass for #cls {}
}
} else {
quote! {
unsafe impl ::pyo3::pyclass::MutablePyClass for #cls {}

View File

@ -4,7 +4,7 @@ use crate::{
exceptions::{PyAttributeError, PyNotImplementedError},
ffi,
impl_::freelist::FreeList,
pycell::{PyCellLayout, Mutability},
pycell::{Mutability, PyCellLayout},
pyclass_init::PyObjectInit,
type_object::{PyLayout, PyTypeObject},
PyClass, PyMethodDefType, PyNativeType, PyResult, PyTypeInfo, Python,
@ -39,7 +39,7 @@ impl<T> Copy for PyClassImplCollector<T> {}
///
/// Users are discouraged from implementing this trait manually; it is a PyO3 implementation detail
/// and may be changed at any time.
pub trait PyClassImpl: Sized {
pub trait PyClassImpl<M: Mutability>: Sized {
/// Class doc string
const DOC: &'static str = "\0";
@ -56,7 +56,7 @@ pub trait PyClassImpl: Sized {
type Layout: PyLayout<Self>;
/// Base class
type BaseType: PyTypeInfo + PyTypeObject + PyClassBaseType;
type BaseType: PyTypeInfo + PyTypeObject + PyClassBaseType<M>;
/// This handles following two situations:
/// 1. In case `T` is `Send`, stub `ThreadChecker` is used and does nothing.
@ -67,8 +67,6 @@ pub trait PyClassImpl: Sized {
/// can be accessed by multiple threads by `threading` module.
type ThreadChecker: PyClassThreadChecker<Self>;
type Mutability: crate::pycell::Mutability;
fn for_each_method_def(_visitor: &mut dyn FnMut(&[PyMethodDefType])) {}
fn get_new() -> Option<ffi::newfunc> {
None
@ -749,9 +747,14 @@ 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: Send, U: PyClassBaseType>(PhantomData<T>, U::ThreadChecker);
pub struct ThreadCheckerInherited<T: Send, U: PyClassBaseType<M>, M: Mutability>(
PhantomData<T>,
U::ThreadChecker,
);
impl<T: Send, U: PyClassBaseType> PyClassThreadChecker<T> for ThreadCheckerInherited<T, U> {
impl<T: Send, U: PyClassBaseType<M>, M: Mutability> PyClassThreadChecker<T>
for ThreadCheckerInherited<T, U, M>
{
fn ensure(&self) {
self.1.ensure();
}
@ -762,25 +765,23 @@ impl<T: Send, U: PyClassBaseType> PyClassThreadChecker<T> for ThreadCheckerInher
}
/// Trait denoting that this class is suitable to be used as a base type for PyClass.
pub trait PyClassBaseType: Sized {
pub trait PyClassBaseType<M: Mutability>: Sized {
type Dict;
type WeakRef;
type LayoutAsBase: PyCellLayout<Self>;
type LayoutAsBase: PyCellLayout<Self, M>;
type BaseNativeType;
type ThreadChecker: PyClassThreadChecker<Self>;
type Initializer: PyObjectInit<Self>;
type Mutability: Mutability;
}
/// All PyClasses can be used as a base type.
impl<T: PyClass> PyClassBaseType for T {
impl<T: PyClass> PyClassBaseType<T::Mutability> for T {
type Dict = T::Dict;
type WeakRef = T::WeakRef;
type LayoutAsBase = crate::pycell::PyCell<T>;
type BaseNativeType = T::BaseNativeType;
type ThreadChecker = T::ThreadChecker;
type Initializer = crate::pyclass_init::PyClassInitializer<Self>;
type Mutability = T::Mutability;
}
/// Default new implementation

View File

@ -174,6 +174,7 @@
//! [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"
use crate::class::impl_::PyClassImpl;
use crate::exceptions::PyRuntimeError;
use crate::pyclass::{MutablePyClass, PyClass};
use crate::pyclass_init::PyClassInitializer;
@ -181,7 +182,6 @@ use crate::pyclass_slots::{PyClassDict, PyClassWeakRef};
use crate::type_object::{PyLayout, PySizedLayout};
use crate::types::PyAny;
use crate::{class::impl_::PyClassBaseType, class::impl_::PyClassThreadChecker};
use crate::class::impl_::PyClassImpl;
use crate::{
conversion::{AsPyPointer, FromPyPointer, ToPyObject},
ffi::PyBaseObject_Type,
@ -191,9 +191,9 @@ use crate::{
use crate::{ffi, IntoPy, PyErr, PyNativeType, PyObject, PyResult, Python};
use std::cell::{Cell, UnsafeCell};
use std::fmt;
use std::marker::PhantomData;
use std::mem::ManuallyDrop;
use std::ops::{Deref, DerefMut};
use std::marker::PhantomData;
pub trait Mutability {
/// Creates a new borrow checker
@ -202,7 +202,7 @@ pub trait Mutability {
fn try_borrow(&self) -> Result<(), PyBorrowError>;
fn try_borrow_unguarded(&self) -> Result<(), PyBorrowError>;
/// Decrements immutable borrow count
fn release_borrow(&self);
/// Increments mutable borrow count, if possible
@ -215,76 +215,74 @@ pub struct Mutable {
flag: Cell<BorrowFlag>,
}
impl Mutability for Mutable {
fn new() -> Self{
Self{flag: Cell::new(BorrowFlag::UNUSED)}
fn new() -> Self {
Self {
flag: Cell::new(BorrowFlag::UNUSED),
}
}
fn try_borrow(&self) -> Result<(), PyBorrowError>{
fn try_borrow(&self) -> Result<(), PyBorrowError> {
let flag = self.flag.get();
if flag != BorrowFlag::HAS_MUTABLE_BORROW {
self.flag.set(flag.increment());
Ok(())
}
else{
} else {
Err(PyBorrowError { _private: () })
}
}
fn try_borrow_unguarded(&self) -> Result<(), PyBorrowError>{
fn try_borrow_unguarded(&self) -> Result<(), PyBorrowError> {
let flag = self.flag.get();
if flag != BorrowFlag::HAS_MUTABLE_BORROW {
Ok(())
}
else{
} else {
Err(PyBorrowError { _private: () })
}
}
fn release_borrow(&self){
fn release_borrow(&self) {
let flag = self.flag.get();
self.flag.set(flag.decrement())
}
fn try_borrow_mut(&self) -> Result<(), PyBorrowMutError>{
fn try_borrow_mut(&self) -> Result<(), PyBorrowMutError> {
let flag = self.flag.get();
if flag == BorrowFlag::UNUSED {
self.flag.set(BorrowFlag::HAS_MUTABLE_BORROW);
Ok(())
}
else{
} else {
Err(PyBorrowMutError { _private: () })
}
}
fn release_borrow_mut(&self){
fn release_borrow_mut(&self) {
self.flag.set(BorrowFlag::UNUSED)
}
}
pub struct Immutable{
flag: PhantomData<Cell<BorrowFlag>>
pub struct Immutable {
flag: PhantomData<Cell<BorrowFlag>>,
}
impl Mutability for Immutable {
fn new() -> Self{
Self{flag: PhantomData}
fn new() -> Self {
Self { flag: PhantomData }
}
fn try_borrow(&self) -> Result<(), PyBorrowError>{
Ok(())
}
fn try_borrow_unguarded(&self) -> Result<(), PyBorrowError>{
fn try_borrow(&self) -> Result<(), PyBorrowError> {
Ok(())
}
fn release_borrow(&self){
fn try_borrow_unguarded(&self) -> Result<(), PyBorrowError> {
Ok(())
}
fn try_borrow_mut(&self) -> Result<(), PyBorrowMutError>{
fn release_borrow(&self) {}
fn try_borrow_mut(&self) -> Result<(), PyBorrowMutError> {
unreachable!()
}
fn release_borrow_mut(&self){
fn release_borrow_mut(&self) {
unreachable!()
}
}
@ -298,7 +296,12 @@ pub struct PyCellBase<T, M: Mutability> {
borrow_impl: M,
}
unsafe impl<T, U, M> PyLayout<T> for PyCellBase<U, M> where U: PySizedLayout<T>, M: Mutability {}
unsafe impl<T, U, M> PyLayout<T> for PyCellBase<U, M>
where
U: PySizedLayout<T>,
M: Mutability,
{
}
/// A container type for (mutably) accessing [`PyClass`] values
///
@ -337,7 +340,7 @@ unsafe impl<T, U, M> PyLayout<T> for PyCellBase<U, M> where U: PySizedLayout<T>,
/// [module-level documentation](self).
#[repr(C)]
pub struct PyCell<T: PyClass> {
ob_base: <T::BaseType as PyClassBaseType>::LayoutAsBase,
ob_base: <T::BaseType as PyClassBaseType<T::Mutability>>::LayoutAsBase,
contents: PyCellContents<T>,
}
@ -491,7 +494,9 @@ impl<T: PyClass> PyCell<T> {
/// });
/// ```
pub fn try_borrow(&self) -> Result<PyRef<'_, T>, PyBorrowError> {
self.borrow_checker().try_borrow().map(|_| PyRef { inner: self })
self.borrow_checker()
.try_borrow()
.map(|_| PyRef { inner: self })
}
/// Mutably borrows the value `T`, returning an error if the value is currently borrowed.
@ -519,7 +524,9 @@ impl<T: PyClass> PyCell<T> {
where
T: MutablePyClass,
{
self.borrow_checker().try_borrow_mut().map(|_| PyRefMut { inner: self })
self.borrow_checker()
.try_borrow_mut()
.map(|_| PyRefMut { inner: self })
}
/// Immutably borrows the value `T`, returning an error if the value is
@ -552,7 +559,9 @@ impl<T: PyClass> PyCell<T> {
/// });
/// ```
pub unsafe fn try_borrow_unguarded(&self) -> Result<&T, PyBorrowError> {
self.borrow_checker().try_borrow_unguarded().map(|_:()| &*self.contents.value.get())
self.borrow_checker()
.try_borrow_unguarded()
.map(|_: ()| &*self.contents.value.get())
}
/// Replaces the wrapped value with a new one, returning the old value.
@ -976,8 +985,11 @@ impl From<PyBorrowMutError> for PyErr {
}
#[doc(hidden)]
pub trait PyCellLayout<T>: PyLayout<T> {
fn borrow_checker(&self) -> &T::Mutability where T: PyClass;
pub trait PyCellLayout<T, M>: PyLayout<T>
where
M: Mutability,
{
fn borrow_checker(&self) -> &M;
/// Implementation of tp_dealloc.
/// # Safety
/// - slf must be a valid pointer to an instance of a T or a subclass.
@ -985,14 +997,13 @@ pub trait PyCellLayout<T>: PyLayout<T> {
unsafe fn tp_dealloc(slf: *mut ffi::PyObject, py: Python);
}
impl<T, U, M> PyCellLayout<T> for PyCellBase<U, M>
impl<T, U, M> PyCellLayout<T, M> for PyCellBase<U, M>
where
U: PySizedLayout<T>,
T: PyTypeInfo,
M: Mutability
M: Mutability,
{
fn borrow_checker(&self) -> &T::Mutability where T: PyClass{
fn borrow_checker(&self) -> &M {
&self.borrow_impl
}
@ -1017,11 +1028,12 @@ where
}
}
impl<T: PyClass> PyCellLayout<T> for PyCell<T>
impl<T: PyClass> PyCellLayout<T, T::Mutability> for PyCell<T>
where
<T::BaseType as PyClassBaseType>::LayoutAsBase: PyCellLayout<T::BaseType>,
<T::BaseType as PyClassBaseType<T::Mutability>>::LayoutAsBase:
PyCellLayout<T::BaseType, T::Mutability>,
{
fn borrow_checker(&self) -> &T ::Mutability {
fn borrow_checker(&self) -> &T::Mutability {
self.contents.thread_checker.ensure();
self.ob_base.borrow_checker()
}
@ -1031,6 +1043,6 @@ where
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)
<T::BaseType as PyClassBaseType<T::Mutability>>::LayoutAsBase::tp_dealloc(slf, py)
}
}

View File

@ -1,11 +1,11 @@
//! `PyClass` and related traits.
use crate::pycell::Mutability;
use crate::{
class::impl_::{fallback_new, tp_dealloc, PyClassImpl},
ffi,
pyclass_slots::{PyClassDict, PyClassWeakRef},
PyCell, PyErr, PyMethodDefType, PyNativeType, PyResult, PyTypeInfo, Python,
};
use crate::pycell::Mutability;
use std::{
convert::TryInto,
ffi::CString,
@ -19,7 +19,7 @@ use std::{
/// The `#[pyclass]` attribute automatically implements this trait for your Rust struct,
/// so you normally don't have to use this trait directly.
pub trait PyClass:
PyTypeInfo<AsRefTarget = PyCell<Self>> + PyClassImpl<Layout = PyCell<Self>>
PyTypeInfo<AsRefTarget = PyCell<Self>> + PyClassImpl<Self::Mutability, Layout = PyCell<Self>>
{
/// Specify this class has `#[pyclass(dict)]` or not.
type Dict: PyClassDict;
@ -29,7 +29,7 @@ pub trait PyClass:
/// `#[pyclass(extends=PyDict)]`, it's `PyDict`.
type BaseNativeType: PyTypeInfo + PyNativeType;
//type Mutability: Mutability;
type Mutability: Mutability;
}
pub unsafe trait MutablePyClass: PyClass {}

View File

@ -127,14 +127,17 @@ impl<T: PyTypeInfo> PyObjectInit<T> for PyNativeTypeInitializer<T> {
/// ```
pub struct PyClassInitializer<T: PyClass> {
init: T,
super_init: <T::BaseType as PyClassBaseType>::Initializer,
super_init: <T::BaseType as PyClassBaseType<T::Mutability>>::Initializer,
}
impl<T: PyClass> PyClassInitializer<T> {
/// Constructs a new initializer from value `T` and base class' initializer.
///
/// It is recommended to use `add_subclass` instead of this method for most usage.
pub fn new(init: T, super_init: <T::BaseType as PyClassBaseType>::Initializer) -> Self {
pub fn new(
init: T,
super_init: <T::BaseType as PyClassBaseType<T::Mutability>>::Initializer,
) -> Self {
Self { init, super_init }
}
@ -187,7 +190,7 @@ impl<T: PyClass> PyClassInitializer<T> {
pub fn add_subclass<S>(self, subclass_value: S) -> PyClassInitializer<S>
where
S: PyClass<BaseType = T>,
S::BaseType: PyClassBaseType<Initializer = Self>,
S::BaseType: PyClassBaseType<T::Mutability, Initializer = Self>,
{
PyClassInitializer::new(subclass_value, self)
}
@ -228,16 +231,16 @@ impl<T: PyClass> PyObjectInit<T> for PyClassInitializer<T> {
/// Layout of a PyCellBase after base new has been called, but the borrow flag has not
/// yet been initialized.
#[repr(C)]
struct PartiallyInitializedPyCellBase<T> {
struct PartiallyInitializedPyCellBase<T: PyClass> {
_ob_base: T,
borrow_flag: MaybeUninit<Cell<BorrowFlag>>,
borrow_flag: MaybeUninit<T::Mutability>,
}
/// Layout of a PyCell after base new has been called, but the contents have not yet been
/// written.
#[repr(C)]
struct PartiallyInitializedPyCell<T: PyClass> {
_ob_base: <T::BaseType as PyClassBaseType>::LayoutAsBase,
_ob_base: <T::BaseType as PyClassBaseType<T::Mutability>>::LayoutAsBase,
contents: MaybeUninit<PyCellContents<T>>,
}
@ -271,7 +274,7 @@ impl<T: PyClass> PyObjectInit<T> for PyClassInitializer<T> {
impl<T> From<T> for PyClassInitializer<T>
where
T: PyClass,
T::BaseType: PyClassBaseType<Initializer = PyNativeTypeInitializer<T::BaseType>>,
T::BaseType: PyClassBaseType<T::Mutability, Initializer = PyNativeTypeInitializer<T::BaseType>>,
{
#[inline]
fn from(value: T) -> PyClassInitializer<T> {
@ -283,7 +286,7 @@ impl<S, B> From<(S, B)> for PyClassInitializer<S>
where
S: PyClass<BaseType = B>,
B: PyClass,
B::BaseType: PyClassBaseType<Initializer = PyNativeTypeInitializer<B::BaseType>>,
B::BaseType: PyClassBaseType<Initializer = PyNativeTypeInitializer<B::BaseType>>,
{
fn from(sub_and_base: (S, B)) -> PyClassInitializer<S> {
let (sub, base) = sub_and_base;

View File

@ -195,14 +195,13 @@ macro_rules! pyobject_native_type_sized {
($name:ty, $layout:path $(;$generics:ident)*) => {
unsafe impl $crate::type_object::PyLayout<$name> for $layout {}
impl $crate::type_object::PySizedLayout<$name> for $layout {}
impl<'a, $($generics,)*> $crate::class::impl_::PyClassBaseType for $name {
impl<'a, $($generics,)*> $crate::class::impl_::PyClassBaseType<$crate::pycell::Mutable> for $name {
type Dict = $crate::pyclass_slots::PyClassDummySlot;
type WeakRef = $crate::pyclass_slots::PyClassDummySlot;
type LayoutAsBase = $crate::pycell::PyCellBase<$layout, $crate::pycell::Mutable>;
type BaseNativeType = $name;
type ThreadChecker = $crate::class::impl_::ThreadCheckerStub<$crate::PyObject>;
type Initializer = $crate::pyclass_init::PyNativeTypeInitializer<Self>;
type Mutability = $crate::pycell::Mutable;
}
}
}