flag: DISALLOW_INSTANTIATION (#6445)

* Set tp_new slot when build heap/static type

* Improve type tp_call impl to check tp_new existence and error if not exist

* Set DISALLOW_INSTANTIATION flag on several types according to cpython impl

* Allow #[pyslot] for function pointer

* Fix DISALLOW_INSTANTIATION

---------

Signed-off-by: snowapril <sinjihng@gmail.com>
Co-authored-by: snowapril <sinjihng@gmail.com>
This commit is contained in:
Jeong, YunWon
2025-12-17 00:12:03 +09:00
committed by GitHub
parent aef4de4ab8
commit 246fab63f7
30 changed files with 184 additions and 163 deletions

View File

@@ -1788,7 +1788,6 @@ class ClassPropertiesAndMethods(unittest.TestCase, ExtraAssertions):
self.assertEqual(b.foo, 3)
self.assertEqual(b.__class__, D)
@unittest.expectedFailure
def test_bad_new(self):
self.assertRaises(TypeError, object.__new__)
self.assertRaises(TypeError, object.__new__, '')

View File

@@ -954,7 +954,10 @@ where
} else if let Ok(f) = args.item.function_or_method() {
(&f.sig().ident, f.span())
} else {
return Err(self.new_syn_error(args.item.span(), "can only be on a method"));
return Err(self.new_syn_error(
args.item.span(),
"can only be on a method or const function pointer",
));
};
let item_attr = args.attrs.remove(self.index());
@@ -1496,7 +1499,9 @@ impl SlotItemMeta {
}
} else {
let ident_str = self.inner().item_name();
let name = if let Some(stripped) = ident_str.strip_prefix("slot_") {
// Convert to lowercase to handle both SLOT_NEW and slot_new
let ident_lower = ident_str.to_lowercase();
let name = if let Some(stripped) = ident_lower.strip_prefix("slot_") {
proc_macro2::Ident::new(stripped, inner.item_ident.span())
} else {
inner.item_ident.clone()
@@ -1609,7 +1614,6 @@ fn extract_impl_attrs(attr: PunctuatedNestedMeta, item: &Ident) -> Result<Extrac
}];
let mut payload = None;
let mut has_constructor = false;
for attr in attr {
match attr {
NestedMeta::Meta(Meta::List(MetaList { path, nested, .. })) => {
@@ -1634,9 +1638,6 @@ fn extract_impl_attrs(attr: PunctuatedNestedMeta, item: &Ident) -> Result<Extrac
"Try `#[pyclass(with(Constructor, ...))]` instead of `#[pyclass(with(DefaultConstructor, ...))]`. DefaultConstructor implicitly implements Constructor."
)
}
if path.is_ident("Constructor") || path.is_ident("Unconstructible") {
has_constructor = true;
}
(
quote!(<Self as #path>::__extend_py_class),
quote!(<Self as #path>::__OWN_METHOD_DEFS),
@@ -1689,11 +1690,6 @@ fn extract_impl_attrs(attr: PunctuatedNestedMeta, item: &Ident) -> Result<Extrac
attr => bail_span!(attr, "Unknown pyimpl attribute"),
}
}
// TODO: DISALLOW_INSTANTIATION check is required
let _ = has_constructor;
// if !withs.is_empty() && !has_constructor {
// bail_span!(item, "#[pyclass(with(...))] does not have a Constructor. Either #[pyclass(with(Constructor, ...))] or #[pyclass(with(Unconstructible, ...))] is mandatory. Consider to add `impl DefaultConstructor for T {{}}` or `impl Unconstructible for T {{}}`.")
// }
Ok(ExtractedImplAttrs {
payload,

View File

@@ -1399,7 +1399,7 @@ mod array {
internal: PyMutex<PositionIterInternal<PyArrayRef>>,
}
#[pyclass(with(IterNext, Iterable), flags(HAS_DICT))]
#[pyclass(with(IterNext, Iterable), flags(HAS_DICT, DISALLOW_INSTANTIATION))]
impl PyArrayIter {
#[pymethod]
fn __setstate__(&self, state: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {

View File

@@ -908,7 +908,7 @@ mod _csv {
}
}
#[pyclass(with(IterNext, Iterable))]
#[pyclass(with(IterNext, Iterable), flags(DISALLOW_INSTANTIATION))]
impl Reader {
#[pygetset]
fn line_num(&self) -> u64 {
@@ -1059,7 +1059,7 @@ mod _csv {
}
}
#[pyclass]
#[pyclass(flags(DISALLOW_INSTANTIATION))]
impl Writer {
#[pygetset(name = "dialect")]
const fn get_dialect(&self, _vm: &VirtualMachine) -> PyDialect {

View File

@@ -16,7 +16,7 @@ pub(crate) mod _struct {
function::{ArgBytesLike, ArgMemoryBuffer, PosArgs},
match_class,
protocol::PyIterReturn,
types::{Constructor, IterNext, Iterable, Representable, SelfIter, Unconstructible},
types::{Constructor, IterNext, Iterable, Representable, SelfIter},
};
use crossbeam_utils::atomic::AtomicCell;
@@ -189,7 +189,7 @@ pub(crate) mod _struct {
}
}
#[pyclass(with(Unconstructible, IterNext, Iterable))]
#[pyclass(with(IterNext, Iterable), flags(DISALLOW_INSTANTIATION))]
impl UnpackIterator {
#[pymethod]
fn __length_hint__(&self) -> usize {
@@ -197,7 +197,7 @@ pub(crate) mod _struct {
}
}
impl SelfIter for UnpackIterator {}
impl Unconstructible for UnpackIterator {}
impl IterNext for UnpackIterator {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
let size = zelf.format_spec.size;

View File

@@ -75,7 +75,7 @@ mod _sqlite {
sliceable::{SaturatedSliceIter, SliceableSequenceOp},
types::{
AsMapping, AsNumber, AsSequence, Callable, Comparable, Constructor, Hashable,
Initializer, IterNext, Iterable, PyComparisonOp, SelfIter, Unconstructible,
Initializer, IterNext, Iterable, PyComparisonOp, SelfIter,
},
utils::ToCString,
};
@@ -2197,8 +2197,6 @@ mod _sqlite {
inner: PyMutex<Option<BlobInner>>,
}
impl Unconstructible for Blob {}
#[derive(Debug)]
struct BlobInner {
blob: SqliteBlob,
@@ -2211,7 +2209,7 @@ mod _sqlite {
}
}
#[pyclass(with(AsMapping, Unconstructible, AsNumber, AsSequence))]
#[pyclass(flags(DISALLOW_INSTANTIATION), with(AsMapping, AsNumber, AsSequence))]
impl Blob {
#[pymethod]
fn close(&self) {
@@ -2592,9 +2590,7 @@ mod _sqlite {
}
}
impl Unconstructible for Statement {}
#[pyclass(with(Unconstructible))]
#[pyclass(flags(DISALLOW_INSTANTIATION))]
impl Statement {
fn new(
connection: &Connection,

View File

@@ -105,7 +105,7 @@ mod unicodedata {
}
}
#[pyclass]
#[pyclass(flags(DISALLOW_INSTANTIATION))]
impl Ucd {
#[pymethod]
fn category(&self, character: PyStrRef, vm: &VirtualMachine) -> PyResult<String> {

View File

@@ -225,7 +225,7 @@ mod zlib {
inner: PyMutex<PyDecompressInner>,
}
#[pyclass]
#[pyclass(flags(DISALLOW_INSTANTIATION))]
impl PyDecompress {
#[pygetset]
fn eof(&self) -> bool {
@@ -383,7 +383,7 @@ mod zlib {
inner: PyMutex<CompressState<CompressInner>>,
}
#[pyclass]
#[pyclass(flags(DISALLOW_INSTANTIATION))]
impl PyCompress {
#[pymethod]
fn compress(&self, data: ArgBytesLike, vm: &VirtualMachine) -> PyResult<Vec<u8>> {

View File

@@ -8,7 +8,7 @@ use crate::{
frame::FrameRef,
function::OptionalArg,
protocol::PyIterReturn,
types::{IterNext, Iterable, Representable, SelfIter, Unconstructible},
types::{IterNext, Iterable, Representable, SelfIter},
};
use crossbeam_utils::atomic::AtomicCell;
@@ -32,7 +32,7 @@ impl PyPayload for PyAsyncGen {
}
}
#[pyclass(with(PyRef, Unconstructible, Representable))]
#[pyclass(flags(DISALLOW_INSTANTIATION), with(PyRef, Representable))]
impl PyAsyncGen {
pub const fn as_coro(&self) -> &Coro {
&self.inner
@@ -201,8 +201,6 @@ impl Representable for PyAsyncGen {
}
}
impl Unconstructible for PyAsyncGen {}
#[pyclass(module = false, name = "async_generator_wrapped_value")]
#[derive(Debug)]
pub(crate) struct PyAsyncGenWrappedValue(pub PyObjectRef);

View File

@@ -5,7 +5,7 @@ use crate::{
common::wtf8::Wtf8,
convert::TryFromObject,
function::{FuncArgs, PyComparisonValue, PyMethodDef, PyMethodFlags, PyNativeFn},
types::{Callable, Comparable, PyComparisonOp, Representable, Unconstructible},
types::{Callable, Comparable, PyComparisonOp, Representable},
};
use std::fmt;
@@ -74,7 +74,7 @@ impl Callable for PyNativeFunction {
}
}
#[pyclass(with(Callable, Unconstructible), flags(HAS_DICT))]
#[pyclass(with(Callable), flags(HAS_DICT, DISALLOW_INSTANTIATION))]
impl PyNativeFunction {
#[pygetset]
fn __module__(zelf: NativeFunctionOrMethod) -> Option<&'static PyStrInterned> {
@@ -145,8 +145,6 @@ impl Representable for PyNativeFunction {
}
}
impl Unconstructible for PyNativeFunction {}
// `PyCMethodObject` in CPython
#[pyclass(name = "builtin_method", module = false, base = PyNativeFunction, ctx = "builtin_method_type")]
pub struct PyNativeMethod {
@@ -155,8 +153,8 @@ pub struct PyNativeMethod {
}
#[pyclass(
with(Unconstructible, Callable, Comparable, Representable),
flags(HAS_DICT)
with(Callable, Comparable, Representable),
flags(HAS_DICT, DISALLOW_INSTANTIATION)
)]
impl PyNativeMethod {
#[pygetset]
@@ -246,8 +244,6 @@ impl Representable for PyNativeMethod {
}
}
impl Unconstructible for PyNativeMethod {}
pub fn init(context: &Context) {
PyNativeFunction::extend_class(context, context.types.builtin_function_or_method_type);
PyNativeMethod::extend_class(context, context.types.builtin_method_type);

View File

@@ -33,7 +33,7 @@ use crate::{
types::{
AsBuffer, AsMapping, AsNumber, AsSequence, Callable, Comparable, Constructor,
DefaultConstructor, Initializer, IterNext, Iterable, PyComparisonOp, Representable,
SelfIter, Unconstructible,
SelfIter,
},
};
use bstr::ByteSlice;
@@ -865,7 +865,7 @@ impl PyPayload for PyByteArrayIterator {
}
}
#[pyclass(with(Unconstructible, IterNext, Iterable))]
#[pyclass(flags(DISALLOW_INSTANTIATION), with(IterNext, Iterable))]
impl PyByteArrayIterator {
#[pymethod]
fn __length_hint__(&self) -> usize {
@@ -886,8 +886,6 @@ impl PyByteArrayIterator {
}
}
impl Unconstructible for PyByteArrayIterator {}
impl SelfIter for PyByteArrayIterator {}
impl IterNext for PyByteArrayIterator {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {

View File

@@ -25,7 +25,7 @@ use crate::{
sliceable::{SequenceIndex, SliceableSequenceOp},
types::{
AsBuffer, AsMapping, AsNumber, AsSequence, Callable, Comparable, Constructor, Hashable,
IterNext, Iterable, PyComparisonOp, Representable, SelfIter, Unconstructible,
IterNext, Iterable, PyComparisonOp, Representable, SelfIter,
},
};
use bstr::ByteSlice;
@@ -749,7 +749,7 @@ impl PyPayload for PyBytesIterator {
}
}
#[pyclass(with(Unconstructible, IterNext, Iterable))]
#[pyclass(flags(DISALLOW_INSTANTIATION), with(IterNext, Iterable))]
impl PyBytesIterator {
#[pymethod]
fn __length_hint__(&self) -> usize {
@@ -770,7 +770,6 @@ impl PyBytesIterator {
.set_state(state, |obj, pos| pos.min(obj.len()), vm)
}
}
impl Unconstructible for PyBytesIterator {}
impl SelfIter for PyBytesIterator {}
impl IterNext for PyBytesIterator {

View File

@@ -6,7 +6,7 @@ use crate::{
frame::FrameRef,
function::OptionalArg,
protocol::PyIterReturn,
types::{IterNext, Iterable, Representable, SelfIter, Unconstructible},
types::{IterNext, Iterable, Representable, SelfIter},
};
use crossbeam_utils::atomic::AtomicCell;
@@ -24,7 +24,7 @@ impl PyPayload for PyCoroutine {
}
}
#[pyclass(with(Py, Unconstructible, IterNext, Representable))]
#[pyclass(flags(DISALLOW_INSTANTIATION), with(Py, IterNext, Representable))]
impl PyCoroutine {
pub const fn as_coro(&self) -> &Coro {
&self.inner
@@ -123,8 +123,6 @@ impl Py<PyCoroutine> {
}
}
impl Unconstructible for PyCoroutine {}
impl Representable for PyCoroutine {
#[inline]
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {

View File

@@ -4,7 +4,7 @@ use crate::{
builtins::{PyTypeRef, builtin_func::PyNativeMethod, type_},
class::PyClassImpl,
function::{FuncArgs, PyMethodDef, PyMethodFlags, PySetterValue},
types::{Callable, GetDescriptor, Representable, Unconstructible},
types::{Callable, GetDescriptor, Representable},
};
use rustpython_common::lock::PyRwLock;
@@ -105,8 +105,8 @@ impl PyMethodDescriptor {
}
#[pyclass(
with(GetDescriptor, Callable, Unconstructible, Representable),
flags(METHOD_DESCRIPTOR)
with(GetDescriptor, Callable, Representable),
flags(METHOD_DESCRIPTOR, DISALLOW_INSTANTIATION)
)]
impl PyMethodDescriptor {
#[pygetset]
@@ -159,8 +159,6 @@ impl Representable for PyMethodDescriptor {
}
}
impl Unconstructible for PyMethodDescriptor {}
#[derive(Debug)]
pub enum MemberKind {
Bool = 14,
@@ -246,7 +244,10 @@ fn calculate_qualname(descr: &PyDescriptorOwned, vm: &VirtualMachine) -> PyResul
}
}
#[pyclass(with(GetDescriptor, Unconstructible, Representable), flags(BASETYPE))]
#[pyclass(
with(GetDescriptor, Representable),
flags(BASETYPE, DISALLOW_INSTANTIATION)
)]
impl PyMemberDescriptor {
#[pygetset]
fn __doc__(&self) -> Option<String> {
@@ -339,8 +340,6 @@ fn set_slot_at_object(
Ok(())
}
impl Unconstructible for PyMemberDescriptor {}
impl Representable for PyMemberDescriptor {
#[inline]
fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {

View File

@@ -19,7 +19,7 @@ use crate::{
recursion::ReprGuard,
types::{
AsMapping, AsNumber, AsSequence, Callable, Comparable, Constructor, DefaultConstructor,
Initializer, IterNext, Iterable, PyComparisonOp, Representable, SelfIter, Unconstructible,
Initializer, IterNext, Iterable, PyComparisonOp, Representable, SelfIter,
},
vm::VirtualMachine,
};
@@ -848,7 +848,7 @@ macro_rules! dict_view {
}
}
#[pyclass(with(Unconstructible, IterNext, Iterable))]
#[pyclass(flags(DISALLOW_INSTANTIATION), with(IterNext, Iterable))]
impl $iter_name {
fn new(dict: PyDictRef) -> Self {
$iter_name {
@@ -878,8 +878,6 @@ macro_rules! dict_view {
}
}
impl Unconstructible for $iter_name {}
impl SelfIter for $iter_name {}
impl IterNext for $iter_name {
#[allow(clippy::redundant_closure_call)]
@@ -923,7 +921,7 @@ macro_rules! dict_view {
}
}
#[pyclass(with(Unconstructible, IterNext, Iterable))]
#[pyclass(flags(DISALLOW_INSTANTIATION), with(IterNext, Iterable))]
impl $reverse_iter_name {
fn new(dict: PyDictRef) -> Self {
let size = dict.size();
@@ -957,8 +955,6 @@ macro_rules! dict_view {
.rev_length_hint(|_| self.size.entries_size)
}
}
impl Unconstructible for $reverse_iter_name {}
impl SelfIter for $reverse_iter_name {}
impl IterNext for $reverse_iter_name {
#[allow(clippy::redundant_closure_call)]
@@ -1126,16 +1122,18 @@ trait ViewSetOps: DictView {
}
impl ViewSetOps for PyDictKeys {}
#[pyclass(with(
DictView,
Unconstructible,
Comparable,
Iterable,
ViewSetOps,
AsSequence,
AsNumber,
Representable
))]
#[pyclass(
flags(DISALLOW_INSTANTIATION),
with(
DictView,
Comparable,
Iterable,
ViewSetOps,
AsSequence,
AsNumber,
Representable
)
)]
impl PyDictKeys {
#[pymethod]
fn __contains__(zelf: PyObjectRef, key: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
@@ -1147,7 +1145,6 @@ impl PyDictKeys {
PyMappingProxy::from(zelf.dict().clone())
}
}
impl Unconstructible for PyDictKeys {}
impl Comparable for PyDictKeys {
fn cmp(
@@ -1190,16 +1187,18 @@ impl AsNumber for PyDictKeys {
}
impl ViewSetOps for PyDictItems {}
#[pyclass(with(
DictView,
Unconstructible,
Comparable,
Iterable,
ViewSetOps,
AsSequence,
AsNumber,
Representable
))]
#[pyclass(
flags(DISALLOW_INSTANTIATION),
with(
DictView,
Comparable,
Iterable,
ViewSetOps,
AsSequence,
AsNumber,
Representable
)
)]
impl PyDictItems {
#[pymethod]
fn __contains__(zelf: PyObjectRef, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
@@ -1210,7 +1209,6 @@ impl PyDictItems {
PyMappingProxy::from(zelf.dict().clone())
}
}
impl Unconstructible for PyDictItems {}
impl Comparable for PyDictItems {
fn cmp(
@@ -1264,14 +1262,16 @@ impl AsNumber for PyDictItems {
}
}
#[pyclass(with(DictView, Unconstructible, Iterable, AsSequence, Representable))]
#[pyclass(
flags(DISALLOW_INSTANTIATION),
with(DictView, Iterable, AsSequence, Representable)
)]
impl PyDictValues {
#[pygetset]
fn mapping(zelf: PyRef<Self>) -> PyMappingProxy {
PyMappingProxy::from(zelf.dict().clone())
}
}
impl Unconstructible for PyDictValues {}
impl AsSequence for PyDictValues {
fn as_sequence() -> &'static PySequenceMethods {

View File

@@ -8,7 +8,7 @@ use crate::{
class::PyClassImpl,
frame::{Frame, FrameRef},
function::PySetterValue,
types::{Representable, Unconstructible},
types::Representable,
};
use num_traits::Zero;
@@ -16,8 +16,6 @@ pub fn init(context: &Context) {
Frame::extend_class(context, context.types.frame_type);
}
impl Unconstructible for Frame {}
impl Representable for Frame {
#[inline]
fn repr(_zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
@@ -31,7 +29,7 @@ impl Representable for Frame {
}
}
#[pyclass(with(Unconstructible, Py))]
#[pyclass(flags(DISALLOW_INSTANTIATION), with(Py))]
impl Frame {
#[pymethod]
const fn clear(&self) {

View File

@@ -10,7 +10,7 @@ use crate::{
frame::FrameRef,
function::OptionalArg,
protocol::PyIterReturn,
types::{IterNext, Iterable, Representable, SelfIter, Unconstructible},
types::{IterNext, Iterable, Representable, SelfIter},
};
#[pyclass(module = false, name = "generator")]
@@ -26,7 +26,7 @@ impl PyPayload for PyGenerator {
}
}
#[pyclass(with(Py, Unconstructible, IterNext, Iterable))]
#[pyclass(flags(DISALLOW_INSTANTIATION), with(Py, IterNext, Iterable))]
impl PyGenerator {
pub const fn as_coro(&self) -> &Coro {
&self.inner
@@ -114,8 +114,6 @@ impl Py<PyGenerator> {
}
}
impl Unconstructible for PyGenerator {}
impl Representable for PyGenerator {
#[inline]
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {

View File

@@ -7,7 +7,7 @@ use crate::{
builtins::type_::PointerSlot,
class::PyClassImpl,
function::{IntoPyGetterFunc, IntoPySetterFunc, PyGetterFunc, PySetterFunc, PySetterValue},
types::{GetDescriptor, Unconstructible},
types::GetDescriptor,
};
#[pyclass(module = false, name = "getset_descriptor")]
@@ -96,7 +96,7 @@ impl PyGetSet {
}
}
#[pyclass(with(GetDescriptor, Unconstructible))]
#[pyclass(flags(DISALLOW_INSTANTIATION), with(GetDescriptor))]
impl PyGetSet {
// Descriptor methods
@@ -152,7 +152,6 @@ impl PyGetSet {
Ok(unsafe { zelf.class.borrow_static() }.to_owned().into())
}
}
impl Unconstructible for PyGetSet {}
pub(crate) fn init(context: &Context) {
PyGetSet::extend_class(context, context.types.getset_type);

View File

@@ -15,7 +15,7 @@ use crate::{
sliceable::{SequenceIndex, SliceableSequenceMutOp, SliceableSequenceOp},
types::{
AsMapping, AsSequence, Comparable, Constructor, Initializer, IterNext, Iterable,
PyComparisonOp, Representable, SelfIter, Unconstructible,
PyComparisonOp, Representable, SelfIter,
},
utils::collection_repr,
vm::VirtualMachine,
@@ -544,7 +544,7 @@ impl PyPayload for PyListIterator {
}
}
#[pyclass(with(Unconstructible, IterNext, Iterable))]
#[pyclass(flags(DISALLOW_INSTANTIATION), with(IterNext, Iterable))]
impl PyListIterator {
#[pymethod]
fn __length_hint__(&self) -> usize {
@@ -565,7 +565,6 @@ impl PyListIterator {
.builtins_iter_reduce(|x| x.clone().into(), vm)
}
}
impl Unconstructible for PyListIterator {}
impl SelfIter for PyListIterator {}
impl IterNext for PyListIterator {
@@ -590,7 +589,7 @@ impl PyPayload for PyListReverseIterator {
}
}
#[pyclass(with(Unconstructible, IterNext, Iterable))]
#[pyclass(flags(DISALLOW_INSTANTIATION), with(IterNext, Iterable))]
impl PyListReverseIterator {
#[pymethod]
fn __length_hint__(&self) -> usize {
@@ -611,7 +610,6 @@ impl PyListReverseIterator {
.builtins_reversed_reduce(|x| x.clone().into(), vm)
}
}
impl Unconstructible for PyListReverseIterator {}
impl SelfIter for PyListReverseIterator {}
impl IterNext for PyListReverseIterator {

View File

@@ -23,7 +23,7 @@ use crate::{
sliceable::SequenceIndexOp,
types::{
AsBuffer, AsMapping, AsSequence, Comparable, Constructor, Hashable, IterNext, Iterable,
PyComparisonOp, Representable, SelfIter, Unconstructible,
PyComparisonOp, Representable, SelfIter,
},
};
use crossbeam_utils::atomic::AtomicCell;
@@ -1132,7 +1132,7 @@ impl PyPayload for PyMemoryViewIterator {
}
}
#[pyclass(with(Unconstructible, IterNext, Iterable))]
#[pyclass(flags(DISALLOW_INSTANTIATION), with(IterNext, Iterable))]
impl PyMemoryViewIterator {
#[pymethod]
fn __reduce__(&self, vm: &VirtualMachine) -> PyTupleRef {
@@ -1141,7 +1141,6 @@ impl PyMemoryViewIterator {
.builtins_iter_reduce(|x| x.clone().into(), vm)
}
}
impl Unconstructible for PyMemoryViewIterator {}
impl SelfIter for PyMemoryViewIterator {}
impl IterNext for PyMemoryViewIterator {

View File

@@ -11,7 +11,7 @@ use crate::{
protocol::{PyIterReturn, PyMappingMethods, PySequenceMethods},
types::{
AsMapping, AsSequence, Comparable, Hashable, IterNext, Iterable, PyComparisonOp,
Representable, SelfIter, Unconstructible,
Representable, SelfIter,
},
};
use crossbeam_utils::atomic::AtomicCell;
@@ -548,7 +548,7 @@ impl PyPayload for PyLongRangeIterator {
}
}
#[pyclass(with(Unconstructible, IterNext, Iterable))]
#[pyclass(flags(DISALLOW_INSTANTIATION), with(IterNext, Iterable))]
impl PyLongRangeIterator {
#[pymethod]
fn __length_hint__(&self) -> BigInt {
@@ -577,7 +577,6 @@ impl PyLongRangeIterator {
)
}
}
impl Unconstructible for PyLongRangeIterator {}
impl SelfIter for PyLongRangeIterator {}
impl IterNext for PyLongRangeIterator {
@@ -614,7 +613,7 @@ impl PyPayload for PyRangeIterator {
}
}
#[pyclass(with(Unconstructible, IterNext, Iterable))]
#[pyclass(flags(DISALLOW_INSTANTIATION), with(IterNext, Iterable))]
impl PyRangeIterator {
#[pymethod]
fn __length_hint__(&self) -> usize {
@@ -640,7 +639,6 @@ impl PyRangeIterator {
)
}
}
impl Unconstructible for PyRangeIterator {}
impl SelfIter for PyRangeIterator {}
impl IterNext for PyRangeIterator {

View File

@@ -18,7 +18,7 @@ use crate::{
types::AsNumber,
types::{
AsSequence, Comparable, Constructor, DefaultConstructor, Hashable, Initializer, IterNext,
Iterable, PyComparisonOp, Representable, SelfIter, Unconstructible,
Iterable, PyComparisonOp, Representable, SelfIter,
},
utils::collection_repr,
vm::VirtualMachine,
@@ -1304,7 +1304,7 @@ impl PyPayload for PySetIterator {
}
}
#[pyclass(with(Unconstructible, IterNext, Iterable))]
#[pyclass(flags(DISALLOW_INSTANTIATION), with(IterNext, Iterable))]
impl PySetIterator {
#[pymethod]
fn __length_hint__(&self) -> usize {
@@ -1330,7 +1330,6 @@ impl PySetIterator {
))
}
}
impl Unconstructible for PySetIterator {}
impl SelfIter for PySetIterator {}
impl IterNext for PySetIterator {

View File

@@ -21,7 +21,7 @@ use crate::{
sliceable::{SequenceIndex, SliceableSequenceOp},
types::{
AsMapping, AsNumber, AsSequence, Comparable, Constructor, Hashable, IterNext, Iterable,
PyComparisonOp, Representable, SelfIter, Unconstructible,
PyComparisonOp, Representable, SelfIter,
},
};
use ascii::{AsciiChar, AsciiStr, AsciiString};
@@ -282,7 +282,7 @@ impl PyPayload for PyStrIterator {
}
}
#[pyclass(with(Unconstructible, IterNext, Iterable))]
#[pyclass(flags(DISALLOW_INSTANTIATION), with(IterNext, Iterable))]
impl PyStrIterator {
#[pymethod]
fn __length_hint__(&self) -> usize {
@@ -307,8 +307,6 @@ impl PyStrIterator {
}
}
impl Unconstructible for PyStrIterator {}
impl SelfIter for PyStrIterator {}
impl IterNext for PyStrIterator {

View File

@@ -16,7 +16,7 @@ use crate::{
sliceable::{SequenceIndex, SliceableSequenceOp},
types::{
AsMapping, AsSequence, Comparable, Constructor, Hashable, IterNext, Iterable,
PyComparisonOp, Representable, SelfIter, Unconstructible,
PyComparisonOp, Representable, SelfIter,
},
utils::collection_repr,
vm::VirtualMachine,
@@ -533,7 +533,7 @@ impl PyPayload for PyTupleIterator {
}
}
#[pyclass(with(Unconstructible, IterNext, Iterable))]
#[pyclass(flags(DISALLOW_INSTANTIATION), with(IterNext, Iterable))]
impl PyTupleIterator {
#[pymethod]
fn __length_hint__(&self) -> usize {
@@ -554,7 +554,6 @@ impl PyTupleIterator {
.builtins_iter_reduce(|x| x.clone().into(), vm)
}
}
impl Unconstructible for PyTupleIterator {}
impl SelfIter for PyTupleIterator {}
impl IterNext for PyTupleIterator {

View File

@@ -191,13 +191,16 @@ impl PyType {
name: &str,
bases: Vec<PyRef<Self>>,
attrs: PyAttributes,
slots: PyTypeSlots,
mut slots: PyTypeSlots,
metaclass: PyRef<Self>,
ctx: &Context,
) -> Result<PyRef<Self>, String> {
// TODO: ensure clean slot name
// assert_eq!(slots.name.borrow(), "");
// Set HEAPTYPE flag for heap-allocated types
slots.flags |= PyTypeFlags::HEAPTYPE;
let name = ctx.new_str(name);
let heaptype_ext = HeapTypeExt {
name: PyRwLock::new(name.clone()),
@@ -401,6 +404,8 @@ impl PyType {
None,
);
Self::set_new(&new_type.slots, &new_type.base);
let weakref_type = super::PyWeak::static_type();
for base in new_type.bases.read().iter() {
base.subclasses.write().push(
@@ -420,9 +425,6 @@ impl PyType {
for cls in self.mro.read().iter() {
for &name in cls.attributes.read().keys() {
if name == identifier!(ctx, __new__) {
continue;
}
if name.as_bytes().starts_with(b"__") && name.as_bytes().ends_with(b"__") {
slot_name_set.insert(name);
}
@@ -436,6 +438,20 @@ impl PyType {
for attr_name in slot_name_set {
self.update_slot::<true>(attr_name, ctx);
}
Self::set_new(&self.slots, &self.base);
}
fn set_new(slots: &PyTypeSlots, base: &Option<PyTypeRef>) {
if slots.flags.contains(PyTypeFlags::DISALLOW_INSTANTIATION) {
slots.new.store(None)
} else if slots.new.load().is_none() {
slots.new.store(
base.as_ref()
.map(|base| base.slots.new.load())
.unwrap_or(None),
)
}
}
// This is used for class initialization where the vm is not yet available.
@@ -1563,15 +1579,28 @@ impl Callable for PyType {
type Args = FuncArgs;
fn call(zelf: &Py<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
vm_trace!("type_call: {:?}", zelf);
let obj = call_slot_new(zelf.to_owned(), zelf.to_owned(), args.clone(), vm)?;
if (zelf.is(vm.ctx.types.type_type) && args.kwargs.is_empty()) || !obj.fast_isinstance(zelf)
{
if zelf.is(vm.ctx.types.type_type) {
let num_args = args.args.len();
if num_args == 1 && args.kwargs.is_empty() {
return Ok(args.args[0].obj_type());
}
if num_args != 3 {
return Err(vm.new_type_error("type() takes 1 or 3 arguments".to_owned()));
}
}
let obj = if let Some(slot_new) = zelf.slots.new.load() {
slot_new(zelf.to_owned(), args.clone(), vm)?
} else {
return Err(vm.new_type_error(format!("cannot create '{}' instances", zelf.slots.name)));
};
if !obj.class().fast_issubclass(zelf) {
return Ok(obj);
}
let init = obj.class().mro_find_map(|cls| cls.slots.init.load());
if let Some(init_method) = init {
if let Some(init_method) = obj.class().slots.init.load() {
init_method(obj.clone(), args, vm)?;
}
Ok(obj)
@@ -1700,6 +1729,40 @@ pub(crate) fn call_slot_new(
args: FuncArgs,
vm: &VirtualMachine,
) -> PyResult {
// Check DISALLOW_INSTANTIATION flag on subtype (the type being instantiated)
if subtype
.slots
.flags
.has_feature(PyTypeFlags::DISALLOW_INSTANTIATION)
{
return Err(vm.new_type_error(format!("cannot create '{}' instances", subtype.slot_name())));
}
// "is not safe" check (tp_new_wrapper logic)
// Check that the user doesn't do something silly and unsafe like
// object.__new__(dict). To do this, we check that the most derived base
// that's not a heap type is this type.
let mut staticbase = subtype.clone();
while staticbase.slots.flags.has_feature(PyTypeFlags::HEAPTYPE) {
if let Some(base) = staticbase.base.as_ref() {
staticbase = base.clone();
} else {
break;
}
}
// Check if staticbase's tp_new differs from typ's tp_new
let typ_new = typ.slots.new.load();
let staticbase_new = staticbase.slots.new.load();
if typ_new.map(|f| f as usize) != staticbase_new.map(|f| f as usize) {
return Err(vm.new_type_error(format!(
"{}.__new__({}) is not safe, use {}.__new__()",
typ.slot_name(),
subtype.slot_name(),
staticbase.slot_name()
)));
}
let slot_new = typ
.deref()
.mro_find_map(|cls| cls.slots.new.load())

View File

@@ -116,13 +116,23 @@ pub trait PyClassImpl: PyClassDef {
);
}
if class.slots.new.load().is_some() {
let bound_new = Context::genesis().slot_new_wrapper.build_bound_method(
ctx,
class.to_owned().into(),
class,
);
class.set_attr(identifier!(ctx, __new__), bound_new.into());
// Don't add __new__ attribute if slot_new is inherited from object
// (Python doesn't add __new__ to __dict__ for inherited slots)
// Exception: object itself should have __new__ in its dict
if let Some(slot_new) = class.slots.new.load() {
let object_new = ctx.types.object_type.slots.new.load();
let is_object_itself = std::ptr::eq(class, ctx.types.object_type);
let is_inherited_from_object = !is_object_itself
&& object_new.is_some_and(|obj_new| slot_new as usize == obj_new as usize);
if !is_inherited_from_object {
let bound_new = Context::genesis().slot_new_wrapper.build_bound_method(
ctx,
class.to_owned().into(),
class,
);
class.set_attr(identifier!(ctx, __new__), bound_new.into());
}
}
if class.slots.hash.load().map_or(0, |h| h as usize) == hash_not_implemented as usize {

View File

@@ -9,7 +9,6 @@ use crate::{
},
object::PyObjectPayload,
sliceable::SequenceIndexOp,
types::Unconstructible,
};
use itertools::Itertools;
use std::{borrow::Cow, fmt::Debug, ops::Range};
@@ -402,7 +401,7 @@ pub struct VecBuffer {
data: PyMutex<Vec<u8>>,
}
#[pyclass(flags(BASETYPE), with(Unconstructible))]
#[pyclass(flags(BASETYPE, DISALLOW_INSTANTIATION))]
impl VecBuffer {
pub fn take(&self) -> Vec<u8> {
std::mem::take(&mut self.data.lock())
@@ -417,8 +416,6 @@ impl From<Vec<u8>> for VecBuffer {
}
}
impl Unconstructible for VecBuffer {}
impl PyRef<VecBuffer> {
pub fn into_pybuffer(self, readonly: bool) -> PyBuffer {
let len = self.data.lock().len();

View File

@@ -1,6 +1,6 @@
use crate::builtins::PyType;
use crate::function::PySetterValue;
use crate::types::{GetDescriptor, Representable, Unconstructible};
use crate::types::{GetDescriptor, Representable};
use crate::{AsObject, Py, PyObjectRef, PyResult, VirtualMachine};
use num_traits::ToPrimitive;
@@ -85,8 +85,6 @@ impl Representable for PyCField {
}
}
impl Unconstructible for PyCField {}
impl GetDescriptor for PyCField {
fn descr_get(
zelf: PyObjectRef,
@@ -184,7 +182,7 @@ impl PyCField {
#[pyclass(
flags(DISALLOW_INSTANTIATION, IMMUTABLETYPE),
with(Unconstructible, Representable, GetDescriptor)
with(Representable, GetDescriptor)
)]
impl PyCField {
#[pyslot]

View File

@@ -167,7 +167,7 @@ pub(super) mod _os {
ospath::{OsPath, OsPathOrFd, OutputMode},
protocol::PyIterReturn,
recursion::ReprGuard,
types::{IterNext, Iterable, PyStructSequence, Representable, SelfIter, Unconstructible},
types::{IterNext, Iterable, PyStructSequence, Representable, SelfIter},
utils::ToCString,
vm::VirtualMachine,
};
@@ -570,7 +570,7 @@ pub(super) mod _os {
ino: AtomicCell<Option<u64>>,
}
#[pyclass(with(Representable, Unconstructible))]
#[pyclass(flags(DISALLOW_INSTANTIATION), with(Representable))]
impl DirEntry {
#[pygetset]
fn name(&self, vm: &VirtualMachine) -> PyResult {
@@ -760,8 +760,6 @@ pub(super) mod _os {
}
}
}
impl Unconstructible for DirEntry {}
#[pyattr]
#[pyclass(name = "ScandirIter")]
#[derive(Debug, PyPayload)]
@@ -770,7 +768,7 @@ pub(super) mod _os {
mode: OutputMode,
}
#[pyclass(with(IterNext, Iterable, Unconstructible))]
#[pyclass(flags(DISALLOW_INSTANTIATION), with(IterNext, Iterable))]
impl ScandirIterator {
#[pymethod]
fn close(&self) {
@@ -788,7 +786,6 @@ pub(super) mod _os {
zelf.close()
}
}
impl Unconstructible for ScandirIterator {}
impl SelfIter for ScandirIterator {}
impl IterNext for ScandirIterator {
fn next(zelf: &crate::Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {

View File

@@ -922,15 +922,6 @@ pub trait DefaultConstructor: PyPayload + Default + std::fmt::Debug {
}
}
/// For types that cannot be instantiated through Python code.
#[pyclass]
pub trait Unconstructible: PyPayload {
#[pyslot]
fn slot_new(cls: PyTypeRef, _args: FuncArgs, vm: &VirtualMachine) -> PyResult {
Err(vm.new_type_error(format!("cannot create '{}' instances", cls.slot_name())))
}
}
impl<T> Constructor for T
where
T: DefaultConstructor,