finish off implementation

This commit is contained in:
David Hewitt 2022-02-08 08:01:30 +00:00
parent 7520b49ac1
commit 6f39deaa37
10 changed files with 63 additions and 80 deletions

View File

@ -592,29 +592,12 @@ fn impl_enum_class(
let default_items =
gen_default_items(cls, vec![default_repr_impl, default_richcmp, default_int]);
let mutability = if args.is_immutable {
quote! {
unsafe impl _pyo3::pyclass::ImmutablePyClass for #cls {}
}
} else {
quote! {
unsafe impl _pyo3::pyclass::MutablePyClass for #cls {}
impl<'a> _pyo3::derive_utils::ExtractExt<'a> for &'a mut #cls
{
type Target = _pyo3::PyRefMut<'a, #cls>;
}
}
};
Ok(quote! {
const _: () = {
use #krate as _pyo3;
#pytypeinfo
#mutability
#pyclass_impls
#default_items
@ -801,7 +784,7 @@ impl<'a> PyClassImplsBuilder<'a> {
};
let base_nativetype = if attr.has_extends {
quote! { <Self::BaseType as _pyo3::impl_::pyclass::PyClassBaseType>::BaseNativeType }
quote! { <Self::BaseType as _pyo3::impl_::pyclass::PyClassBaseType<Self::Mutability>>::BaseNativeType }
} else {
quote! { _pyo3::PyAny }
};
@ -810,7 +793,6 @@ impl<'a> PyClassImplsBuilder<'a> {
type Dict = #dict;
type WeakRef = #weakref;
type BaseNativeType = #base_nativetype;
type Mutability = _pyo3::pycell::Immutable;
}
}
}
@ -944,7 +926,7 @@ impl<'a> PyClassImplsBuilder<'a> {
};
quote! {
impl _pyo3::impl_::pyclass::PyClassImpl<#mutability> for #cls {
impl _pyo3::impl_::pyclass::PyClassImpl for #cls {
const DOC: &'static str = #doc;
const IS_GC: bool = #is_gc;
const IS_BASETYPE: bool = #is_basetype;
@ -954,6 +936,7 @@ impl<'a> PyClassImplsBuilder<'a> {
type BaseType = #base;
type ThreadChecker = #thread_checker;
#inventory
type Mutability = #mutability;
fn for_all_items(visitor: &mut dyn ::std::ops::FnMut(& _pyo3::impl_::pyclass::PyClassItems)) {
use _pyo3::impl_::pyclass::*;
@ -985,9 +968,9 @@ impl<'a> PyClassImplsBuilder<'a> {
#dict_offset
#weaklist_offset
#inventory_class
}
#inventory_class
}
}

View File

@ -2,7 +2,8 @@ use crate::{
exceptions::{PyAttributeError, PyNotImplementedError},
ffi,
impl_::freelist::FreeList,
pycell::{Mutability, PyCellLayout},
pycell::{Mutability, Mutable, PyCellLayout},
pyclass::MutablePyClass,
pyclass_init::PyObjectInit,
type_object::{PyLayout, PyTypeObject},
PyCell, PyClass, PyMethodDefType, PyNativeType, PyResult, PyTypeInfo, Python,
@ -145,7 +146,7 @@ unsafe impl Sync for PyClassItems {}
///
/// Users are discouraged from implementing this trait manually; it is a PyO3 implementation detail
/// and may be changed at any time.
pub trait PyClassImpl<M: Mutability>: Sized {
pub trait PyClassImpl: Sized {
/// Class doc string
const DOC: &'static str = "\0";
@ -162,7 +163,10 @@ pub trait PyClassImpl<M: Mutability>: Sized {
type Layout: PyLayout<Self>;
/// Base class
type BaseType: PyTypeInfo + PyTypeObject + PyClassBaseType<M>;
type BaseType: PyTypeInfo + PyTypeObject + PyClassBaseType<Self::Mutability>;
/// Immutable or mutable
type Mutability: Mutability;
/// This handles following two situations:
/// 1. In case `T` is `Send`, stub `ThreadChecker` is used and does nothing.
@ -818,13 +822,13 @@ 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<M>, M: Mutability>(
pub struct ThreadCheckerInherited<T: PyClass + Send, U: PyClassBaseType<T::Mutability>>(
PhantomData<T>,
U::ThreadChecker,
);
impl<T: Send, U: PyClassBaseType<M>, M: Mutability> PyClassThreadChecker<T>
for ThreadCheckerInherited<T, U, M>
impl<T: PyClass + Send, U: PyClassBaseType<T::Mutability>> PyClassThreadChecker<T>
for ThreadCheckerInherited<T, U>
{
fn ensure(&self) {
self.1.ensure();
@ -843,8 +847,10 @@ pub trait PyClassBaseType<M: Mutability>: Sized {
type Initializer: PyObjectInit<Self>;
}
/// All PyClasses can be used as a base type.
impl<T: PyClass> PyClassBaseType<T::Mutability> for T {
/// 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 {
type LayoutAsBase = crate::pycell::PyCell<T>;
type BaseNativeType = T::BaseNativeType;
type ThreadChecker = T::ThreadChecker;

View File

@ -700,8 +700,8 @@ impl<'p, T: PyClass> PyRef<'p, T> {
impl<'p, T, U> AsRef<U> for PyRef<'p, T>
where
T: PyClass<BaseType = U>,
U: PyClass,
T: MutablePyClass<BaseType = U>, // For now, only mutable classes can be extended
U: MutablePyClass,
{
fn as_ref(&self) -> &T::BaseType {
unsafe { &*self.inner.ob_base.get_ptr() }
@ -710,8 +710,8 @@ where
impl<'p, T, U> PyRef<'p, T>
where
T: PyClass<BaseType = U>,
U: PyClass,
T: MutablePyClass<BaseType = U>, // For now, only mutable classes can be extended
U: MutablePyClass,
{
/// Gets a `PyRef<T::BaseType>`.
///

View File

@ -1,5 +1,5 @@
//! `PyClass` and related traits.
use crate::pycell::Mutability;
use crate::pycell::{Immutable, Mutable};
use crate::{
callback::IntoPyCallbackOutput,
ffi,
@ -23,7 +23,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<Self::Mutability, Layout = PyCell<Self>>
PyTypeInfo<AsRefTarget = PyCell<Self>> + PyClassImpl<Layout = PyCell<Self>>
{
/// Specify this class has `#[pyclass(dict)]` or not.
type Dict: PyClassDict;
@ -32,12 +32,13 @@ pub trait PyClass:
/// The closest native ancestor. This is `PyAny` by default, and when you declare
/// `#[pyclass(extends=PyDict)]`, it's `PyDict`.
type BaseNativeType: PyTypeInfo + PyNativeType;
type Mutability: Mutability;
}
pub unsafe trait MutablePyClass: PyClass {}
pub unsafe trait ImmutablePyClass: PyClass {}
pub trait MutablePyClass: PyClass<Mutability = Mutable> {}
pub trait ImmutablePyClass: PyClass<Mutability = Immutable> {}
impl<T> MutablePyClass for T where T: PyClass<Mutability = Mutable> {}
impl<T> ImmutablePyClass for T where T: PyClass<Mutability = Immutable> {}
fn into_raw<T>(vec: Vec<T>) -> *mut c_void {
Box::into_raw(vec.into_boxed_slice()) as _

View File

@ -1,14 +1,16 @@
//! 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::{BorrowFlag, PyCellContents},
pycell::PyCellContents,
type_object::{get_tp_alloc, PyTypeInfo},
};
use std::{
cell::{Cell, UnsafeCell},
cell::UnsafeCell,
marker::PhantomData,
mem::{ManuallyDrop, MaybeUninit},
};
@ -193,7 +195,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<T::Mutability, Initializer = Self>,
S::BaseType: PyClassBaseType<S::Mutability, Initializer = Self>,
{
PyClassInitializer::new(subclass_value, self)
}
@ -234,9 +236,9 @@ 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: PyClass> {
struct PartiallyInitializedPyCellBase<T, M: Mutability> {
_ob_base: T,
borrow_flag: MaybeUninit<T::Mutability>,
borrow_flag: MaybeUninit<M>,
}
/// Layout of a PyCell after base new has been called, but the contents have not yet been
@ -251,11 +253,8 @@ impl<T: PyClass> PyObjectInit<T> for PyClassInitializer<T> {
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> = obj as _;
std::ptr::write(
(*base).borrow_flag.as_mut_ptr(),
Cell::new(BorrowFlag::UNUSED),
);
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 _;
@ -287,9 +286,9 @@ where
impl<S, B> From<(S, B)> for PyClassInitializer<S>
where
S: PyClass<BaseType = B>,
B: PyClass,
B::BaseType: PyClassBaseType<Initializer = PyNativeTypeInitializer<B::BaseType>>,
S: MutablePyClass<BaseType = B>,
B: MutablePyClass,
B::BaseType: PyClassBaseType<S::Mutability, Initializer = PyNativeTypeInitializer<B::BaseType>>,
{
fn from(sub_and_base: (S, B)) -> PyClassInitializer<S> {
let (sub, base) = sub_and_base;

View File

@ -196,8 +196,8 @@ 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::impl_::pyclass::PyClassBaseType<$crate::pycell::Mutable> for $name {
type LayoutAsBase = $crate::pycell::PyCellBase<$layout, $crate::pycell::Mutable>;
impl<'a, M: $crate::pycell::Mutability, $($generics,)*> $crate::impl_::pyclass::PyClassBaseType<M> for $name {
type LayoutAsBase = $crate::pycell::PyCellBase<$layout, M>;
type BaseNativeType = $name;
type ThreadChecker = $crate::impl_::pyclass::ThreadCheckerStub<$crate::PyObject>;
type Initializer = $crate::pyclass_init::PyNativeTypeInitializer<Self>;

View File

@ -17,6 +17,7 @@ fn test_compile_errors() {
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");
t.compile_fail("tests/ui/invalid_property_args.rs");
@ -28,12 +29,8 @@ 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");
<<<<<<< HEAD
t.compile_fail("tests/ui/invalid_pymethod_proto_args.rs");
t.compile_fail("tests/ui/invalid_pymethod_proto_args_py.rs");
=======
t.compile_fail("tests/ui/invalid_immutable_pyclass_borrow.rs");
>>>>>>> 0fa03a67cd (Implement opt-in immutable pyclasses)
tests_rust_1_49(&t);
tests_rust_1_56(&t);

View File

@ -4,7 +4,8 @@ error[E0277]: the trait bound `PyDict: PyClass` is not satisfied
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: 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
@ -13,10 +14,11 @@ error[E0277]: the trait bound `PyDict: PyClass` is not satisfied
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: 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 by a bound in `ThreadCheckerInherited`
--> src/impl_/pyclass.rs
|
| pub struct ThreadCheckerInherited<T: Send, U: PyClassBaseType>(PhantomData<T>, U::ThreadChecker);
| ^^^^^^^^^^^^^^^ required by this bound in `ThreadCheckerInherited`
| pub struct ThreadCheckerInherited<T: PyClass + Send, U: PyClassBaseType<T::Mutability>>(
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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)

View File

@ -1,5 +1,12 @@
error[E0277]: the trait bound `Foo: MutablePyClass` is not satisfied
--> tests/ui/invalid_immutable_pyclass_borrow.rs:10:33
|
10 | let borrow = foo.as_ref(py).borrow_mut();
| ^^^^^^^^^^ the trait `MutablePyClass` is not implemented for `Foo`
error[E0271]: type mismatch resolving `<Foo as PyClassImpl>::Mutability == Mutable`
--> tests/ui/invalid_immutable_pyclass_borrow.rs:10:33
|
10 | let borrow = foo.as_ref(py).borrow_mut();
| ^^^^^^^^^^ expected struct `Mutable`, found struct `Immutable`
|
= note: required because of the requirements on the impl of `MutablePyClass` for `Foo`
note: required by a bound in `PyCell::<T>::borrow_mut`
--> src/pycell.rs
|
| T: MutablePyClass,
| ^^^^^^^^^^^^^^ required by this bound in `PyCell::<T>::borrow_mut`

View File

@ -11,20 +11,8 @@ note: required because it appears within the type `NotThreadSafe`
5 | struct NotThreadSafe {
| ^^^^^^^^^^^^^
note: required by a bound in `ThreadCheckerStub`
<<<<<<< HEAD
<<<<<<< HEAD
--> src/impl_/pyclass.rs
|
| pub struct ThreadCheckerStub<T: Send>(PhantomData<T>);
=======
--> src/class/impl_.rs:731:33
|
731 | pub struct ThreadCheckerStub<T: Send>(PhantomData<T>);
>>>>>>> 0fa03a67cd (Implement opt-in immutable pyclasses)
=======
--> src/class/impl_.rs:728:33
|
728 | pub struct ThreadCheckerStub<T: Send>(PhantomData<T>);
>>>>>>> 7cded1178d (Fix formatting.)
| ^^^^ required by this bound in `ThreadCheckerStub`
= note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info)