From 842804882076775743672410e6e3f76c131b640c Mon Sep 17 00:00:00 2001 From: jfh Date: Wed, 13 Oct 2021 18:53:47 +0300 Subject: [PATCH 1/2] Add Unconstructible trait. --- vm/src/slots.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/vm/src/slots.rs b/vm/src/slots.rs index 1819b671f4..10a64cc00b 100644 --- a/vm/src/slots.rs +++ b/vm/src/slots.rs @@ -157,6 +157,20 @@ pub trait SlotConstructor: PyValue { fn py_new(cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult; } +/// For types that cannot be instantiated through Python code. +pub trait Unconstructible: PyValue {} + +impl SlotConstructor for T +where + T: Unconstructible, +{ + type Args = FuncArgs; + + fn py_new(cls: PyTypeRef, _args: Self::Args, vm: &VirtualMachine) -> PyResult { + Err(vm.new_type_error(format!("cannot create {} instances", cls.slot_name()))) + } +} + #[pyimpl] pub trait SlotDestructor: PyValue { #[inline] // for __del__ From ff67ad445cca95b53618a97fdf9c9cde6245f674 Mon Sep 17 00:00:00 2001 From: jfh Date: Wed, 13 Oct 2021 18:54:04 +0300 Subject: [PATCH 2/2] Annotate iterators with Unconstructible. --- .../snippets/forbidden_instantiation.py | 18 ++++++++++++++++++ vm/src/builtins/bytearray.rs | 6 ++++-- vm/src/builtins/bytes.rs | 6 ++++-- vm/src/builtins/dict.rs | 10 ++++++---- vm/src/builtins/list.rs | 10 ++++++---- vm/src/builtins/pystr.rs | 5 +++-- vm/src/builtins/range.rs | 14 ++++++-------- vm/src/builtins/set.rs | 5 +++-- vm/src/builtins/tuple.rs | 5 +++-- 9 files changed, 53 insertions(+), 26 deletions(-) create mode 100644 extra_tests/snippets/forbidden_instantiation.py diff --git a/extra_tests/snippets/forbidden_instantiation.py b/extra_tests/snippets/forbidden_instantiation.py new file mode 100644 index 0000000000..386c894c06 --- /dev/null +++ b/extra_tests/snippets/forbidden_instantiation.py @@ -0,0 +1,18 @@ +from typing import Type +from testutils import assert_raises + +def check_forbidden_instantiation(typ, reverse=False): + f = reversed if reverse else iter + with assert_raises(TypeError): + type(f(typ()))() + +dict_values, dict_items = lambda: {}.values(), lambda: {}.items() +# types with custom forward iterators +iter_types = [list, set, str, bytearray, bytes, dict, tuple, lambda: range(0), dict_items, dict_values] +# types with custom backwards iterators +reviter_types = [list, dict, lambda: range(0), dict_values, dict_items] + +for typ in iter_types: + check_forbidden_instantiation(typ) +for typ in reviter_types: + check_forbidden_instantiation(typ, reverse=True) diff --git a/vm/src/builtins/bytearray.rs b/vm/src/builtins/bytearray.rs index 28a64ccb66..ad6cc9dd6c 100644 --- a/vm/src/builtins/bytearray.rs +++ b/vm/src/builtins/bytearray.rs @@ -24,7 +24,7 @@ use crate::{ sliceable::{PySliceableSequence, PySliceableSequenceMut, SequenceIndex}, slots::{ AsBuffer, AsMapping, Callable, Comparable, Hashable, Iterable, IteratorIterable, - PyComparisonOp, SlotIterator, Unhashable, + PyComparisonOp, SlotConstructor, SlotIterator, Unconstructible, Unhashable, }, utils::Either, IdProtocol, PyClassDef, PyClassImpl, PyComparisonValue, PyContext, PyObjectRef, PyRef, @@ -804,7 +804,7 @@ impl PyValue for PyByteArrayIterator { } } -#[pyimpl(with(SlotIterator))] +#[pyimpl(with(SlotConstructor, SlotIterator))] impl PyByteArrayIterator { #[pymethod(magic)] fn length_hint(&self) -> usize { @@ -824,6 +824,8 @@ impl PyByteArrayIterator { .set_state(state, |obj, pos| pos.min(obj.len()), vm) } } +impl Unconstructible for PyByteArrayIterator {} + impl IteratorIterable for PyByteArrayIterator {} impl SlotIterator for PyByteArrayIterator { fn next(zelf: &PyRef, vm: &VirtualMachine) -> PyResult { diff --git a/vm/src/builtins/bytes.rs b/vm/src/builtins/bytes.rs index 8e6a090e0b..9fb645653c 100644 --- a/vm/src/builtins/bytes.rs +++ b/vm/src/builtins/bytes.rs @@ -12,7 +12,7 @@ use crate::{ protocol::{BufferInternal, BufferOptions, PyBuffer, PyIterReturn, PyMappingMethods}, slots::{ AsBuffer, AsMapping, Callable, Comparable, Hashable, Iterable, IteratorIterable, - PyComparisonOp, SlotConstructor, SlotIterator, + PyComparisonOp, SlotConstructor, SlotIterator, Unconstructible, }, utils::Either, IdProtocol, PyClassImpl, PyComparisonValue, PyContext, PyObjectRef, PyRef, PyResult, PyValue, @@ -639,7 +639,7 @@ impl PyValue for PyBytesIterator { } } -#[pyimpl(with(SlotIterator))] +#[pyimpl(with(SlotConstructor, SlotIterator))] impl PyBytesIterator { #[pymethod(magic)] fn length_hint(&self) -> usize { @@ -660,6 +660,8 @@ impl PyBytesIterator { .set_state(state, |obj, pos| pos.min(obj.len()), vm) } } +impl Unconstructible for PyBytesIterator {} + impl IteratorIterable for PyBytesIterator {} impl SlotIterator for PyBytesIterator { fn next(zelf: &PyRef, vm: &VirtualMachine) -> PyResult { diff --git a/vm/src/builtins/dict.rs b/vm/src/builtins/dict.rs index 2cd368dd9f..17437ab3b6 100644 --- a/vm/src/builtins/dict.rs +++ b/vm/src/builtins/dict.rs @@ -9,8 +9,8 @@ use crate::{ function::{ArgIterable, FuncArgs, IntoPyObject, KwArgs, OptionalArg}, protocol::{PyIterIter, PyIterReturn, PyMappingMethods}, slots::{ - AsMapping, Comparable, Hashable, Iterable, IteratorIterable, PyComparisonOp, SlotIterator, - Unhashable, + AsMapping, Comparable, Hashable, Iterable, IteratorIterable, PyComparisonOp, + SlotConstructor, SlotIterator, Unconstructible, Unhashable, }, vm::{ReprGuard, VirtualMachine}, IdProtocol, ItemProtocol, @@ -762,7 +762,7 @@ macro_rules! dict_view { } } - #[pyimpl(with(SlotIterator))] + #[pyimpl(with(SlotConstructor, SlotIterator))] impl $iter_name { fn new(dict: PyDictRef) -> Self { $iter_name { @@ -776,6 +776,7 @@ macro_rules! dict_view { self.internal.lock().length_hint(|_| self.size.entries_size) } } + impl Unconstructible for $iter_name {} impl IteratorIterable for $iter_name {} impl SlotIterator for $iter_name { @@ -818,7 +819,7 @@ macro_rules! dict_view { } } - #[pyimpl(with(SlotIterator))] + #[pyimpl(with(SlotConstructor, SlotIterator))] impl $reverse_iter_name { fn new(dict: PyDictRef) -> Self { let size = dict.size(); @@ -836,6 +837,7 @@ macro_rules! dict_view { .rev_length_hint(|_| self.size.entries_size) } } + impl Unconstructible for $reverse_iter_name {} impl IteratorIterable for $reverse_iter_name {} impl SlotIterator for $reverse_iter_name { diff --git a/vm/src/builtins/list.rs b/vm/src/builtins/list.rs index 53313373d2..b063b40869 100644 --- a/vm/src/builtins/list.rs +++ b/vm/src/builtins/list.rs @@ -8,8 +8,8 @@ use crate::{ sequence::{self, SimpleSeq}, sliceable::{PySliceableSequence, PySliceableSequenceMut, SequenceIndex}, slots::{ - AsMapping, Comparable, Hashable, Iterable, IteratorIterable, PyComparisonOp, SlotIterator, - Unhashable, + AsMapping, Comparable, Hashable, Iterable, IteratorIterable, PyComparisonOp, + SlotConstructor, SlotIterator, Unconstructible, Unhashable, }, stdlib::sys, utils::Either, @@ -517,7 +517,7 @@ impl PyValue for PyListIterator { } } -#[pyimpl(with(SlotIterator))] +#[pyimpl(with(SlotConstructor, SlotIterator))] impl PyListIterator { #[pymethod(magic)] fn length_hint(&self) -> usize { @@ -538,6 +538,7 @@ impl PyListIterator { .builtins_iter_reduce(|x| x.clone().into(), vm) } } +impl Unconstructible for PyListIterator {} impl IteratorIterable for PyListIterator {} impl SlotIterator for PyListIterator { @@ -564,7 +565,7 @@ impl PyValue for PyListReverseIterator { } } -#[pyimpl(with(SlotIterator))] +#[pyimpl(with(SlotConstructor, SlotIterator))] impl PyListReverseIterator { #[pymethod(magic)] fn length_hint(&self) -> usize { @@ -585,6 +586,7 @@ impl PyListReverseIterator { .builtins_reversed_reduce(|x| x.clone().into(), vm) } } +impl Unconstructible for PyListReverseIterator {} impl IteratorIterable for PyListReverseIterator {} impl SlotIterator for PyListReverseIterator { diff --git a/vm/src/builtins/pystr.rs b/vm/src/builtins/pystr.rs index ec3ea2ae73..28e4747f04 100644 --- a/vm/src/builtins/pystr.rs +++ b/vm/src/builtins/pystr.rs @@ -11,7 +11,7 @@ use crate::{ sliceable::PySliceableSequence, slots::{ Comparable, Hashable, Iterable, IteratorIterable, PyComparisonOp, SlotConstructor, - SlotIterator, + SlotIterator, Unconstructible, }, stdlib::sys, utils::Either, @@ -202,7 +202,7 @@ impl PyValue for PyStrIterator { } } -#[pyimpl(with(SlotIterator))] +#[pyimpl(with(SlotConstructor, SlotIterator))] impl PyStrIterator { #[pymethod(magic)] fn length_hint(&self) -> usize { @@ -226,6 +226,7 @@ impl PyStrIterator { .builtins_iter_reduce(|x| x.clone().into(), vm) } } +impl Unconstructible for PyStrIterator {} impl IteratorIterable for PyStrIterator {} impl SlotIterator for PyStrIterator { diff --git a/vm/src/builtins/range.rs b/vm/src/builtins/range.rs index fbf0f4afeb..3ed80f7288 100644 --- a/vm/src/builtins/range.rs +++ b/vm/src/builtins/range.rs @@ -5,7 +5,8 @@ use crate::{ function::{FuncArgs, OptionalArg}, protocol::{PyIterReturn, PyMappingMethods}, slots::{ - AsMapping, Comparable, Hashable, Iterable, IteratorIterable, PyComparisonOp, SlotIterator, + AsMapping, Comparable, Hashable, Iterable, IteratorIterable, PyComparisonOp, + SlotConstructor, SlotIterator, Unconstructible, }, IdProtocol, IntoPyRef, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, TypeProtocol, VirtualMachine, @@ -511,7 +512,7 @@ impl PyValue for PyLongRangeIterator { } } -#[pyimpl(with(SlotIterator))] +#[pyimpl(with(SlotConstructor, SlotIterator))] impl PyLongRangeIterator { #[pyslot] fn slot_new(_cls: PyTypeRef, _args: FuncArgs, vm: &VirtualMachine) -> PyResult { @@ -545,6 +546,7 @@ impl PyLongRangeIterator { ) } } +impl Unconstructible for PyLongRangeIterator {} impl IteratorIterable for PyLongRangeIterator {} impl SlotIterator for PyLongRangeIterator { @@ -580,13 +582,8 @@ impl PyValue for PyRangeIterator { } } -#[pyimpl(with(SlotIterator))] +#[pyimpl(with(SlotConstructor, SlotIterator))] impl PyRangeIterator { - #[pyslot] - fn slot_new(_cls: PyTypeRef, _args: FuncArgs, vm: &VirtualMachine) -> PyResult { - Err(vm.new_type_error("cannot create 'range_iterator' instances".to_owned())) - } - #[pymethod(magic)] fn length_hint(&self) -> usize { let index = self.index.load(); @@ -615,6 +612,7 @@ impl PyRangeIterator { ) } } +impl Unconstructible for PyRangeIterator {} impl IteratorIterable for PyRangeIterator {} impl SlotIterator for PyRangeIterator { diff --git a/vm/src/builtins/set.rs b/vm/src/builtins/set.rs index a507835358..3c9a04979d 100644 --- a/vm/src/builtins/set.rs +++ b/vm/src/builtins/set.rs @@ -9,7 +9,7 @@ use crate::{ protocol::PyIterReturn, slots::{ Comparable, Hashable, Iterable, IteratorIterable, PyComparisonOp, SlotConstructor, - SlotIterator, Unhashable, + SlotIterator, Unconstructible, Unhashable, }, vm::{ReprGuard, VirtualMachine}, IdProtocol, PyClassImpl, PyComparisonValue, PyContext, PyObjectRef, PyRef, PyResult, PyValue, @@ -884,7 +884,7 @@ impl PyValue for PySetIterator { } } -#[pyimpl(with(SlotIterator))] +#[pyimpl(with(SlotConstructor, SlotIterator))] impl PySetIterator { #[pymethod(magic)] fn length_hint(&self) -> usize { @@ -907,6 +907,7 @@ impl PySetIterator { )) } } +impl Unconstructible for PySetIterator {} impl IteratorIterable for PySetIterator {} impl SlotIterator for PySetIterator { diff --git a/vm/src/builtins/tuple.rs b/vm/src/builtins/tuple.rs index 15637c4d0b..3ee6084b11 100644 --- a/vm/src/builtins/tuple.rs +++ b/vm/src/builtins/tuple.rs @@ -7,7 +7,7 @@ use crate::{ sliceable::PySliceableSequence, slots::{ AsMapping, Comparable, Hashable, Iterable, IteratorIterable, PyComparisonOp, - SlotConstructor, SlotIterator, + SlotConstructor, SlotIterator, Unconstructible, }, stdlib::sys, utils::Either, @@ -379,7 +379,7 @@ impl PyValue for PyTupleIterator { } } -#[pyimpl(with(SlotIterator))] +#[pyimpl(with(SlotConstructor, SlotIterator))] impl PyTupleIterator { #[pymethod(magic)] fn length_hint(&self) -> usize { @@ -400,6 +400,7 @@ impl PyTupleIterator { .builtins_iter_reduce(|x| x.clone().into(), vm) } } +impl Unconstructible for PyTupleIterator {} impl IteratorIterable for PyTupleIterator {} impl SlotIterator for PyTupleIterator {