better mutability inheritance rules
This commit is contained in:
parent
7118e94947
commit
e9bd41efb2
|
@ -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>;
|
||||
|
@ -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::*;
|
||||
|
@ -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'")
|
||||
|
|
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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::*;
|
||||
|
|
|
@ -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,
|
||||
|
@ -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`.
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
|
278
src/pycell.rs
278
src/pycell.rs
|
@ -173,7 +173,9 @@
|
|||
//! [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::exceptions::PyRuntimeError;
|
||||
use crate::impl_::pyclass::{PyClassBaseType, PyClassDict, PyClassThreadChecker, PyClassWeakRef};
|
||||
use crate::impl_::pyclass::{
|
||||
PyClassBaseType, PyClassDict, PyClassImpl, PyClassThreadChecker, PyClassWeakRef,
|
||||
};
|
||||
use crate::pyclass::{MutablePyClass, PyClass};
|
||||
use crate::pyclass_init::PyClassInitializer;
|
||||
use crate::type_object::{PyLayout, PySizedLayout};
|
||||
|
@ -191,6 +193,212 @@ use std::marker::PhantomData;
|
|||
use std::mem::ManuallyDrop;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
pub struct EmptySlot(());
|
||||
pub struct BorrowChecker(Cell<BorrowFlag>);
|
||||
pub struct FilledInAncestor(());
|
||||
|
||||
impl BorrowChecker {
|
||||
fn try_borrow(&self) -> Result<(), PyBorrowError> {
|
||||
let flag = self.0.get();
|
||||
if flag != BorrowFlag::HAS_MUTABLE_BORROW {
|
||||
self.0.set(flag.increment());
|
||||
Ok(())
|
||||
} else {
|
||||
Err(PyBorrowError { _private: () })
|
||||
}
|
||||
}
|
||||
|
||||
fn try_borrow_unguarded(&self) -> Result<(), PyBorrowError> {
|
||||
let flag = self.0.get();
|
||||
if flag != BorrowFlag::HAS_MUTABLE_BORROW {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(PyBorrowError { _private: () })
|
||||
}
|
||||
}
|
||||
|
||||
fn release_borrow(&self) {
|
||||
let flag = self.0.get();
|
||||
self.0.set(flag.decrement())
|
||||
}
|
||||
|
||||
fn try_borrow_mut(&self) -> Result<(), PyBorrowMutError> {
|
||||
let flag = self.0.get();
|
||||
if flag == BorrowFlag::UNUSED {
|
||||
self.0.set(BorrowFlag::HAS_MUTABLE_BORROW);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(PyBorrowMutError { _private: () })
|
||||
}
|
||||
}
|
||||
|
||||
fn release_borrow_mut(&self) {
|
||||
self.0.set(BorrowFlag::UNUSED)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PyClassMutabilityStorage {
|
||||
fn new() -> Self;
|
||||
}
|
||||
|
||||
impl PyClassMutabilityStorage for EmptySlot {
|
||||
fn new() -> Self {
|
||||
Self(())
|
||||
}
|
||||
}
|
||||
|
||||
impl PyClassMutabilityStorage for BorrowChecker {
|
||||
fn new() -> Self {
|
||||
Self(Cell::new(BorrowFlag::UNUSED))
|
||||
}
|
||||
}
|
||||
|
||||
// - Storage type, either empty, present, or in ancestor
|
||||
// - Mutability is either
|
||||
// - Immutable - i.e. EmptySlot
|
||||
// - Mutable - i.e. BorrowChecker
|
||||
// - ExtendsMutableAncestor<Mutability> - FilledInAncestor
|
||||
// - Mutability trait needs to encode the inheritance
|
||||
|
||||
pub trait PyClassMutability {
|
||||
// The storage for this inheritance layer. Only the first mutable class in
|
||||
// an inheritance hierarchy needs to store the borrow flag.
|
||||
type Storage: PyClassMutabilityStorage;
|
||||
// The borrow flag needed to implement this class' mutability. Empty until
|
||||
// the first mutable class, at which point it is BorrowChecker and will be
|
||||
// for all subclasses.
|
||||
type Checker;
|
||||
type ImmutableChild: PyClassMutability;
|
||||
type MutableChild: PyClassMutability;
|
||||
|
||||
/// Increments immutable borrow count, if possible
|
||||
fn try_borrow(checker: &Self::Checker) -> Result<(), PyBorrowError>;
|
||||
|
||||
fn try_borrow_unguarded(checker: &Self::Checker) -> Result<(), PyBorrowError>;
|
||||
|
||||
/// Decrements immutable borrow count
|
||||
fn release_borrow(checker: &Self::Checker);
|
||||
/// Increments mutable borrow count, if possible
|
||||
fn try_borrow_mut(checker: &Self::Checker) -> Result<(), PyBorrowMutError>;
|
||||
/// Decremements mutable borrow count
|
||||
fn release_borrow_mut(checker: &Self::Checker);
|
||||
}
|
||||
|
||||
pub trait GetBorrowChecker<T: PyClassImpl> {
|
||||
fn borrow_checker(cell: &PyCell<T>) -> &<T::PyClassMutability as PyClassMutability>::Checker;
|
||||
}
|
||||
|
||||
impl<T: PyClassImpl<PyClassMutability = Self>> GetBorrowChecker<T> for MutableClass {
|
||||
fn borrow_checker(cell: &PyCell<T>) -> &BorrowChecker {
|
||||
&cell.contents.borrow_checker
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PyClassImpl<PyClassMutability = Self>> GetBorrowChecker<T> for ImmutableClass {
|
||||
fn borrow_checker(cell: &PyCell<T>) -> &EmptySlot {
|
||||
&cell.contents.borrow_checker
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PyClassImpl<PyClassMutability = Self>, M: PyClassMutability> GetBorrowChecker<T>
|
||||
for ExtendsMutableAncestor<M>
|
||||
where
|
||||
T::BaseType: PyClassImpl<Layout = PyCell<T::BaseType>>
|
||||
+ PyClassBaseType<LayoutAsBase = PyCell<T::BaseType>>,
|
||||
<T::BaseType as PyClassImpl>::PyClassMutability: PyClassMutability<Checker = BorrowChecker>,
|
||||
{
|
||||
fn borrow_checker(cell: &PyCell<T>) -> &BorrowChecker {
|
||||
<<T::BaseType as PyClassImpl>::PyClassMutability as GetBorrowChecker<T::BaseType>>::borrow_checker(&cell.ob_base)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ImmutableClass(());
|
||||
pub struct MutableClass(());
|
||||
pub struct ExtendsMutableAncestor<M: PyClassMutability>(PhantomData<M>);
|
||||
|
||||
impl PyClassMutability for ImmutableClass {
|
||||
type Storage = EmptySlot;
|
||||
type Checker = EmptySlot;
|
||||
type ImmutableChild = ImmutableClass;
|
||||
type MutableChild = MutableClass;
|
||||
|
||||
fn try_borrow(_: &EmptySlot) -> Result<(), PyBorrowError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn try_borrow_unguarded(_: &EmptySlot) -> Result<(), PyBorrowError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn release_borrow(_: &EmptySlot) {}
|
||||
|
||||
fn try_borrow_mut(_: &EmptySlot) -> Result<(), PyBorrowMutError> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn release_borrow_mut(_: &EmptySlot) {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
impl PyClassMutability for MutableClass {
|
||||
type Storage = BorrowChecker;
|
||||
type Checker = BorrowChecker;
|
||||
type ImmutableChild = ExtendsMutableAncestor<ImmutableClass>;
|
||||
type MutableChild = ExtendsMutableAncestor<MutableClass>;
|
||||
|
||||
// FIXME the below are all wrong
|
||||
|
||||
fn try_borrow(checker: &BorrowChecker) -> Result<(), PyBorrowError> {
|
||||
checker.try_borrow()
|
||||
}
|
||||
|
||||
fn try_borrow_unguarded(checker: &BorrowChecker) -> Result<(), PyBorrowError> {
|
||||
checker.try_borrow_unguarded()
|
||||
}
|
||||
|
||||
fn release_borrow(checker: &BorrowChecker) {
|
||||
checker.release_borrow()
|
||||
}
|
||||
|
||||
fn try_borrow_mut(checker: &BorrowChecker) -> Result<(), PyBorrowMutError> {
|
||||
checker.try_borrow_mut()
|
||||
}
|
||||
|
||||
fn release_borrow_mut(checker: &BorrowChecker) {
|
||||
checker.release_borrow_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: PyClassMutability> PyClassMutability for ExtendsMutableAncestor<M> {
|
||||
type Storage = EmptySlot;
|
||||
type Checker = BorrowChecker;
|
||||
type ImmutableChild = ExtendsMutableAncestor<ImmutableClass>;
|
||||
type MutableChild = ExtendsMutableAncestor<MutableClass>;
|
||||
|
||||
// FIXME the below are all wrong
|
||||
|
||||
fn try_borrow(checker: &BorrowChecker) -> Result<(), PyBorrowError> {
|
||||
checker.try_borrow()
|
||||
}
|
||||
|
||||
fn try_borrow_unguarded(checker: &BorrowChecker) -> Result<(), PyBorrowError> {
|
||||
checker.try_borrow_unguarded()
|
||||
}
|
||||
|
||||
fn release_borrow(checker: &BorrowChecker) {
|
||||
checker.release_borrow()
|
||||
}
|
||||
|
||||
fn try_borrow_mut(checker: &BorrowChecker) -> Result<(), PyBorrowMutError> {
|
||||
checker.try_borrow_mut()
|
||||
}
|
||||
|
||||
fn release_borrow_mut(checker: &BorrowChecker) {
|
||||
checker.release_borrow_mut()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Mutability {
|
||||
/// Creates a new borrow checker
|
||||
fn new() -> Self;
|
||||
|
@ -284,20 +492,13 @@ impl Mutability for Immutable {
|
|||
}
|
||||
|
||||
/// Base layout of PyCell.
|
||||
/// This is necessary for sharing BorrowFlag between parents and children.
|
||||
#[doc(hidden)]
|
||||
#[repr(C)]
|
||||
pub struct PyCellBase<T, M: Mutability> {
|
||||
pub struct PyCellBase<T> {
|
||||
ob_base: T,
|
||||
borrow_impl: M,
|
||||
}
|
||||
|
||||
unsafe impl<T, U, M> PyLayout<T> for PyCellBase<U, M>
|
||||
where
|
||||
U: PySizedLayout<T>,
|
||||
M: Mutability,
|
||||
{
|
||||
}
|
||||
unsafe impl<T, U> PyLayout<T> for PyCellBase<U> where U: PySizedLayout<T> {}
|
||||
|
||||
/// A container type for (mutably) accessing [`PyClass`] values
|
||||
///
|
||||
|
@ -335,14 +536,15 @@ where
|
|||
/// For more information on how, when and why (not) to use `PyCell` please see the
|
||||
/// [module-level documentation](self).
|
||||
#[repr(C)]
|
||||
pub struct PyCell<T: PyClass> {
|
||||
ob_base: <T::BaseType as PyClassBaseType<T::Mutability>>::LayoutAsBase,
|
||||
pub struct PyCell<T: PyClassImpl> {
|
||||
ob_base: <T::BaseType as PyClassBaseType>::LayoutAsBase,
|
||||
contents: PyCellContents<T>,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub(crate) struct PyCellContents<T: PyClass> {
|
||||
pub(crate) struct PyCellContents<T: PyClassImpl> {
|
||||
pub(crate) value: ManuallyDrop<UnsafeCell<T>>,
|
||||
pub(crate) borrow_checker: <T::PyClassMutability as PyClassMutability>::Storage,
|
||||
pub(crate) thread_checker: T::ThreadChecker,
|
||||
pub(crate) dict: T::Dict,
|
||||
pub(crate) weakref: T::WeakRef,
|
||||
|
@ -412,9 +614,8 @@ impl<T: PyClass> PyCell<T> {
|
|||
/// });
|
||||
/// ```
|
||||
pub fn try_borrow(&self) -> Result<PyRef<'_, T>, PyBorrowError> {
|
||||
self.borrow_checker()
|
||||
.try_borrow()
|
||||
.map(|_| PyRef { inner: self })
|
||||
self.ensure_threadsafe();
|
||||
T::PyClassMutability::try_borrow(self.borrow_checker()).map(|_| PyRef { inner: self })
|
||||
}
|
||||
|
||||
/// Mutably borrows the value `T`, returning an error if the value is currently borrowed.
|
||||
|
@ -442,8 +643,8 @@ impl<T: PyClass> PyCell<T> {
|
|||
where
|
||||
T: MutablePyClass,
|
||||
{
|
||||
self.borrow_checker()
|
||||
.try_borrow_mut()
|
||||
self.ensure_threadsafe();
|
||||
T::PyClassMutability::try_borrow_mut(self.borrow_checker())
|
||||
.map(|_| PyRefMut { inner: self })
|
||||
}
|
||||
|
||||
|
@ -477,8 +678,8 @@ impl<T: PyClass> PyCell<T> {
|
|||
/// });
|
||||
/// ```
|
||||
pub unsafe fn try_borrow_unguarded(&self) -> Result<&T, PyBorrowError> {
|
||||
self.borrow_checker()
|
||||
.try_borrow_unguarded()
|
||||
self.ensure_threadsafe();
|
||||
T::PyClassMutability::try_borrow_unguarded(self.borrow_checker())
|
||||
.map(|_: ()| &*self.contents.value.get())
|
||||
}
|
||||
|
||||
|
@ -593,7 +794,13 @@ impl<T: PyClass> PyCell<T> {
|
|||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: PyClass> PyLayout<T> for PyCell<T> {}
|
||||
impl<T: PyClassImpl> PyCell<T> {
|
||||
fn borrow_checker(&self) -> &<T::PyClassMutability as PyClassMutability>::Checker {
|
||||
T::PyClassMutability::borrow_checker(self)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: PyClassImpl> PyLayout<T> for PyCell<T> {}
|
||||
impl<T: PyClass> PySizedLayout<T> for PyCell<T> {}
|
||||
|
||||
impl<T: PyClass> AsPyPointer for PyCell<T> {
|
||||
|
@ -776,7 +983,7 @@ impl<'p, T: PyClass> Deref for PyRef<'p, T> {
|
|||
|
||||
impl<'p, T: PyClass> Drop for PyRef<'p, T> {
|
||||
fn drop(&mut self) {
|
||||
self.inner.borrow_checker().release_borrow()
|
||||
T::PyClassMutability::release_borrow(self.inner.borrow_checker())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -874,7 +1081,7 @@ impl<'p, T: MutablePyClass> DerefMut for PyRefMut<'p, T> {
|
|||
|
||||
impl<'p, T: MutablePyClass> Drop for PyRefMut<'p, T> {
|
||||
fn drop(&mut self) {
|
||||
self.inner.borrow_checker().release_borrow_mut()
|
||||
T::PyClassMutability::release_borrow_mut(self.inner.borrow_checker())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -969,11 +1176,8 @@ impl From<PyBorrowMutError> for PyErr {
|
|||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait PyCellLayout<T, M>: PyLayout<T>
|
||||
where
|
||||
M: Mutability,
|
||||
{
|
||||
fn borrow_checker(&self) -> &M;
|
||||
pub trait PyCellLayout<T>: PyLayout<T> {
|
||||
fn ensure_threadsafe(&self);
|
||||
/// Implementation of tp_dealloc.
|
||||
/// # Safety
|
||||
/// - slf must be a valid pointer to an instance of a T or a subclass.
|
||||
|
@ -981,15 +1185,12 @@ where
|
|||
unsafe fn tp_dealloc(slf: *mut ffi::PyObject, py: Python<'_>);
|
||||
}
|
||||
|
||||
impl<T, U, M> PyCellLayout<T, M> for PyCellBase<U, M>
|
||||
impl<T, U> PyCellLayout<T> for PyCellBase<U>
|
||||
where
|
||||
U: PySizedLayout<T>,
|
||||
T: PyTypeInfo,
|
||||
M: Mutability,
|
||||
{
|
||||
fn borrow_checker(&self) -> &M {
|
||||
&self.borrow_impl
|
||||
}
|
||||
fn ensure_threadsafe(&self) {}
|
||||
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 {
|
||||
|
@ -1011,14 +1212,13 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: PyClass> PyCellLayout<T, T::Mutability> for PyCell<T>
|
||||
impl<T: PyClassImpl> PyCellLayout<T> for PyCell<T>
|
||||
where
|
||||
<T::BaseType as PyClassBaseType<T::Mutability>>::LayoutAsBase:
|
||||
PyCellLayout<T::BaseType, T::Mutability>,
|
||||
<T::BaseType as PyClassBaseType>::LayoutAsBase: PyCellLayout<T::BaseType>,
|
||||
{
|
||||
fn borrow_checker(&self) -> &T::Mutability {
|
||||
fn ensure_threadsafe(&self) {
|
||||
self.contents.thread_checker.ensure();
|
||||
self.ob_base.borrow_checker()
|
||||
self.ob_base.ensure_threadsafe();
|
||||
}
|
||||
unsafe fn tp_dealloc(slf: *mut ffi::PyObject, py: Python<'_>) {
|
||||
// Safety: Python only calls tp_dealloc when no references to the object remain.
|
||||
|
@ -1026,6 +1226,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<T::Mutability>>::LayoutAsBase::tp_dealloc(slf, py)
|
||||
<T::BaseType as PyClassBaseType>::LayoutAsBase::tp_dealloc(slf, py)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,11 +5,10 @@ use crate::{
|
|||
exceptions::PyTypeError,
|
||||
ffi,
|
||||
impl_::pyclass::{
|
||||
assign_sequence_item_from_mapping, get_sequence_item_from_mapping, tp_dealloc, PyClassDict,
|
||||
PyClassImpl, PyClassItems, PyClassWeakRef,
|
||||
assign_sequence_item_from_mapping, get_sequence_item_from_mapping, tp_dealloc, PyClassImpl,
|
||||
PyClassItems,
|
||||
},
|
||||
IntoPy, IntoPyPointer, PyCell, PyErr, PyMethodDefType, PyNativeType, PyObject, PyResult,
|
||||
PyTypeInfo, Python,
|
||||
IntoPy, IntoPyPointer, PyCell, PyErr, PyMethodDefType, PyObject, PyResult, PyTypeInfo, Python,
|
||||
};
|
||||
use std::{
|
||||
convert::TryInto,
|
||||
|
@ -26,13 +25,6 @@ use std::{
|
|||
pub trait PyClass:
|
||||
PyTypeInfo<AsRefTarget = PyCell<Self>> + PyClassImpl<Layout = PyCell<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;
|
||||
}
|
||||
|
||||
pub trait MutablePyClass: PyClass<Mutability = Mutable> {}
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
//! Contains initialization utilities for `#[pyclass]`.
|
||||
use crate::callback::IntoPyCallbackOutput;
|
||||
use crate::impl_::pyclass::{PyClassBaseType, PyClassDict, PyClassThreadChecker, PyClassWeakRef};
|
||||
use crate::pycell::Mutability;
|
||||
use crate::pyclass::MutablePyClass;
|
||||
use crate::{ffi, PyCell, PyClass, PyErr, PyResult, Python};
|
||||
use crate::{
|
||||
ffi::PyTypeObject,
|
||||
pycell::PyCellContents,
|
||||
pycell::{PyCellContents, PyClassMutability, PyClassMutabilityStorage},
|
||||
type_object::{get_tp_alloc, PyTypeInfo},
|
||||
};
|
||||
use std::{
|
||||
|
@ -132,17 +131,14 @@ impl<T: PyTypeInfo> PyObjectInit<T> for PyNativeTypeInitializer<T> {
|
|||
/// ```
|
||||
pub struct PyClassInitializer<T: PyClass> {
|
||||
init: T,
|
||||
super_init: <T::BaseType as PyClassBaseType<T::Mutability>>::Initializer,
|
||||
super_init: <T::BaseType as PyClassBaseType>::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<T::Mutability>>::Initializer,
|
||||
) -> Self {
|
||||
pub fn new(init: T, super_init: <T::BaseType as PyClassBaseType>::Initializer) -> Self {
|
||||
Self { init, super_init }
|
||||
}
|
||||
|
||||
|
@ -195,7 +191,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<S::Mutability, Initializer = Self>,
|
||||
S::BaseType: PyClassBaseType<Initializer = Self>,
|
||||
{
|
||||
PyClassInitializer::new(subclass_value, self)
|
||||
}
|
||||
|
@ -233,35 +229,23 @@ impl<T: PyClass> PyObjectInit<T> for PyClassInitializer<T> {
|
|||
py: Python<'_>,
|
||||
subtype: *mut PyTypeObject,
|
||||
) -> PyResult<*mut ffi::PyObject> {
|
||||
/// Layout of a PyCellBase after base new has been called, but the borrow flag has not
|
||||
/// yet been initialized.
|
||||
#[repr(C)]
|
||||
struct PartiallyInitializedPyCellBase<T, M: Mutability> {
|
||||
_ob_base: T,
|
||||
borrow_flag: MaybeUninit<M>,
|
||||
}
|
||||
|
||||
/// 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<T::Mutability>>::LayoutAsBase,
|
||||
_ob_base: <T::BaseType as PyClassBaseType>::LayoutAsBase,
|
||||
contents: MaybeUninit<PyCellContents<T>>,
|
||||
}
|
||||
|
||||
let Self { init, super_init } = self;
|
||||
let obj = super_init.into_new_object(py, subtype)?;
|
||||
|
||||
// FIXME: Only need to initialize borrow flag once per whole hierarchy
|
||||
let base: *mut PartiallyInitializedPyCellBase<T::BaseNativeType, T::Mutability> = obj as _;
|
||||
std::ptr::write((*base).borrow_flag.as_mut_ptr(), T::Mutability::new());
|
||||
|
||||
// FIXME: Initialize borrow flag if necessary??
|
||||
let cell: *mut PartiallyInitializedPyCell<T> = obj as _;
|
||||
std::ptr::write(
|
||||
(*cell).contents.as_mut_ptr(),
|
||||
PyCellContents {
|
||||
value: ManuallyDrop::new(UnsafeCell::new(init)),
|
||||
borrow_checker: <T::PyClassMutability as PyClassMutability>::Storage::new(),
|
||||
thread_checker: T::ThreadChecker::new(),
|
||||
dict: T::Dict::new(),
|
||||
weakref: T::WeakRef::new(),
|
||||
|
@ -276,7 +260,7 @@ impl<T: PyClass> PyObjectInit<T> for PyClassInitializer<T> {
|
|||
impl<T> From<T> for PyClassInitializer<T>
|
||||
where
|
||||
T: PyClass,
|
||||
T::BaseType: PyClassBaseType<T::Mutability, Initializer = PyNativeTypeInitializer<T::BaseType>>,
|
||||
T::BaseType: PyClassBaseType<Initializer = PyNativeTypeInitializer<T::BaseType>>,
|
||||
{
|
||||
#[inline]
|
||||
fn from(value: T) -> PyClassInitializer<T> {
|
||||
|
@ -288,7 +272,7 @@ impl<S, B> From<(S, B)> for PyClassInitializer<S>
|
|||
where
|
||||
S: MutablePyClass<BaseType = B>,
|
||||
B: MutablePyClass,
|
||||
B::BaseType: PyClassBaseType<S::Mutability, 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;
|
||||
|
|
|
@ -192,11 +192,12 @@ 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<M: $crate::pycell::Mutability, $($generics,)*> $crate::impl_::pyclass::PyClassBaseType<M> for $name {
|
||||
type LayoutAsBase = $crate::pycell::PyCellBase<$layout, M>;
|
||||
impl<$($generics,)*> $crate::impl_::pyclass::PyClassBaseType for $name {
|
||||
type LayoutAsBase = $crate::pycell::PyCellBase<$layout>;
|
||||
type BaseNativeType = $name;
|
||||
type ThreadChecker = $crate::impl_::pyclass::ThreadCheckerStub<$crate::PyObject>;
|
||||
type Initializer = $crate::pyclass_init::PyNativeTypeInitializer<Self>;
|
||||
type PyClassMutability = $crate::pycell::ImmutableClass;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,6 @@ fn test_compile_errors() {
|
|||
#[cfg(not(feature = "nightly"))]
|
||||
fn _test_compile_errors() {
|
||||
let t = trybuild::TestCases::new();
|
||||
t.compile_fail("tests/ui/invalid_immutable_pyclass_borrow.rs");
|
||||
|
||||
t.compile_fail("tests/ui/invalid_macro_args.rs");
|
||||
t.compile_fail("tests/ui/invalid_need_module_arg_position.rs");
|
||||
|
@ -41,8 +40,6 @@ fn _test_compile_errors() {
|
|||
t.compile_fail("tests/ui/invalid_pymethod_names.rs");
|
||||
t.compile_fail("tests/ui/invalid_pymodule_args.rs");
|
||||
t.compile_fail("tests/ui/reject_generics.rs");
|
||||
t.compile_fail("tests/ui/invalid_immutable_pyclass_borrow.rs");
|
||||
t.compile_fail("tests/ui/wrong_aspyref_lifetimes.rs");
|
||||
t.compile_fail("tests/ui/invalid_pymethod_proto_args.rs");
|
||||
t.compile_fail("tests/ui/invalid_pymethod_proto_args_py.rs");
|
||||
|
||||
|
@ -97,6 +94,7 @@ fn _test_compile_errors() {
|
|||
|
||||
#[rustversion::since(1.60)]
|
||||
fn tests_rust_1_60(t: &trybuild::TestCases) {
|
||||
t.compile_fail("tests/ui/invalid_immutable_pyclass_borrow.rs");
|
||||
t.compile_fail("tests/ui/invalid_pymethod_receiver.rs");
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,201 @@
|
|||
#![cfg(feature = "macros")]
|
||||
|
||||
use pyo3::impl_::pyclass::{PyClassBaseType, PyClassImpl};
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::pycell::{
|
||||
BorrowChecker, ExtendsMutableAncestor, ImmutableClass, MutableClass, PyClassMutability,
|
||||
};
|
||||
use pyo3::PyClass;
|
||||
|
||||
#[pyclass(subclass)]
|
||||
struct MutableBase;
|
||||
|
||||
#[pyclass(extends = MutableBase, subclass)]
|
||||
struct MutableChildOfMutableBase;
|
||||
|
||||
#[pyclass(extends = MutableBase, immutable, subclass)]
|
||||
struct ImmutableChildOfMutableBase;
|
||||
|
||||
#[pyclass(extends = MutableChildOfMutableBase)]
|
||||
struct MutableChildOfMutableChildOfMutableBase;
|
||||
|
||||
#[pyclass(extends = ImmutableChildOfMutableBase)]
|
||||
struct MutableChildOfImmutableChildOfMutableBase;
|
||||
|
||||
#[pyclass(extends = MutableChildOfMutableBase, immutable)]
|
||||
struct ImmutableChildOfMutableChildOfMutableBase;
|
||||
|
||||
#[pyclass(extends = ImmutableChildOfMutableBase, immutable)]
|
||||
struct ImmutableChildOfImmutableChildOfMutableBase;
|
||||
|
||||
#[pyclass(immutable, subclass)]
|
||||
struct ImmutableBase;
|
||||
|
||||
#[pyclass(extends = ImmutableBase, subclass)]
|
||||
struct MutableChildOfImmutableBase;
|
||||
|
||||
#[pyclass(extends = ImmutableBase, immutable, subclass)]
|
||||
struct ImmutableChildOfImmutableBase;
|
||||
|
||||
#[pyclass(extends = MutableChildOfImmutableBase)]
|
||||
struct MutableChildOfMutableChildOfImmutableBase;
|
||||
|
||||
#[pyclass(extends = ImmutableChildOfImmutableBase)]
|
||||
struct MutableChildOfImmutableChildOfImmutableBase;
|
||||
|
||||
#[pyclass(extends = MutableChildOfImmutableBase, immutable)]
|
||||
struct ImmutableChildOfMutableChildOfImmutableBase;
|
||||
|
||||
#[pyclass(extends = ImmutableChildOfImmutableBase, immutable)]
|
||||
struct ImmutableChildOfImmutableChildOfImmutableBase;
|
||||
|
||||
fn assert_mutable<T: PyClass<PyClassMutability = MutableClass>>() {}
|
||||
fn assert_immutable<T: PyClass<PyClassMutability = ImmutableClass>>() {}
|
||||
fn assert_mutable_with_mutable_ancestor<
|
||||
T: PyClass<PyClassMutability = ExtendsMutableAncestor<MutableClass>>,
|
||||
>()
|
||||
// These horrible bounds are necessary for Rust 1.48 but not newer versions
|
||||
where
|
||||
<T as PyClassImpl>::BaseType: PyClassImpl<Layout = PyCell<T::BaseType>>,
|
||||
<<T as PyClassImpl>::BaseType as PyClassImpl>::PyClassMutability:
|
||||
PyClassMutability<Checker = BorrowChecker>,
|
||||
<T as PyClassImpl>::BaseType: PyClassBaseType<LayoutAsBase = PyCell<T::BaseType>>,
|
||||
{
|
||||
}
|
||||
fn assert_immutable_with_mutable_ancestor<
|
||||
T: PyClass<PyClassMutability = ExtendsMutableAncestor<ImmutableClass>>,
|
||||
>()
|
||||
// These horrible bounds are necessary for Rust 1.48 but not newer versions
|
||||
where
|
||||
<T as PyClassImpl>::BaseType: PyClassImpl<Layout = PyCell<T::BaseType>>,
|
||||
<<T as PyClassImpl>::BaseType as PyClassImpl>::PyClassMutability:
|
||||
PyClassMutability<Checker = BorrowChecker>,
|
||||
<T as PyClassImpl>::BaseType: PyClassBaseType<LayoutAsBase = PyCell<T::BaseType>>,
|
||||
{
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inherited_mutability() {
|
||||
// mutable base
|
||||
assert_mutable::<MutableBase>();
|
||||
|
||||
// children of mutable base have a mutable ancestor
|
||||
assert_mutable_with_mutable_ancestor::<MutableChildOfMutableBase>();
|
||||
assert_immutable_with_mutable_ancestor::<ImmutableChildOfMutableBase>();
|
||||
|
||||
// grandchildren of mutable base have a mutable ancestor
|
||||
assert_mutable_with_mutable_ancestor::<MutableChildOfMutableChildOfMutableBase>();
|
||||
assert_mutable_with_mutable_ancestor::<MutableChildOfImmutableChildOfMutableBase>();
|
||||
assert_immutable_with_mutable_ancestor::<ImmutableChildOfMutableChildOfMutableBase>();
|
||||
assert_immutable_with_mutable_ancestor::<ImmutableChildOfImmutableChildOfMutableBase>();
|
||||
|
||||
// immutable base and children
|
||||
assert_immutable::<ImmutableBase>();
|
||||
assert_immutable::<ImmutableChildOfImmutableBase>();
|
||||
assert_immutable::<ImmutableChildOfImmutableChildOfImmutableBase>();
|
||||
|
||||
// mutable children of immutable at any level are simply mutable
|
||||
assert_mutable::<MutableChildOfImmutableBase>();
|
||||
assert_mutable::<MutableChildOfImmutableChildOfImmutableBase>();
|
||||
|
||||
// children of the mutable child display this property
|
||||
assert_mutable_with_mutable_ancestor::<MutableChildOfMutableChildOfImmutableBase>();
|
||||
assert_immutable_with_mutable_ancestor::<ImmutableChildOfMutableChildOfImmutableBase>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mutable_borrow_prevents_further_borrows() {
|
||||
Python::with_gil(|py| {
|
||||
let mmm = Py::new(
|
||||
py,
|
||||
PyClassInitializer::from(MutableBase)
|
||||
.add_subclass(MutableChildOfMutableBase)
|
||||
.add_subclass(MutableChildOfMutableChildOfMutableBase),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mmm_cell: &PyCell<MutableChildOfMutableChildOfMutableBase> = mmm.as_ref(py);
|
||||
|
||||
let mmm_refmut = mmm_cell.borrow_mut();
|
||||
|
||||
// Cannot take any other mutable or immutable borrows whilst the object is borrowed mutably
|
||||
assert!(mmm_cell
|
||||
.extract::<PyRef<'_, MutableChildOfMutableChildOfMutableBase>>()
|
||||
.is_err());
|
||||
assert!(mmm_cell
|
||||
.extract::<PyRef<'_, MutableChildOfMutableBase>>()
|
||||
.is_err());
|
||||
assert!(mmm_cell.extract::<PyRef<'_, MutableBase>>().is_err());
|
||||
assert!(mmm_cell
|
||||
.extract::<PyRefMut<'_, MutableChildOfMutableChildOfMutableBase>>()
|
||||
.is_err());
|
||||
assert!(mmm_cell
|
||||
.extract::<PyRefMut<'_, MutableChildOfMutableBase>>()
|
||||
.is_err());
|
||||
assert!(mmm_cell.extract::<PyRefMut<'_, MutableBase>>().is_err());
|
||||
|
||||
// With the borrow dropped, all other borrow attempts will succeed
|
||||
drop(mmm_refmut);
|
||||
|
||||
assert!(mmm_cell
|
||||
.extract::<PyRef<'_, MutableChildOfMutableChildOfMutableBase>>()
|
||||
.is_ok());
|
||||
assert!(mmm_cell
|
||||
.extract::<PyRef<'_, MutableChildOfMutableBase>>()
|
||||
.is_ok());
|
||||
assert!(mmm_cell.extract::<PyRef<'_, MutableBase>>().is_ok());
|
||||
assert!(mmm_cell
|
||||
.extract::<PyRefMut<'_, MutableChildOfMutableChildOfMutableBase>>()
|
||||
.is_ok());
|
||||
assert!(mmm_cell
|
||||
.extract::<PyRefMut<'_, MutableChildOfMutableBase>>()
|
||||
.is_ok());
|
||||
assert!(mmm_cell.extract::<PyRefMut<'_, MutableBase>>().is_ok());
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_immutable_borrows_prevent_mutable_borrows() {
|
||||
Python::with_gil(|py| {
|
||||
let mmm = Py::new(
|
||||
py,
|
||||
PyClassInitializer::from(MutableBase)
|
||||
.add_subclass(MutableChildOfMutableBase)
|
||||
.add_subclass(MutableChildOfMutableChildOfMutableBase),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mmm_cell: &PyCell<MutableChildOfMutableChildOfMutableBase> = mmm.as_ref(py);
|
||||
|
||||
let mmm_refmut = mmm_cell.borrow();
|
||||
|
||||
// Further immutable borrows are ok
|
||||
assert!(mmm_cell
|
||||
.extract::<PyRef<'_, MutableChildOfMutableChildOfMutableBase>>()
|
||||
.is_ok());
|
||||
assert!(mmm_cell
|
||||
.extract::<PyRef<'_, MutableChildOfMutableBase>>()
|
||||
.is_ok());
|
||||
assert!(mmm_cell.extract::<PyRef<'_, MutableBase>>().is_ok());
|
||||
|
||||
// Further mutable borrows are not ok
|
||||
assert!(mmm_cell
|
||||
.extract::<PyRefMut<'_, MutableChildOfMutableChildOfMutableBase>>()
|
||||
.is_err());
|
||||
assert!(mmm_cell
|
||||
.extract::<PyRefMut<'_, MutableChildOfMutableBase>>()
|
||||
.is_err());
|
||||
assert!(mmm_cell.extract::<PyRefMut<'_, MutableBase>>().is_err());
|
||||
|
||||
// With the borrow dropped, all mutable borrow attempts will succeed
|
||||
drop(mmm_refmut);
|
||||
|
||||
assert!(mmm_cell
|
||||
.extract::<PyRefMut<'_, MutableChildOfMutableChildOfMutableBase>>()
|
||||
.is_ok());
|
||||
assert!(mmm_cell
|
||||
.extract::<PyRefMut<'_, MutableChildOfMutableBase>>()
|
||||
.is_ok());
|
||||
assert!(mmm_cell.extract::<PyRefMut<'_, MutableBase>>().is_ok());
|
||||
})
|
||||
}
|
|
@ -1,24 +1,22 @@
|
|||
error[E0277]: the trait bound `PyDict: PyClass` is not satisfied
|
||||
--> tests/ui/abi3_nativetype_inheritance.rs:5:1
|
||||
|
|
||||
5 | #[pyclass(extends=PyDict)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `PyClass` is not implemented for `PyDict`
|
||||
|
|
||||
= note: required because of the requirements on the impl of `MutablePyClass` for `PyDict`
|
||||
= note: required because of the requirements on the impl of `PyClassBaseType<Mutable>` for `PyDict`
|
||||
= note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error[E0277]: the trait bound `PyDict: PyClass` is not satisfied
|
||||
--> tests/ui/abi3_nativetype_inheritance.rs:5:1
|
||||
|
|
||||
5 | #[pyclass(extends=PyDict)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `PyClass` is not implemented for `PyDict`
|
||||
|
|
||||
= note: required because of the requirements on the impl of `MutablePyClass` for `PyDict`
|
||||
= note: required because of the requirements on the impl of `PyClassBaseType<Mutable>` for `PyDict`
|
||||
= note: required because of the requirements on the impl of `PyClassBaseType` for `PyDict`
|
||||
note: required by a bound in `ThreadCheckerInherited`
|
||||
--> src/impl_/pyclass.rs
|
||||
|
|
||||
| pub struct ThreadCheckerInherited<T: PyClass + Send, U: PyClassBaseType<T::Mutability>>(
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ThreadCheckerInherited`
|
||||
| pub struct ThreadCheckerInherited<T: PyClass + Send, U: PyClassBaseType>(
|
||||
| ^^^^^^^^^^^^^^^ required by this bound in `ThreadCheckerInherited`
|
||||
= note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error[E0277]: the trait bound `PyDict: PyClass` is not satisfied
|
||||
--> tests/ui/abi3_nativetype_inheritance.rs:5:1
|
||||
|
|
||||
5 | #[pyclass(extends=PyDict)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `PyClass` is not implemented for `PyDict`
|
||||
|
|
||||
= note: required because of the requirements on the impl of `PyClassBaseType` for `PyDict`
|
||||
= note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
|
|
@ -10,4 +10,14 @@ fn borrow_mut_fails(foo: Py<Foo>, py: Python){
|
|||
let borrow = foo.as_ref(py).borrow_mut();
|
||||
}
|
||||
|
||||
fn main(){}
|
||||
#[pyclass(subclass)]
|
||||
struct MutableBase;
|
||||
|
||||
#[pyclass(immutable, extends = MutableBase)]
|
||||
struct ImmutableChild;
|
||||
|
||||
fn borrow_mut_of_child_fails(child: Py<ImmutableChild>, py: Python){
|
||||
let borrow = child.as_ref(py).borrow_mut();
|
||||
}
|
||||
|
||||
fn main(){}
|
||||
|
|
|
@ -10,3 +10,16 @@ note: required by a bound in `PyCell::<T>::borrow_mut`
|
|||
|
|
||||
| T: MutablePyClass,
|
||||
| ^^^^^^^^^^^^^^ required by this bound in `PyCell::<T>::borrow_mut`
|
||||
|
||||
error[E0271]: type mismatch resolving `<ImmutableChild as PyClassImpl>::Mutability == Mutable`
|
||||
--> tests/ui/invalid_immutable_pyclass_borrow.rs:20:35
|
||||
|
|
||||
20 | let borrow = child.as_ref(py).borrow_mut();
|
||||
| ^^^^^^^^^^ expected struct `Mutable`, found struct `Immutable`
|
||||
|
|
||||
= note: required because of the requirements on the impl of `MutablePyClass` for `ImmutableChild`
|
||||
note: required by a bound in `PyCell::<T>::borrow_mut`
|
||||
--> src/pycell.rs
|
||||
|
|
||||
| T: MutablePyClass,
|
||||
| ^^^^^^^^^^^^^^ required by this bound in `PyCell::<T>::borrow_mut`
|
||||
|
|
Loading…
Reference in New Issue