From a758495b62b810ad5a71e33fb1a65ff63d9f8f2c Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Sat, 11 Jun 2022 12:20:43 +0100 Subject: [PATCH] pyclass: switch from immutable to frozen --- guide/src/class.md | 2 +- pyo3-macros-backend/src/attributes.rs | 2 +- pyo3-macros-backend/src/pyclass.rs | 26 +++----- src/boolean_traits.rs | 23 +++++++ src/class/basic.rs | 13 ++-- src/class/buffer.rs | 5 +- src/class/gc.rs | 5 +- src/class/mapping.rs | 9 +-- src/class/number.rs | 31 ++++----- src/class/sequence.rs | 11 ++-- src/conversion.rs | 6 +- src/impl_/pyclass.rs | 7 +- src/instance.rs | 11 ++-- src/lib.rs | 1 + src/pycell.rs | 64 +++++++++---------- src/pyclass.rs | 7 -- src/pyclass_init.rs | 5 +- tests/test_compile_error.rs | 2 +- tests/test_mutable_pyclass.rs | 23 +++---- tests/ui/abi3_nativetype_inheritance.stderr | 14 ++++ ...ow.rs => invalid_frozen_pyclass_borrow.rs} | 4 +- tests/ui/invalid_frozen_pyclass_borrow.stderr | 23 +++++++ .../invalid_immutable_pyclass_borrow.stderr | 25 -------- tests/ui/invalid_pyclass_args.stderr | 4 +- 24 files changed, 174 insertions(+), 149 deletions(-) create mode 100644 src/boolean_traits.rs rename tests/ui/{invalid_immutable_pyclass_borrow.rs => invalid_frozen_pyclass_borrow.rs} (84%) create mode 100644 tests/ui/invalid_frozen_pyclass_borrow.stderr delete mode 100644 tests/ui/invalid_immutable_pyclass_borrow.stderr diff --git a/guide/src/class.md b/guide/src/class.md index fc284030f93..2af0d2eadf1 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -974,7 +974,7 @@ impl pyo3::impl_::pyclass::PyClassImpl for MyClass { type Layout = PyCell; type BaseType = PyAny; type ThreadChecker = pyo3::impl_::pyclass::ThreadCheckerStub; - type Mutability = pyo3::pycell::Mutable; + type Frozen = pyo3::boolean_traits::False; type PyClassMutability = pyo3::pycell::MutableClass; type Dict = ::pyo3::impl_::pyclass::PyClassDummySlot; type WeakRef = ::pyo3::impl_::pyclass::PyClassDummySlot; diff --git a/pyo3-macros-backend/src/attributes.rs b/pyo3-macros-backend/src/attributes.rs index 66e1a4e1891..42efade0f34 100644 --- a/pyo3-macros-backend/src/attributes.rs +++ b/pyo3-macros-backend/src/attributes.rs @@ -15,6 +15,7 @@ pub mod kw { syn::custom_keyword!(extends); syn::custom_keyword!(freelist); syn::custom_keyword!(from_py_with); + syn::custom_keyword!(frozen); syn::custom_keyword!(gc); syn::custom_keyword!(get); syn::custom_keyword!(item); @@ -29,7 +30,6 @@ pub mod kw { syn::custom_keyword!(transparent); syn::custom_keyword!(unsendable); syn::custom_keyword!(weakref); - syn::custom_keyword!(immutable); } #[derive(Clone, Debug)] diff --git a/pyo3-macros-backend/src/pyclass.rs b/pyo3-macros-backend/src/pyclass.rs index 19ff3d84921..7bc14747d59 100644 --- a/pyo3-macros-backend/src/pyclass.rs +++ b/pyo3-macros-backend/src/pyclass.rs @@ -60,7 +60,7 @@ pub struct PyClassPyO3Options { pub dict: Option, pub extends: Option, pub freelist: Option, - pub immutable: Option, + pub frozen: Option, pub mapping: Option, pub module: Option, pub name: Option, @@ -77,7 +77,7 @@ enum PyClassPyO3Option { Dict(kw::dict), Extends(ExtendsAttribute), Freelist(FreelistAttribute), - Immutable(kw::immutable), + Frozen(kw::frozen), Mapping(kw::mapping), Module(ModuleAttribute), Name(NameAttribute), @@ -100,8 +100,8 @@ impl Parse for PyClassPyO3Option { input.parse().map(PyClassPyO3Option::Extends) } else if lookahead.peek(attributes::kw::freelist) { input.parse().map(PyClassPyO3Option::Freelist) - } else if lookahead.peek(attributes::kw::immutable) { - input.parse().map(PyClassPyO3Option::Immutable) + } else if lookahead.peek(attributes::kw::frozen) { + input.parse().map(PyClassPyO3Option::Frozen) } else if lookahead.peek(attributes::kw::mapping) { input.parse().map(PyClassPyO3Option::Mapping) } else if lookahead.peek(attributes::kw::module) { @@ -159,7 +159,7 @@ impl PyClassPyO3Options { PyClassPyO3Option::Dict(dict) => set_option!(dict), PyClassPyO3Option::Extends(extends) => set_option!(extends), PyClassPyO3Option::Freelist(freelist) => set_option!(freelist), - PyClassPyO3Option::Immutable(immutable) => set_option!(immutable), + PyClassPyO3Option::Frozen(frozen) => set_option!(frozen), PyClassPyO3Option::Mapping(mapping) => set_option!(mapping), PyClassPyO3Option::Module(module) => set_option!(module), PyClassPyO3Option::Name(name) => set_option!(name), @@ -717,7 +717,7 @@ impl<'a> PyClassImplsBuilder<'a> { } fn impl_extractext(&self) -> TokenStream { let cls = self.cls; - if self.attr.options.immutable.is_some() { + if self.attr.options.frozen.is_some() { quote! { impl<'a> _pyo3::derive_utils::ExtractExt<'a> for &'a #cls { @@ -844,17 +844,7 @@ impl<'a> PyClassImplsBuilder<'a> { let deprecations = &self.attr.deprecations; - let mutability = if self.attr.options.immutable.is_some() { - quote! { - _pyo3::pycell::Immutable - } - } else { - quote! { - _pyo3::pycell::Mutable - } - }; - - let class_mutability = if self.attr.options.immutable.is_some() { + let class_mutability = if self.attr.options.frozen.is_some() { quote! { ImmutableChild } @@ -896,7 +886,7 @@ impl<'a> PyClassImplsBuilder<'a> { type BaseType = #base; type ThreadChecker = #thread_checker; #inventory - type Mutability = #mutability; + type Frozen = ::Frozen; type PyClassMutability = <<#base as _pyo3::impl_::pyclass::PyClassBaseType>::PyClassMutability as _pyo3::pycell::PyClassMutability>::#class_mutability; type Dict = #dict; type WeakRef = #weakref; diff --git a/src/boolean_traits.rs b/src/boolean_traits.rs new file mode 100644 index 00000000000..4bc8e2fda74 --- /dev/null +++ b/src/boolean_traits.rs @@ -0,0 +1,23 @@ +// Copyright (c) 2022-present PyO3 Project and Contributors + +//! A mechanism to have associated True / False values in the absence of +//! associated const equality. + +pub(crate) mod private { + use super::*; + + /// A way to "seal" the boolean traits. + pub trait Boolean {} + + impl Boolean for True {} + impl Boolean for False {} +} + +pub struct True(()); +pub struct False(()); + +/// A trait which is used to describe whether a `#[pyclass]` is frozen. +pub trait Frozen: private::Boolean {} + +impl Frozen for True {} +impl Frozen for False {} diff --git a/src/class/basic.rs b/src/class/basic.rs index be70f8eecc0..7fcfa540d84 100644 --- a/src/class/basic.rs +++ b/src/class/basic.rs @@ -9,10 +9,9 @@ //! Parts of the documentation are copied from the respective methods from the //! [typeobj docs](https://docs.python.org/3/c-api/typeobj.html) +use crate::boolean_traits::False; use crate::callback::{HashCallbackOutput, IntoPyCallbackOutput}; -use crate::{ - exceptions, ffi, pyclass::MutablePyClass, FromPyObject, PyAny, PyCell, PyClass, PyObject, -}; +use crate::{exceptions, ffi, FromPyObject, PyAny, PyCell, PyClass, PyObject}; use std::os::raw::c_int; /// Basic Python class customization @@ -28,14 +27,14 @@ pub trait PyObjectProtocol<'p>: PyClass { fn __setattr__(&'p mut self, name: Self::Name, value: Self::Value) -> Self::Result where - Self: PyObjectSetAttrProtocol<'p> + MutablePyClass, + Self: PyObjectSetAttrProtocol<'p> + PyClass, { unimplemented!() } fn __delattr__(&'p mut self, name: Self::Name) -> Self::Result where - Self: PyObjectDelAttrProtocol<'p> + MutablePyClass, + Self: PyObjectDelAttrProtocol<'p> + PyClass, { unimplemented!() } @@ -79,12 +78,12 @@ pub trait PyObjectGetAttrProtocol<'p>: PyObjectProtocol<'p> { type Name: FromPyObject<'p>; type Result: IntoPyCallbackOutput; } -pub trait PyObjectSetAttrProtocol<'p>: PyObjectProtocol<'p> + MutablePyClass { +pub trait PyObjectSetAttrProtocol<'p>: PyObjectProtocol<'p> + PyClass { type Name: FromPyObject<'p>; type Value: FromPyObject<'p>; type Result: IntoPyCallbackOutput<()>; } -pub trait PyObjectDelAttrProtocol<'p>: PyObjectProtocol<'p> + MutablePyClass { +pub trait PyObjectDelAttrProtocol<'p>: PyObjectProtocol<'p> + PyClass { type Name: FromPyObject<'p>; type Result: IntoPyCallbackOutput<()>; } diff --git a/src/class/buffer.rs b/src/class/buffer.rs index 90693813644..201490c7569 100644 --- a/src/class/buffer.rs +++ b/src/class/buffer.rs @@ -5,8 +5,9 @@ //! //! For more information check [buffer protocol](https://docs.python.org/3/c-api/buffer.html) //! c-api +use crate::boolean_traits::False; use crate::callback::IntoPyCallbackOutput; -use crate::{ffi, pyclass::MutablePyClass, PyCell, PyRefMut}; +use crate::{ffi, PyCell, PyClass, PyRefMut}; use std::os::raw::c_int; /// Buffer protocol interface @@ -15,7 +16,7 @@ use std::os::raw::c_int; /// c-api. #[allow(unused_variables)] #[deprecated(since = "0.16.0", note = "prefer `#[pymethods]` to `#[pyproto]`")] -pub trait PyBufferProtocol<'p>: MutablePyClass { +pub trait PyBufferProtocol<'p>: PyClass { // No default implementations so that implementors of this trait provide both methods. fn bf_getbuffer( diff --git a/src/class/gc.rs b/src/class/gc.rs index ec4ab6a622a..0d1c0581be6 100644 --- a/src/class/gc.rs +++ b/src/class/gc.rs @@ -3,14 +3,15 @@ //! Python GC support -use crate::{ffi, pyclass::MutablePyClass, PyCell}; +use crate::boolean_traits::False; +use crate::{ffi, PyCell, PyClass}; use std::os::raw::{c_int, c_void}; pub use crate::impl_::pymethods::{PyTraverseError, PyVisit}; /// GC support #[deprecated(since = "0.16.0", note = "prefer `#[pymethods]` to `#[pyproto]`")] -pub trait PyGCProtocol<'p>: MutablePyClass { +pub trait PyGCProtocol<'p>: PyClass { fn __traverse__(&'p self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>; fn __clear__(&'p mut self); } diff --git a/src/class/mapping.rs b/src/class/mapping.rs index 533f5178ba2..e6fb58d7887 100644 --- a/src/class/mapping.rs +++ b/src/class/mapping.rs @@ -4,13 +4,14 @@ //! Python Mapping Interface //! Trait and support implementation for implementing mapping support +use crate::boolean_traits::False; use crate::callback::IntoPyCallbackOutput; -use crate::{pyclass::MutablePyClass, FromPyObject, PyClass, PyObject}; +use crate::{FromPyObject, PyClass, PyObject}; /// Mapping interface #[allow(unused_variables)] #[deprecated(since = "0.16.0", note = "prefer `#[pymethods]` to `#[pyproto]`")] -pub trait PyMappingProtocol<'p>: PyClass { +pub trait PyMappingProtocol<'p>: PyClass { fn __len__(&'p self) -> Self::Result where Self: PyMappingLenProtocol<'p>, @@ -52,13 +53,13 @@ pub trait PyMappingGetItemProtocol<'p>: PyMappingProtocol<'p> { type Result: IntoPyCallbackOutput; } -pub trait PyMappingSetItemProtocol<'p>: PyMappingProtocol<'p> + MutablePyClass { +pub trait PyMappingSetItemProtocol<'p>: PyMappingProtocol<'p> + PyClass { type Key: FromPyObject<'p>; type Value: FromPyObject<'p>; type Result: IntoPyCallbackOutput<()>; } -pub trait PyMappingDelItemProtocol<'p>: PyMappingProtocol<'p> + MutablePyClass { +pub trait PyMappingDelItemProtocol<'p>: PyMappingProtocol<'p> + PyClass { type Key: FromPyObject<'p>; type Result: IntoPyCallbackOutput<()>; } diff --git a/src/class/number.rs b/src/class/number.rs index 2ae7a81388d..e8e6133889a 100644 --- a/src/class/number.rs +++ b/src/class/number.rs @@ -3,9 +3,10 @@ //! Python Number Interface //! Trait and support implementation for implementing number protocol +use crate::boolean_traits::False; use crate::callback::IntoPyCallbackOutput; use crate::err::PyErr; -use crate::{ffi, pyclass::MutablePyClass, FromPyObject, PyClass, PyObject}; +use crate::{ffi, FromPyObject, PyClass, PyObject}; /// Number interface #[allow(unused_variables)] @@ -461,47 +462,47 @@ pub trait PyNumberROrProtocol<'p>: PyNumberProtocol<'p> { type Result: IntoPyCallbackOutput; } -pub trait PyNumberIAddProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass { +pub trait PyNumberIAddProtocol<'p>: PyNumberProtocol<'p> + PyClass { type Other: FromPyObject<'p>; type Result: IntoPyCallbackOutput<()>; } -pub trait PyNumberISubProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass { +pub trait PyNumberISubProtocol<'p>: PyNumberProtocol<'p> + PyClass { type Other: FromPyObject<'p>; type Result: IntoPyCallbackOutput<()>; } -pub trait PyNumberIMulProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass { +pub trait PyNumberIMulProtocol<'p>: PyNumberProtocol<'p> + PyClass { type Other: FromPyObject<'p>; type Result: IntoPyCallbackOutput<()>; } -pub trait PyNumberIMatmulProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass { +pub trait PyNumberIMatmulProtocol<'p>: PyNumberProtocol<'p> + PyClass { type Other: FromPyObject<'p>; type Result: IntoPyCallbackOutput<()>; } -pub trait PyNumberITruedivProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass { +pub trait PyNumberITruedivProtocol<'p>: PyNumberProtocol<'p> + PyClass { type Other: FromPyObject<'p>; type Result: IntoPyCallbackOutput<()>; } -pub trait PyNumberIFloordivProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass { +pub trait PyNumberIFloordivProtocol<'p>: PyNumberProtocol<'p> + PyClass { type Other: FromPyObject<'p>; type Result: IntoPyCallbackOutput<()>; } -pub trait PyNumberIModProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass { +pub trait PyNumberIModProtocol<'p>: PyNumberProtocol<'p> + PyClass { type Other: FromPyObject<'p>; type Result: IntoPyCallbackOutput<()>; } -pub trait PyNumberIDivmodProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass { +pub trait PyNumberIDivmodProtocol<'p>: PyNumberProtocol<'p> + PyClass { type Other: FromPyObject<'p>; type Result: IntoPyCallbackOutput<()>; } -pub trait PyNumberIPowProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass { +pub trait PyNumberIPowProtocol<'p>: PyNumberProtocol<'p> + PyClass { type Other: FromPyObject<'p>; type Result: IntoPyCallbackOutput<()>; // See https://bugs.python.org/issue36379 @@ -509,28 +510,28 @@ pub trait PyNumberIPowProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass { } #[allow(clippy::upper_case_acronyms)] -pub trait PyNumberILShiftProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass { +pub trait PyNumberILShiftProtocol<'p>: PyNumberProtocol<'p> + PyClass { type Other: FromPyObject<'p>; type Result: IntoPyCallbackOutput<()>; } #[allow(clippy::upper_case_acronyms)] -pub trait PyNumberIRShiftProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass { +pub trait PyNumberIRShiftProtocol<'p>: PyNumberProtocol<'p> + PyClass { type Other: FromPyObject<'p>; type Result: IntoPyCallbackOutput<()>; } -pub trait PyNumberIAndProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass { +pub trait PyNumberIAndProtocol<'p>: PyNumberProtocol<'p> + PyClass { type Other: FromPyObject<'p>; type Result: IntoPyCallbackOutput<()>; } -pub trait PyNumberIXorProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass { +pub trait PyNumberIXorProtocol<'p>: PyNumberProtocol<'p> + PyClass { type Other: FromPyObject<'p>; type Result: IntoPyCallbackOutput<()>; } -pub trait PyNumberIOrProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass { +pub trait PyNumberIOrProtocol<'p>: PyNumberProtocol<'p> + PyClass { type Other: FromPyObject<'p>; type Result: IntoPyCallbackOutput<()>; } diff --git a/src/class/sequence.rs b/src/class/sequence.rs index 8e486ef4fcd..bc936092b06 100644 --- a/src/class/sequence.rs +++ b/src/class/sequence.rs @@ -3,10 +3,11 @@ //! Python Sequence Interface //! Trait and support implementation for implementing sequence +use crate::boolean_traits::False; use crate::callback::IntoPyCallbackOutput; use crate::conversion::{FromPyObject, IntoPy}; use crate::err::PyErr; -use crate::{exceptions, ffi, pyclass::MutablePyClass, PyAny, PyCell, PyClass, PyObject}; +use crate::{exceptions, ffi, PyAny, PyCell, PyClass, PyObject}; use std::os::raw::c_int; /// Sequence interface @@ -89,13 +90,13 @@ pub trait PySequenceGetItemProtocol<'p>: PySequenceProtocol<'p> { type Result: IntoPyCallbackOutput; } -pub trait PySequenceSetItemProtocol<'p>: PySequenceProtocol<'p> + MutablePyClass { +pub trait PySequenceSetItemProtocol<'p>: PySequenceProtocol<'p> + PyClass { type Index: FromPyObject<'p> + From; type Value: FromPyObject<'p>; type Result: IntoPyCallbackOutput<()>; } -pub trait PySequenceDelItemProtocol<'p>: PySequenceProtocol<'p> + MutablePyClass { +pub trait PySequenceDelItemProtocol<'p>: PySequenceProtocol<'p> + PyClass { type Index: FromPyObject<'p> + From; type Result: IntoPyCallbackOutput<()>; } @@ -116,14 +117,14 @@ pub trait PySequenceRepeatProtocol<'p>: PySequenceProtocol<'p> { } pub trait PySequenceInplaceConcatProtocol<'p>: - PySequenceProtocol<'p> + IntoPy + MutablePyClass + PySequenceProtocol<'p> + IntoPy + PyClass { type Other: FromPyObject<'p>; type Result: IntoPyCallbackOutput; } pub trait PySequenceInplaceRepeatProtocol<'p>: - PySequenceProtocol<'p> + IntoPy + MutablePyClass + 'p + PySequenceProtocol<'p> + IntoPy + PyClass + 'p { type Index: FromPyObject<'p> + From; type Result: IntoPyCallbackOutput; diff --git a/src/conversion.rs b/src/conversion.rs index 3f4b5452464..df0a960bc1f 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -1,12 +1,12 @@ // Copyright (c) 2017-present PyO3 Project and Contributors //! Defines conversions between Rust and Python types. +use crate::boolean_traits::False; use crate::err::{self, PyDowncastError, PyResult}; use crate::type_object::PyTypeInfo; use crate::types::PyTuple; use crate::{ - ffi, gil, pyclass::MutablePyClass, Py, PyAny, PyCell, PyClass, PyNativeType, PyObject, PyRef, - PyRefMut, Python, + ffi, gil, Py, PyAny, PyCell, PyClass, PyNativeType, PyObject, PyRef, PyRefMut, Python, }; use std::ptr::NonNull; @@ -375,7 +375,7 @@ where impl<'a, T> FromPyObject<'a> for PyRefMut<'a, T> where - T: MutablePyClass, + T: PyClass, { fn extract(obj: &'a PyAny) -> PyResult { let cell: &PyCell = PyTryFrom::try_from(obj)?; diff --git a/src/impl_/pyclass.rs b/src/impl_/pyclass.rs index 60316befe27..0f90aac8aff 100644 --- a/src/impl_/pyclass.rs +++ b/src/impl_/pyclass.rs @@ -1,8 +1,9 @@ use crate::{ + boolean_traits::Frozen, exceptions::{PyAttributeError, PyNotImplementedError}, ffi, impl_::freelist::FreeList, - pycell::{GetBorrowChecker, Mutability, PyCellLayout, PyClassMutability}, + pycell::{GetBorrowChecker, PyCellLayout, PyClassMutability}, pyclass_init::PyObjectInit, type_object::PyLayout, Py, PyAny, PyCell, PyClass, PyErr, PyMethodDefType, PyNativeType, PyResult, PyTypeInfo, Python, @@ -164,8 +165,8 @@ pub trait PyClassImpl: Sized { /// Base class type BaseType: PyTypeInfo + PyClassBaseType; - /// Immutable or mutable - type Mutability: Mutability; + /// Frozen or not + type Frozen: Frozen; /// Immutable or mutable type PyClassMutability: PyClassMutability + GetBorrowChecker; diff --git a/src/instance.rs b/src/instance.rs index 8850b452ecc..0da4f24de32 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -1,3 +1,4 @@ +use crate::boolean_traits::False; // Copyright (c) 2017-present PyO3 Project and Contributors use crate::conversion::PyTryFrom; use crate::err::{self, PyDowncastError, PyErr, PyResult}; @@ -5,8 +6,8 @@ use crate::gil; use crate::pycell::{PyBorrowError, PyBorrowMutError, PyCell}; use crate::types::{PyDict, PyString, PyTuple}; use crate::{ - ffi, pyclass::MutablePyClass, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, PyAny, PyClass, - PyClassInitializer, PyRef, PyRefMut, PyTypeInfo, Python, ToPyObject, + ffi, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, PyAny, PyClass, PyClassInitializer, + PyRef, PyRefMut, PyTypeInfo, Python, ToPyObject, }; use std::marker::PhantomData; use std::mem; @@ -432,7 +433,7 @@ where /// [`try_borrow_mut`](#method.try_borrow_mut). pub fn borrow_mut<'py>(&'py self, py: Python<'py>) -> PyRefMut<'py, T> where - T: MutablePyClass, + T: PyClass, { self.as_ref(py).borrow_mut() } @@ -462,7 +463,7 @@ where py: Python<'py>, ) -> Result, PyBorrowMutError> where - T: MutablePyClass, + T: PyClass, { self.as_ref(py).try_borrow_mut() } @@ -908,7 +909,7 @@ where impl<'a, T> std::convert::From> for Py where - T: MutablePyClass, + T: PyClass, { fn from(pyref: PyRefMut<'a, T>) -> Self { unsafe { Py::from_borrowed_ptr(pyref.py(), pyref.as_ptr()) } diff --git a/src/lib.rs b/src/lib.rs index 8f7741ff7a7..73767e01d8b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -357,6 +357,7 @@ pub use inventory; // Re-exported for `#[pyclass]` and `#[pymethods]` with `mult #[macro_use] mod internal_tricks; +pub mod boolean_traits; pub mod buffer; #[doc(hidden)] pub mod callback; diff --git a/src/pycell.rs b/src/pycell.rs index 928e7c3d1b1..a324108ff99 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -196,11 +196,12 @@ //! [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 and the Interior Mutability Pattern - The Rust Programming Language" +use crate::boolean_traits::{False, Frozen, True}; use crate::exceptions::PyRuntimeError; use crate::impl_::pyclass::{ PyClassBaseType, PyClassDict, PyClassImpl, PyClassThreadChecker, PyClassWeakRef, }; -use crate::pyclass::{MutablePyClass, PyClass}; +use crate::pyclass::PyClass; use crate::pyclass_init::PyClassInitializer; use crate::type_object::{PyLayout, PySizedLayout}; use crate::types::PyAny; @@ -321,6 +322,7 @@ pub trait PyClassMutability { type Checker: PyClassBorrowChecker; type ImmutableChild: PyClassMutability; type MutableChild: PyClassMutability; + type Frozen: Frozen; } pub trait GetBorrowChecker { @@ -360,6 +362,7 @@ impl PyClassMutability for ImmutableClass { type Checker = EmptySlot; type ImmutableChild = ImmutableClass; type MutableChild = MutableClass; + type Frozen = True; } impl PyClassMutability for MutableClass { @@ -367,6 +370,7 @@ impl PyClassMutability for MutableClass { type Checker = BorrowChecker; type ImmutableChild = ExtendsMutableAncestor; type MutableChild = ExtendsMutableAncestor; + type Frozen = False; } impl PyClassMutability for ExtendsMutableAncestor { @@ -374,15 +378,9 @@ impl PyClassMutability for ExtendsMutableAncestor { type Checker = BorrowChecker; type ImmutableChild = ExtendsMutableAncestor; type MutableChild = ExtendsMutableAncestor; + type Frozen = M::Frozen; } -pub trait Mutability {} - -pub struct Mutable; -impl Mutability for Mutable {} -pub struct Immutable; -impl Mutability for Immutable {} - /// Base layout of PyCell. #[doc(hidden)] #[repr(C)] @@ -475,7 +473,7 @@ impl PyCell { /// [`try_borrow_mut`](#method.try_borrow_mut). pub fn borrow_mut(&self) -> PyRefMut<'_, T> where - T: MutablePyClass, + T: PyClass, { self.try_borrow_mut().expect("Already borrowed") } @@ -535,7 +533,7 @@ impl PyCell { /// ``` pub fn try_borrow_mut(&self) -> Result, PyBorrowMutError> where - T: MutablePyClass, + T: PyClass, { self.ensure_threadsafe(); self.borrow_checker() @@ -587,7 +585,7 @@ impl PyCell { #[inline] pub fn replace(&self, t: T) -> T where - T: MutablePyClass, + T: PyClass, { std::mem::replace(&mut *self.borrow_mut(), t) } @@ -599,7 +597,7 @@ impl PyCell { /// Panics if the value is currently borrowed. pub fn replace_with T>(&self, f: F) -> T where - T: MutablePyClass, + T: PyClass, { let mut_borrow = &mut *self.borrow_mut(); let replacement = f(mut_borrow); @@ -614,7 +612,7 @@ impl PyCell { #[inline] pub fn swap(&self, other: &Self) where - T: MutablePyClass, + T: PyClass, { std::mem::swap(&mut *self.borrow_mut(), &mut *other.borrow_mut()) } @@ -801,8 +799,8 @@ impl<'p, T: PyClass> PyRef<'p, T> { impl<'p, T, U> AsRef for PyRef<'p, T> where - T: MutablePyClass, // For now, only mutable classes can be extended - U: MutablePyClass, + T: PyClass, + U: PyClass, { fn as_ref(&self) -> &T::BaseType { unsafe { &*self.inner.ob_base.get_ptr() } @@ -811,8 +809,8 @@ where impl<'p, T, U> PyRef<'p, T> where - T: MutablePyClass, // For now, only mutable classes can be extended - U: MutablePyClass, + T: PyClass, + U: PyClass, { /// Gets a `PyRef`. /// @@ -911,11 +909,11 @@ impl fmt::Debug for PyRef<'_, T> { /// A wrapper type for a mutably borrowed value from a[`PyCell`]``. /// /// See the [module-level documentation](self) for more information. -pub struct PyRefMut<'p, T: MutablePyClass> { +pub struct PyRefMut<'p, T: PyClass> { inner: &'p PyCell, } -impl<'p, T: MutablePyClass> PyRefMut<'p, T> { +impl<'p, T: PyClass> PyRefMut<'p, T> { /// Returns a `Python` token that is bound to the lifetime of the `PyRefMut`. pub fn py(&self) -> Python<'_> { unsafe { Python::assume_gil_acquired() } @@ -924,8 +922,8 @@ impl<'p, T: MutablePyClass> PyRefMut<'p, T> { impl<'p, T, U> AsRef for PyRefMut<'p, T> where - T: PyClass + MutablePyClass, - U: MutablePyClass, + T: PyClass + PyClass, + U: PyClass, { fn as_ref(&self) -> &T::BaseType { unsafe { &*self.inner.ob_base.get_ptr() } @@ -934,8 +932,8 @@ where impl<'p, T, U> AsMut for PyRefMut<'p, T> where - T: PyClass + MutablePyClass, - U: MutablePyClass, + T: PyClass + PyClass, + U: PyClass, { fn as_mut(&mut self) -> &mut T::BaseType { unsafe { &mut *self.inner.ob_base.get_ptr() } @@ -944,8 +942,8 @@ where impl<'p, T, U> PyRefMut<'p, T> where - T: PyClass + MutablePyClass, - U: MutablePyClass, + T: PyClass + PyClass, + U: PyClass, { /// Gets a `PyRef`. /// @@ -959,7 +957,7 @@ where } } -impl<'p, T: MutablePyClass> Deref for PyRefMut<'p, T> { +impl<'p, T: PyClass> Deref for PyRefMut<'p, T> { type Target = T; #[inline] @@ -968,39 +966,41 @@ impl<'p, T: MutablePyClass> Deref for PyRefMut<'p, T> { } } -impl<'p, T: MutablePyClass> DerefMut for PyRefMut<'p, T> { +impl<'p, T: PyClass> DerefMut for PyRefMut<'p, T> { #[inline] fn deref_mut(&mut self) -> &mut T { unsafe { &mut *self.inner.get_ptr() } } } -impl<'p, T: MutablePyClass> Drop for PyRefMut<'p, T> { +impl<'p, T: PyClass> Drop for PyRefMut<'p, T> { fn drop(&mut self) { self.inner.borrow_checker().release_borrow_mut() } } -impl IntoPy for PyRefMut<'_, T> { +impl> IntoPy for PyRefMut<'_, T> { fn into_py(self, py: Python<'_>) -> PyObject { unsafe { PyObject::from_borrowed_ptr(py, self.inner.as_ptr()) } } } -impl<'a, T: MutablePyClass> AsPyPointer for PyRefMut<'a, T> { +impl<'a, T: PyClass> AsPyPointer for PyRefMut<'a, T> { fn as_ptr(&self) -> *mut ffi::PyObject { self.inner.as_ptr() } } -impl<'a, T: MutablePyClass> std::convert::TryFrom<&'a PyCell> for crate::PyRefMut<'a, T> { +impl<'a, T: PyClass> std::convert::TryFrom<&'a PyCell> + for crate::PyRefMut<'a, T> +{ type Error = PyBorrowMutError; fn try_from(cell: &'a crate::PyCell) -> Result { cell.try_borrow_mut() } } -impl fmt::Debug for PyRefMut<'_, T> { +impl + fmt::Debug> fmt::Debug for PyRefMut<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&*(self.deref()), f) } diff --git a/src/pyclass.rs b/src/pyclass.rs index 5596079ffd9..3ce0819b613 100644 --- a/src/pyclass.rs +++ b/src/pyclass.rs @@ -1,5 +1,4 @@ //! `PyClass` and related traits. -use crate::pycell::{Immutable, Mutable}; use crate::{ callback::IntoPyCallbackOutput, exceptions::PyTypeError, @@ -27,12 +26,6 @@ pub trait PyClass: { } -pub trait MutablePyClass: PyClass {} -pub trait ImmutablePyClass: PyClass {} - -impl MutablePyClass for T where T: PyClass {} -impl ImmutablePyClass for T where T: PyClass {} - fn into_raw(vec: Vec) -> *mut c_void { Box::into_raw(vec.into_boxed_slice()) as _ } diff --git a/src/pyclass_init.rs b/src/pyclass_init.rs index 273e6b3f996..629798bbd7c 100644 --- a/src/pyclass_init.rs +++ b/src/pyclass_init.rs @@ -1,7 +1,6 @@ //! Contains initialization utilities for `#[pyclass]`. use crate::callback::IntoPyCallbackOutput; use crate::impl_::pyclass::{PyClassBaseType, PyClassDict, PyClassThreadChecker, PyClassWeakRef}; -use crate::pyclass::MutablePyClass; use crate::{ffi, PyCell, PyClass, PyErr, PyResult, Python}; use crate::{ ffi::PyTypeObject, @@ -270,8 +269,8 @@ where impl From<(S, B)> for PyClassInitializer where - S: MutablePyClass, - B: MutablePyClass, + S: PyClass, + B: PyClass, B::BaseType: PyClassBaseType>, { fn from(sub_and_base: (S, B)) -> PyClassInitializer { diff --git a/tests/test_compile_error.rs b/tests/test_compile_error.rs index 26d9f66685c..349cc20e6e0 100644 --- a/tests/test_compile_error.rs +++ b/tests/test_compile_error.rs @@ -98,7 +98,7 @@ fn _test_compile_errors() { #[rustversion::since(1.60)] fn tests_rust_1_60(t: &trybuild::TestCases) { t.compile_fail("tests/ui/invalid_intern_arg.rs"); - t.compile_fail("tests/ui/invalid_immutable_pyclass_borrow.rs"); + t.compile_fail("tests/ui/invalid_frozen_pyclass_borrow.rs"); t.compile_fail("tests/ui/invalid_pymethod_receiver.rs"); t.compile_fail("tests/ui/missing_intopy.rs"); } diff --git a/tests/test_mutable_pyclass.rs b/tests/test_mutable_pyclass.rs index 8d5072e50fc..975ae898f14 100644 --- a/tests/test_mutable_pyclass.rs +++ b/tests/test_mutable_pyclass.rs @@ -1,5 +1,6 @@ #![cfg(feature = "macros")] +use pyo3::boolean_traits::{False, True}; use pyo3::impl_::pyclass::{PyClassBaseType, PyClassImpl}; use pyo3::prelude::*; use pyo3::pycell::{ @@ -13,7 +14,7 @@ struct MutableBase; #[pyclass(extends = MutableBase, subclass)] struct MutableChildOfMutableBase; -#[pyclass(extends = MutableBase, immutable, subclass)] +#[pyclass(extends = MutableBase, frozen, subclass)] struct ImmutableChildOfMutableBase; #[pyclass(extends = MutableChildOfMutableBase)] @@ -22,19 +23,19 @@ struct MutableChildOfMutableChildOfMutableBase; #[pyclass(extends = ImmutableChildOfMutableBase)] struct MutableChildOfImmutableChildOfMutableBase; -#[pyclass(extends = MutableChildOfMutableBase, immutable)] +#[pyclass(extends = MutableChildOfMutableBase, frozen)] struct ImmutableChildOfMutableChildOfMutableBase; -#[pyclass(extends = ImmutableChildOfMutableBase, immutable)] +#[pyclass(extends = ImmutableChildOfMutableBase, frozen)] struct ImmutableChildOfImmutableChildOfMutableBase; -#[pyclass(immutable, subclass)] +#[pyclass(frozen, subclass)] struct ImmutableBase; #[pyclass(extends = ImmutableBase, subclass)] struct MutableChildOfImmutableBase; -#[pyclass(extends = ImmutableBase, immutable, subclass)] +#[pyclass(extends = ImmutableBase, frozen, subclass)] struct ImmutableChildOfImmutableBase; #[pyclass(extends = MutableChildOfImmutableBase)] @@ -43,16 +44,16 @@ struct MutableChildOfMutableChildOfImmutableBase; #[pyclass(extends = ImmutableChildOfImmutableBase)] struct MutableChildOfImmutableChildOfImmutableBase; -#[pyclass(extends = MutableChildOfImmutableBase, immutable)] +#[pyclass(extends = MutableChildOfImmutableBase, frozen)] struct ImmutableChildOfMutableChildOfImmutableBase; -#[pyclass(extends = ImmutableChildOfImmutableBase, immutable)] +#[pyclass(extends = ImmutableChildOfImmutableBase, frozen)] struct ImmutableChildOfImmutableChildOfImmutableBase; -fn assert_mutable>() {} -fn assert_immutable>() {} +fn assert_mutable>() {} +fn assert_immutable>() {} fn assert_mutable_with_mutable_ancestor< - T: PyClass>, + T: PyClass>, >() // These horrible bounds are necessary for Rust 1.48 but not newer versions where @@ -63,7 +64,7 @@ where { } fn assert_immutable_with_mutable_ancestor< - T: PyClass>, + T: PyClass>, >() // These horrible bounds are necessary for Rust 1.48 but not newer versions where diff --git a/tests/ui/abi3_nativetype_inheritance.stderr b/tests/ui/abi3_nativetype_inheritance.stderr index cf8c2218c6a..24563eef544 100644 --- a/tests/ui/abi3_nativetype_inheritance.stderr +++ b/tests/ui/abi3_nativetype_inheritance.stderr @@ -1,3 +1,17 @@ +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: required by a bound in `PyRefMut` + --> src/pycell.rs + | + | pub struct PyRefMut<'p, T: PyClass> { + | ^^^^^^^^^^^^^^ required by this bound in `PyRefMut` + = 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 | diff --git a/tests/ui/invalid_immutable_pyclass_borrow.rs b/tests/ui/invalid_frozen_pyclass_borrow.rs similarity index 84% rename from tests/ui/invalid_immutable_pyclass_borrow.rs rename to tests/ui/invalid_frozen_pyclass_borrow.rs index a50e7ffded2..37f7ff69ee0 100644 --- a/tests/ui/invalid_immutable_pyclass_borrow.rs +++ b/tests/ui/invalid_frozen_pyclass_borrow.rs @@ -1,6 +1,6 @@ use pyo3::prelude::*; -#[pyclass(immutable)] +#[pyclass(frozen)] pub struct Foo { #[pyo3(get)] field: u32, @@ -13,7 +13,7 @@ fn borrow_mut_fails(foo: Py, py: Python){ #[pyclass(subclass)] struct MutableBase; -#[pyclass(immutable, extends = MutableBase)] +#[pyclass(frozen, extends = MutableBase)] struct ImmutableChild; fn borrow_mut_of_child_fails(child: Py, py: Python){ diff --git a/tests/ui/invalid_frozen_pyclass_borrow.stderr b/tests/ui/invalid_frozen_pyclass_borrow.stderr new file mode 100644 index 00000000000..3f929d8a71e --- /dev/null +++ b/tests/ui/invalid_frozen_pyclass_borrow.stderr @@ -0,0 +1,23 @@ +error[E0271]: type mismatch resolving `::Frozen == False` + --> tests/ui/invalid_frozen_pyclass_borrow.rs:10:33 + | +10 | let borrow = foo.as_ref(py).borrow_mut(); + | ^^^^^^^^^^ expected struct `False`, found struct `True` + | +note: required by a bound in `PyCell::::borrow_mut` + --> src/pycell.rs + | + | T: PyClass, + | ^^^^^^^^^^^^^^ required by this bound in `PyCell::::borrow_mut` + +error[E0271]: type mismatch resolving `::Frozen == False` + --> tests/ui/invalid_frozen_pyclass_borrow.rs:20:35 + | +20 | let borrow = child.as_ref(py).borrow_mut(); + | ^^^^^^^^^^ expected struct `False`, found struct `True` + | +note: required by a bound in `PyCell::::borrow_mut` + --> src/pycell.rs + | + | T: PyClass, + | ^^^^^^^^^^^^^^ required by this bound in `PyCell::::borrow_mut` diff --git a/tests/ui/invalid_immutable_pyclass_borrow.stderr b/tests/ui/invalid_immutable_pyclass_borrow.stderr deleted file mode 100644 index 2b0653a9251..00000000000 --- a/tests/ui/invalid_immutable_pyclass_borrow.stderr +++ /dev/null @@ -1,25 +0,0 @@ -error[E0271]: type mismatch resolving `::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::::borrow_mut` - --> src/pycell.rs - | - | T: MutablePyClass, - | ^^^^^^^^^^^^^^ required by this bound in `PyCell::::borrow_mut` - -error[E0271]: type mismatch resolving `::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::::borrow_mut` - --> src/pycell.rs - | - | T: MutablePyClass, - | ^^^^^^^^^^^^^^ required by this bound in `PyCell::::borrow_mut` diff --git a/tests/ui/invalid_pyclass_args.stderr b/tests/ui/invalid_pyclass_args.stderr index 1fd61d5c364..77c0bc0e0af 100644 --- a/tests/ui/invalid_pyclass_args.stderr +++ b/tests/ui/invalid_pyclass_args.stderr @@ -1,4 +1,4 @@ -error: expected one of: `crate`, `dict`, `extends`, `freelist`, `immutable`, `mapping`, `module`, `name`, `subclass`, `text_signature`, `unsendable`, `weakref`, `gc` +error: expected one of: `crate`, `dict`, `extends`, `freelist`, `frozen`, `mapping`, `module`, `name`, `subclass`, `text_signature`, `unsendable`, `weakref`, `gc` --> tests/ui/invalid_pyclass_args.rs:3:11 | 3 | #[pyclass(extend=pyo3::types::PyDict)] @@ -34,7 +34,7 @@ error: expected string literal 18 | #[pyclass(module = my_module)] | ^^^^^^^^^ -error: expected one of: `crate`, `dict`, `extends`, `freelist`, `immutable`, `mapping`, `module`, `name`, `subclass`, `text_signature`, `unsendable`, `weakref`, `gc` +error: expected one of: `crate`, `dict`, `extends`, `freelist`, `frozen`, `mapping`, `module`, `name`, `subclass`, `text_signature`, `unsendable`, `weakref`, `gc` --> tests/ui/invalid_pyclass_args.rs:21:11 | 21 | #[pyclass(weakrev)]