Skip to main content

rustpython_vm/builtins/
set.rs

1/*
2 * Builtin set type with a sequence of unique items.
3 */
4use super::{
5    IterStatus, PositionIterInternal, PyDict, PyDictRef, PyGenericAlias, PyTupleRef, PyType,
6    PyTypeRef, builtins_iter,
7};
8use crate::common::lock::LazyLock;
9use crate::{
10    AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
11    atomic_func,
12    class::PyClassImpl,
13    common::{ascii, hash::PyHash, lock::PyMutex, rc::PyRc, wtf8::Wtf8Buf},
14    convert::ToPyResult,
15    dict_inner::{self, DictSize},
16    function::{ArgIterable, FuncArgs, OptionalArg, PosArgs, PyArithmeticValue, PyComparisonValue},
17    protocol::{PyIterReturn, PyNumberMethods, PySequenceMethods},
18    recursion::ReprGuard,
19    types::AsNumber,
20    types::{
21        AsSequence, Comparable, Constructor, DefaultConstructor, Hashable, Initializer, IterNext,
22        Iterable, PyComparisonOp, Representable, SelfIter,
23    },
24    utils::collection_repr,
25    vm::VirtualMachine,
26};
27use alloc::fmt;
28use core::borrow::Borrow;
29use core::ops::Deref;
30use rustpython_common::{
31    atomic::{Ordering, PyAtomic, Radium},
32    hash,
33};
34
35pub type SetContentType = dict_inner::Dict<()>;
36
37#[pyclass(module = false, name = "set", unhashable = true, traverse)]
38#[derive(Default)]
39pub struct PySet {
40    pub(super) inner: PySetInner,
41}
42
43impl PySet {
44    #[deprecated(note = "Use `PySet::default().into_ref(ctx)` instead")]
45    pub fn new_ref(ctx: &Context) -> PyRef<Self> {
46        Self::default().into_ref(ctx)
47    }
48
49    pub fn elements(&self) -> Vec<PyObjectRef> {
50        self.inner.elements()
51    }
52
53    fn fold_op(
54        &self,
55        others: impl core::iter::Iterator<Item = ArgIterable>,
56        op: fn(&PySetInner, ArgIterable, &VirtualMachine) -> PyResult<PySetInner>,
57        vm: &VirtualMachine,
58    ) -> PyResult<Self> {
59        Ok(Self {
60            inner: self.inner.fold_op(others, op, vm)?,
61        })
62    }
63
64    fn op(
65        &self,
66        other: AnySet,
67        op: fn(&PySetInner, ArgIterable, &VirtualMachine) -> PyResult<PySetInner>,
68        vm: &VirtualMachine,
69    ) -> PyResult<Self> {
70        Ok(Self {
71            inner: self
72                .inner
73                .fold_op(core::iter::once(other.into_iterable(vm)?), op, vm)?,
74        })
75    }
76}
77
78#[pyclass(module = false, name = "frozenset", unhashable = true)]
79pub struct PyFrozenSet {
80    inner: PySetInner,
81    hash: PyAtomic<PyHash>,
82}
83
84impl Default for PyFrozenSet {
85    fn default() -> Self {
86        Self {
87            inner: PySetInner::default(),
88            hash: hash::SENTINEL.into(),
89        }
90    }
91}
92
93impl PyFrozenSet {
94    // Also used by ssl.rs windows.
95    pub fn from_iter(
96        vm: &VirtualMachine,
97        it: impl IntoIterator<Item = PyObjectRef>,
98    ) -> PyResult<Self> {
99        let inner = PySetInner::default();
100        for elem in it {
101            inner.add(elem, vm)?;
102        }
103        // FIXME: empty set check
104        Ok(Self {
105            inner,
106            ..Default::default()
107        })
108    }
109
110    pub fn elements(&self) -> Vec<PyObjectRef> {
111        self.inner.elements()
112    }
113
114    fn fold_op(
115        &self,
116        others: impl core::iter::Iterator<Item = ArgIterable>,
117        op: fn(&PySetInner, ArgIterable, &VirtualMachine) -> PyResult<PySetInner>,
118        vm: &VirtualMachine,
119    ) -> PyResult<Self> {
120        Ok(Self {
121            inner: self.inner.fold_op(others, op, vm)?,
122            ..Default::default()
123        })
124    }
125
126    fn op(
127        &self,
128        other: AnySet,
129        op: fn(&PySetInner, ArgIterable, &VirtualMachine) -> PyResult<PySetInner>,
130        vm: &VirtualMachine,
131    ) -> PyResult<Self> {
132        Ok(Self {
133            inner: self
134                .inner
135                .fold_op(core::iter::once(other.into_iterable(vm)?), op, vm)?,
136            ..Default::default()
137        })
138    }
139}
140
141impl fmt::Debug for PySet {
142    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143        // TODO: implement more detailed, non-recursive Debug formatter
144        f.write_str("set")
145    }
146}
147
148impl fmt::Debug for PyFrozenSet {
149    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150        // TODO: implement more detailed, non-recursive Debug formatter
151        f.write_str("PyFrozenSet ")?;
152        f.debug_set().entries(self.elements().iter()).finish()
153    }
154}
155
156impl PyPayload for PySet {
157    #[inline]
158    fn class(ctx: &Context) -> &'static Py<PyType> {
159        ctx.types.set_type
160    }
161}
162
163impl PyPayload for PyFrozenSet {
164    #[inline]
165    fn class(ctx: &Context) -> &'static Py<PyType> {
166        ctx.types.frozenset_type
167    }
168}
169
170#[derive(Default, Clone)]
171pub(super) struct PySetInner {
172    content: PyRc<SetContentType>,
173}
174
175unsafe impl crate::object::Traverse for PySetInner {
176    fn traverse(&self, tracer_fn: &mut crate::object::TraverseFn<'_>) {
177        // FIXME(discord9): Rc means shared ref, so should it be traced?
178        self.content.traverse(tracer_fn)
179    }
180}
181
182impl PySetInner {
183    pub(super) fn from_iter<T>(iter: T, vm: &VirtualMachine) -> PyResult<Self>
184    where
185        T: IntoIterator<Item = PyResult<PyObjectRef>>,
186    {
187        let set = Self::default();
188        for item in iter {
189            set.add(item?, vm)?;
190        }
191        Ok(set)
192    }
193
194    fn fold_op<O>(
195        &self,
196        others: impl core::iter::Iterator<Item = O>,
197        op: fn(&Self, O, &VirtualMachine) -> PyResult<Self>,
198        vm: &VirtualMachine,
199    ) -> PyResult<Self> {
200        let mut res = self.copy();
201        for other in others {
202            res = op(&res, other, vm)?;
203        }
204        Ok(res)
205    }
206
207    fn len(&self) -> usize {
208        self.content.len()
209    }
210
211    fn sizeof(&self) -> usize {
212        self.content.sizeof()
213    }
214
215    fn copy(&self) -> Self {
216        Self {
217            content: PyRc::new((*self.content).clone()),
218        }
219    }
220
221    fn contains(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
222        self.retry_op_with_frozenset(needle, vm, |needle, vm| self.content.contains(vm, needle))
223    }
224
225    fn compare(&self, other: &Self, op: PyComparisonOp, vm: &VirtualMachine) -> PyResult<bool> {
226        if op == PyComparisonOp::Ne {
227            return self.compare(other, PyComparisonOp::Eq, vm).map(|eq| !eq);
228        }
229        if !op.eval_ord(self.len().cmp(&other.len())) {
230            return Ok(false);
231        }
232
233        let (superset, subset) = match op {
234            PyComparisonOp::Lt | PyComparisonOp::Le => (other, self),
235            _ => (self, other),
236        };
237
238        for key in subset.elements() {
239            if !superset.contains(&key, vm)? {
240                return Ok(false);
241            }
242        }
243        Ok(true)
244    }
245
246    pub(super) fn union(&self, other: ArgIterable, vm: &VirtualMachine) -> PyResult<Self> {
247        let set = self.clone();
248        for item in other.iter(vm)? {
249            set.add(item?, vm)?;
250        }
251
252        Ok(set)
253    }
254
255    pub(super) fn intersection(&self, other: ArgIterable, vm: &VirtualMachine) -> PyResult<Self> {
256        let set = Self::default();
257        for item in other.iter(vm)? {
258            let obj = item?;
259            if self.contains(&obj, vm)? {
260                set.add(obj, vm)?;
261            }
262        }
263        Ok(set)
264    }
265
266    pub(super) fn difference(&self, other: ArgIterable, vm: &VirtualMachine) -> PyResult<Self> {
267        let set = self.copy();
268        for item in other.iter(vm)? {
269            set.content.delete_if_exists(vm, &*item?)?;
270        }
271        Ok(set)
272    }
273
274    pub(super) fn symmetric_difference(
275        &self,
276        other: ArgIterable,
277        vm: &VirtualMachine,
278    ) -> PyResult<Self> {
279        let new_inner = self.clone();
280
281        // We want to remove duplicates in other
282        let other_set = Self::from_iter(other.iter(vm)?, vm)?;
283
284        for item in other_set.elements() {
285            new_inner.content.delete_or_insert(vm, &item, ())?
286        }
287
288        Ok(new_inner)
289    }
290
291    fn issuperset(&self, other: ArgIterable, vm: &VirtualMachine) -> PyResult<bool> {
292        for item in other.iter(vm)? {
293            if !self.contains(&*item?, vm)? {
294                return Ok(false);
295            }
296        }
297        Ok(true)
298    }
299
300    fn issubset(&self, other: ArgIterable, vm: &VirtualMachine) -> PyResult<bool> {
301        let other_set = Self::from_iter(other.iter(vm)?, vm)?;
302        self.compare(&other_set, PyComparisonOp::Le, vm)
303    }
304
305    pub(super) fn isdisjoint(&self, other: ArgIterable, vm: &VirtualMachine) -> PyResult<bool> {
306        for item in other.iter(vm)? {
307            if self.contains(&*item?, vm)? {
308                return Ok(false);
309            }
310        }
311        Ok(true)
312    }
313
314    fn iter(&self) -> PySetIterator {
315        PySetIterator {
316            size: self.content.size(),
317            internal: PyMutex::new(PositionIterInternal::new(self.content.clone(), 0)),
318        }
319    }
320
321    fn repr(&self, class_name: Option<&str>, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
322        collection_repr(class_name, "{", "}", self.elements().iter(), vm)
323    }
324
325    fn add(&self, item: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
326        self.content.insert(vm, &*item, ())
327    }
328
329    fn remove(&self, item: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
330        self.retry_op_with_frozenset(&item, vm, |item, vm| self.content.delete(vm, item))
331    }
332
333    fn discard(&self, item: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
334        self.retry_op_with_frozenset(item, vm, |item, vm| self.content.delete_if_exists(vm, item))
335    }
336
337    fn clear(&self) {
338        self.content.clear()
339    }
340
341    fn elements(&self) -> Vec<PyObjectRef> {
342        self.content.keys()
343    }
344
345    fn pop(&self, vm: &VirtualMachine) -> PyResult {
346        // TODO: should be pop_front, but that requires rearranging every index
347        if let Some((key, _)) = self.content.pop_back() {
348            Ok(key)
349        } else {
350            let err_msg = vm.ctx.new_str(ascii!("pop from an empty set")).into();
351            Err(vm.new_key_error(err_msg))
352        }
353    }
354
355    fn update(
356        &self,
357        others: impl core::iter::Iterator<Item = ArgIterable>,
358        vm: &VirtualMachine,
359    ) -> PyResult<()> {
360        for iterable in others {
361            for item in iterable.iter(vm)? {
362                self.add(item?, vm)?;
363            }
364        }
365        Ok(())
366    }
367
368    fn update_internal(&self, iterable: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
369        // check AnySet
370        if let Ok(any_set) = AnySet::try_from_object(vm, iterable.to_owned()) {
371            self.merge_set(any_set, vm)
372        // check Dict
373        } else if let Ok(dict) = iterable.to_owned().downcast_exact::<PyDict>(vm) {
374            self.merge_dict(dict.into_pyref(), vm)
375        } else {
376            // add iterable that is not AnySet or Dict
377            for item in iterable.try_into_value::<ArgIterable>(vm)?.iter(vm)? {
378                self.add(item?, vm)?;
379            }
380            Ok(())
381        }
382    }
383
384    fn merge_set(&self, any_set: AnySet, vm: &VirtualMachine) -> PyResult<()> {
385        for item in any_set.as_inner().elements() {
386            self.add(item, vm)?;
387        }
388        Ok(())
389    }
390
391    fn merge_dict(&self, dict: PyDictRef, vm: &VirtualMachine) -> PyResult<()> {
392        for (key, _value) in dict {
393            self.add(key, vm)?;
394        }
395        Ok(())
396    }
397
398    fn intersection_update(
399        &self,
400        others: impl core::iter::Iterator<Item = ArgIterable>,
401        vm: &VirtualMachine,
402    ) -> PyResult<()> {
403        let temp_inner = self.fold_op(others, Self::intersection, vm)?;
404        self.clear();
405        for obj in temp_inner.elements() {
406            self.add(obj, vm)?;
407        }
408        Ok(())
409    }
410
411    fn difference_update(
412        &self,
413        others: impl core::iter::Iterator<Item = ArgIterable>,
414        vm: &VirtualMachine,
415    ) -> PyResult<()> {
416        for iterable in others {
417            let items = iterable.iter(vm)?.collect::<Result<Vec<_>, _>>()?;
418            for item in items {
419                self.content.delete_if_exists(vm, &*item)?;
420            }
421        }
422        Ok(())
423    }
424
425    fn symmetric_difference_update(
426        &self,
427        others: impl core::iter::Iterator<Item = ArgIterable>,
428        vm: &VirtualMachine,
429    ) -> PyResult<()> {
430        for iterable in others {
431            // We want to remove duplicates in iterable
432            let iterable_set = Self::from_iter(iterable.iter(vm)?, vm)?;
433            for item in iterable_set.elements() {
434                self.content.delete_or_insert(vm, &item, ())?;
435            }
436        }
437        Ok(())
438    }
439
440    fn hash(&self, vm: &VirtualMachine) -> PyResult<PyHash> {
441        // Work to increase the bit dispersion for closely spaced hash values.
442        // This is important because some use cases have many combinations of a
443        // small number of elements with nearby hashes so that many distinct
444        // combinations collapse to only a handful of distinct hash values.
445        const fn _shuffle_bits(h: u64) -> u64 {
446            ((h ^ 89869747) ^ (h.wrapping_shl(16))).wrapping_mul(3644798167)
447        }
448        // Factor in the number of active entries
449        let mut hash: u64 = (self.len() as u64 + 1).wrapping_mul(1927868237);
450        // Xor-in shuffled bits from every entry's hash field because xor is
451        // commutative and a frozenset hash should be independent of order.
452        hash = self.content.try_fold_keys(hash, |h, element| {
453            Ok(h ^ _shuffle_bits(element.hash(vm)? as u64))
454        })?;
455        // Disperse patterns arising in nested frozen-sets
456        hash ^= (hash >> 11) ^ (hash >> 25);
457        hash = hash.wrapping_mul(69069).wrapping_add(907133923);
458        // -1 is reserved as an error code
459        if hash == u64::MAX {
460            hash = 590923713;
461        }
462        Ok(hash as PyHash)
463    }
464
465    // Run operation, on failure, if item is a set/set subclass, convert it
466    // into a frozenset and try the operation again. Propagates original error
467    // on failure to convert and restores item in KeyError on failure (remove).
468    fn retry_op_with_frozenset<T, F>(
469        &self,
470        item: &PyObject,
471        vm: &VirtualMachine,
472        op: F,
473    ) -> PyResult<T>
474    where
475        F: Fn(&PyObject, &VirtualMachine) -> PyResult<T>,
476    {
477        op(item, vm).or_else(|original_err| {
478            item.downcast_ref::<PySet>()
479                // Keep original error around.
480                .ok_or(original_err)
481                .and_then(|set| {
482                    op(
483                        &PyFrozenSet {
484                            inner: set.inner.copy(),
485                            ..Default::default()
486                        }
487                        .into_pyobject(vm),
488                        vm,
489                    )
490                    // If operation raised KeyError, report original set (set.remove)
491                    .map_err(|op_err| {
492                        if op_err.fast_isinstance(vm.ctx.exceptions.key_error) {
493                            vm.new_key_error(item.to_owned())
494                        } else {
495                            op_err
496                        }
497                    })
498                })
499        })
500    }
501}
502
503fn extract_set(obj: &PyObject) -> Option<&PySetInner> {
504    match_class!(match obj {
505        ref set @ PySet => Some(&set.inner),
506        ref frozen @ PyFrozenSet => Some(&frozen.inner),
507        _ => None,
508    })
509}
510
511fn reduce_set(
512    zelf: &PyObject,
513    vm: &VirtualMachine,
514) -> PyResult<(PyTypeRef, PyTupleRef, Option<PyDictRef>)> {
515    Ok((
516        zelf.class().to_owned(),
517        vm.new_tuple((extract_set(zelf)
518            .unwrap_or(&PySetInner::default())
519            .elements(),)),
520        zelf.dict(),
521    ))
522}
523
524#[pyclass(
525    with(
526        Constructor,
527        Initializer,
528        AsSequence,
529        Comparable,
530        Iterable,
531        AsNumber,
532        Representable
533    ),
534    flags(BASETYPE, _MATCH_SELF, HAS_WEAKREF)
535)]
536impl PySet {
537    fn __len__(&self) -> usize {
538        self.inner.len()
539    }
540
541    fn __contains__(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
542        self.inner.contains(needle, vm)
543    }
544
545    #[pymethod]
546    fn __sizeof__(&self) -> usize {
547        core::mem::size_of::<Self>() + self.inner.sizeof()
548    }
549
550    #[pymethod]
551    fn copy(&self) -> Self {
552        Self {
553            inner: self.inner.copy(),
554        }
555    }
556
557    #[pymethod]
558    fn union(&self, others: PosArgs<ArgIterable>, vm: &VirtualMachine) -> PyResult<Self> {
559        self.fold_op(others.into_iter(), PySetInner::union, vm)
560    }
561
562    #[pymethod]
563    fn intersection(&self, others: PosArgs<ArgIterable>, vm: &VirtualMachine) -> PyResult<Self> {
564        self.fold_op(others.into_iter(), PySetInner::intersection, vm)
565    }
566
567    #[pymethod]
568    fn difference(&self, others: PosArgs<ArgIterable>, vm: &VirtualMachine) -> PyResult<Self> {
569        self.fold_op(others.into_iter(), PySetInner::difference, vm)
570    }
571
572    #[pymethod]
573    fn symmetric_difference(
574        &self,
575        others: PosArgs<ArgIterable>,
576        vm: &VirtualMachine,
577    ) -> PyResult<Self> {
578        self.fold_op(others.into_iter(), PySetInner::symmetric_difference, vm)
579    }
580
581    #[pymethod]
582    fn issubset(&self, other: ArgIterable, vm: &VirtualMachine) -> PyResult<bool> {
583        self.inner.issubset(other, vm)
584    }
585
586    #[pymethod]
587    fn issuperset(&self, other: ArgIterable, vm: &VirtualMachine) -> PyResult<bool> {
588        self.inner.issuperset(other, vm)
589    }
590
591    #[pymethod]
592    fn isdisjoint(&self, other: ArgIterable, vm: &VirtualMachine) -> PyResult<bool> {
593        self.inner.isdisjoint(other, vm)
594    }
595
596    fn __or__(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyArithmeticValue<Self>> {
597        if let Ok(other) = AnySet::try_from_object(vm, other) {
598            Ok(PyArithmeticValue::Implemented(self.op(
599                other,
600                PySetInner::union,
601                vm,
602            )?))
603        } else {
604            Ok(PyArithmeticValue::NotImplemented)
605        }
606    }
607
608    fn __and__(
609        &self,
610        other: PyObjectRef,
611        vm: &VirtualMachine,
612    ) -> PyResult<PyArithmeticValue<Self>> {
613        if let Ok(other) = AnySet::try_from_object(vm, other) {
614            Ok(PyArithmeticValue::Implemented(self.op(
615                other,
616                PySetInner::intersection,
617                vm,
618            )?))
619        } else {
620            Ok(PyArithmeticValue::NotImplemented)
621        }
622    }
623
624    fn __sub__(
625        &self,
626        other: PyObjectRef,
627        vm: &VirtualMachine,
628    ) -> PyResult<PyArithmeticValue<Self>> {
629        if let Ok(other) = AnySet::try_from_object(vm, other) {
630            Ok(PyArithmeticValue::Implemented(self.op(
631                other,
632                PySetInner::difference,
633                vm,
634            )?))
635        } else {
636            Ok(PyArithmeticValue::NotImplemented)
637        }
638    }
639
640    fn __rsub__(
641        zelf: PyRef<Self>,
642        other: PyObjectRef,
643        vm: &VirtualMachine,
644    ) -> PyResult<PyArithmeticValue<Self>> {
645        if let Ok(other) = AnySet::try_from_object(vm, other) {
646            Ok(PyArithmeticValue::Implemented(Self {
647                inner: other
648                    .as_inner()
649                    .difference(ArgIterable::try_from_object(vm, zelf.into())?, vm)?,
650            }))
651        } else {
652            Ok(PyArithmeticValue::NotImplemented)
653        }
654    }
655
656    fn __xor__(
657        &self,
658        other: PyObjectRef,
659        vm: &VirtualMachine,
660    ) -> PyResult<PyArithmeticValue<Self>> {
661        if let Ok(other) = AnySet::try_from_object(vm, other) {
662            Ok(PyArithmeticValue::Implemented(self.op(
663                other,
664                PySetInner::symmetric_difference,
665                vm,
666            )?))
667        } else {
668            Ok(PyArithmeticValue::NotImplemented)
669        }
670    }
671
672    #[pymethod]
673    pub fn add(&self, item: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
674        self.inner.add(item, vm)
675    }
676
677    #[pymethod]
678    fn remove(&self, item: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
679        self.inner.remove(item, vm)
680    }
681
682    #[pymethod]
683    fn discard(&self, item: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
684        self.inner.discard(&item, vm).map(|_| ())
685    }
686
687    #[pymethod]
688    fn clear(&self) {
689        self.inner.clear()
690    }
691
692    #[pymethod]
693    fn pop(&self, vm: &VirtualMachine) -> PyResult {
694        self.inner.pop(vm)
695    }
696
697    fn __ior__(zelf: PyRef<Self>, set: AnySet, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
698        zelf.inner.update(set.into_iterable_iter(vm)?, vm)?;
699        Ok(zelf)
700    }
701
702    #[pymethod]
703    fn update(&self, others: PosArgs<PyObjectRef>, vm: &VirtualMachine) -> PyResult<()> {
704        for iterable in others {
705            self.inner.update_internal(iterable, vm)?;
706        }
707        Ok(())
708    }
709
710    #[pymethod]
711    fn intersection_update(
712        &self,
713        others: PosArgs<ArgIterable>,
714        vm: &VirtualMachine,
715    ) -> PyResult<()> {
716        self.inner.intersection_update(others.into_iter(), vm)?;
717        Ok(())
718    }
719
720    fn __iand__(zelf: PyRef<Self>, set: AnySet, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
721        if !set.is(zelf.as_object()) {
722            zelf.inner
723                .intersection_update(core::iter::once(set.into_iterable(vm)?), vm)?;
724        }
725        Ok(zelf)
726    }
727
728    #[pymethod]
729    fn difference_update(&self, others: PosArgs<ArgIterable>, vm: &VirtualMachine) -> PyResult<()> {
730        self.inner.difference_update(others.into_iter(), vm)
731    }
732
733    fn __isub__(zelf: PyRef<Self>, set: AnySet, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
734        if set.is(zelf.as_object()) {
735            zelf.inner.clear();
736        } else {
737            zelf.inner
738                .difference_update(set.into_iterable_iter(vm)?, vm)?;
739        }
740        Ok(zelf)
741    }
742
743    #[pymethod]
744    fn symmetric_difference_update(
745        &self,
746        others: PosArgs<ArgIterable>,
747        vm: &VirtualMachine,
748    ) -> PyResult<()> {
749        self.inner
750            .symmetric_difference_update(others.into_iter(), vm)
751    }
752
753    fn __ixor__(zelf: PyRef<Self>, set: AnySet, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
754        if set.is(zelf.as_object()) {
755            zelf.inner.clear();
756        } else {
757            zelf.inner
758                .symmetric_difference_update(set.into_iterable_iter(vm)?, vm)?;
759        }
760        Ok(zelf)
761    }
762
763    #[pymethod]
764    fn __reduce__(
765        zelf: PyRef<Self>,
766        vm: &VirtualMachine,
767    ) -> PyResult<(PyTypeRef, PyTupleRef, Option<PyDictRef>)> {
768        reduce_set(zelf.as_ref(), vm)
769    }
770
771    #[pyclassmethod]
772    fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
773        PyGenericAlias::from_args(cls, args, vm)
774    }
775}
776
777impl DefaultConstructor for PySet {}
778
779impl Initializer for PySet {
780    type Args = OptionalArg<PyObjectRef>;
781
782    fn init(zelf: PyRef<Self>, iterable: Self::Args, vm: &VirtualMachine) -> PyResult<()> {
783        zelf.clear();
784        if let OptionalArg::Present(it) = iterable {
785            zelf.update(PosArgs::new(vec![it]), vm)?;
786        }
787        Ok(())
788    }
789}
790
791impl AsSequence for PySet {
792    fn as_sequence() -> &'static PySequenceMethods {
793        static AS_SEQUENCE: LazyLock<PySequenceMethods> = LazyLock::new(|| PySequenceMethods {
794            length: atomic_func!(|seq, _vm| Ok(PySet::sequence_downcast(seq).__len__())),
795            contains: atomic_func!(
796                |seq, needle, vm| PySet::sequence_downcast(seq).__contains__(needle, vm)
797            ),
798            ..PySequenceMethods::NOT_IMPLEMENTED
799        });
800        &AS_SEQUENCE
801    }
802}
803
804impl Comparable for PySet {
805    fn cmp(
806        zelf: &crate::Py<Self>,
807        other: &PyObject,
808        op: PyComparisonOp,
809        vm: &VirtualMachine,
810    ) -> PyResult<PyComparisonValue> {
811        extract_set(other).map_or(Ok(PyComparisonValue::NotImplemented), |other| {
812            Ok(zelf.inner.compare(other, op, vm)?.into())
813        })
814    }
815}
816
817impl Iterable for PySet {
818    fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
819        Ok(zelf.inner.iter().into_pyobject(vm))
820    }
821}
822
823impl AsNumber for PySet {
824    fn as_number() -> &'static PyNumberMethods {
825        static AS_NUMBER: PyNumberMethods = PyNumberMethods {
826            // Binary ops check both operands are sets (like CPython's set_sub, etc.)
827            // This is needed because __rsub__ swaps operands: a.__rsub__(b) calls subtract(b, a)
828            subtract: Some(|a, b, vm| {
829                if !AnySet::check(a, vm) || !AnySet::check(b, vm) {
830                    return Ok(vm.ctx.not_implemented());
831                }
832                if let Some(a) = a.downcast_ref::<PySet>() {
833                    a.__sub__(b.to_owned(), vm).to_pyresult(vm)
834                } else if let Some(a) = a.downcast_ref::<PyFrozenSet>() {
835                    // When called via __rsub__, a might be PyFrozenSet
836                    a.__sub__(b.to_owned(), vm)
837                        .map(|r| {
838                            r.map(|s| PySet {
839                                inner: s.inner.clone(),
840                            })
841                        })
842                        .to_pyresult(vm)
843                } else {
844                    Ok(vm.ctx.not_implemented())
845                }
846            }),
847            and: Some(|a, b, vm| {
848                if !AnySet::check(a, vm) || !AnySet::check(b, vm) {
849                    return Ok(vm.ctx.not_implemented());
850                }
851                if let Some(a) = a.downcast_ref::<PySet>() {
852                    a.__and__(b.to_owned(), vm).to_pyresult(vm)
853                } else if let Some(a) = a.downcast_ref::<PyFrozenSet>() {
854                    a.__and__(b.to_owned(), vm)
855                        .map(|r| {
856                            r.map(|s| PySet {
857                                inner: s.inner.clone(),
858                            })
859                        })
860                        .to_pyresult(vm)
861                } else {
862                    Ok(vm.ctx.not_implemented())
863                }
864            }),
865            xor: Some(|a, b, vm| {
866                if !AnySet::check(a, vm) || !AnySet::check(b, vm) {
867                    return Ok(vm.ctx.not_implemented());
868                }
869                if let Some(a) = a.downcast_ref::<PySet>() {
870                    a.__xor__(b.to_owned(), vm).to_pyresult(vm)
871                } else if let Some(a) = a.downcast_ref::<PyFrozenSet>() {
872                    a.__xor__(b.to_owned(), vm)
873                        .map(|r| {
874                            r.map(|s| PySet {
875                                inner: s.inner.clone(),
876                            })
877                        })
878                        .to_pyresult(vm)
879                } else {
880                    Ok(vm.ctx.not_implemented())
881                }
882            }),
883            or: Some(|a, b, vm| {
884                if !AnySet::check(a, vm) || !AnySet::check(b, vm) {
885                    return Ok(vm.ctx.not_implemented());
886                }
887                if let Some(a) = a.downcast_ref::<PySet>() {
888                    a.__or__(b.to_owned(), vm).to_pyresult(vm)
889                } else if let Some(a) = a.downcast_ref::<PyFrozenSet>() {
890                    a.__or__(b.to_owned(), vm)
891                        .map(|r| {
892                            r.map(|s| PySet {
893                                inner: s.inner.clone(),
894                            })
895                        })
896                        .to_pyresult(vm)
897                } else {
898                    Ok(vm.ctx.not_implemented())
899                }
900            }),
901            inplace_subtract: Some(|a, b, vm| {
902                if let Some(a) = a.downcast_ref::<PySet>() {
903                    PySet::__isub__(a.to_owned(), AnySet::try_from_object(vm, b.to_owned())?, vm)
904                        .to_pyresult(vm)
905                } else {
906                    Ok(vm.ctx.not_implemented())
907                }
908            }),
909            inplace_and: Some(|a, b, vm| {
910                if let Some(a) = a.downcast_ref::<PySet>() {
911                    PySet::__iand__(a.to_owned(), AnySet::try_from_object(vm, b.to_owned())?, vm)
912                        .to_pyresult(vm)
913                } else {
914                    Ok(vm.ctx.not_implemented())
915                }
916            }),
917            inplace_xor: Some(|a, b, vm| {
918                if let Some(a) = a.downcast_ref::<PySet>() {
919                    PySet::__ixor__(a.to_owned(), AnySet::try_from_object(vm, b.to_owned())?, vm)
920                        .to_pyresult(vm)
921                } else {
922                    Ok(vm.ctx.not_implemented())
923                }
924            }),
925            inplace_or: Some(|a, b, vm| {
926                if let Some(a) = a.downcast_ref::<PySet>() {
927                    PySet::__ior__(a.to_owned(), AnySet::try_from_object(vm, b.to_owned())?, vm)
928                        .to_pyresult(vm)
929                } else {
930                    Ok(vm.ctx.not_implemented())
931                }
932            }),
933            ..PyNumberMethods::NOT_IMPLEMENTED
934        };
935        &AS_NUMBER
936    }
937}
938
939impl Representable for PySet {
940    #[inline]
941    fn repr_wtf8(zelf: &crate::Py<Self>, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
942        let class = zelf.class();
943        let borrowed_name = class.name();
944        let class_name = borrowed_name.deref();
945        if zelf.inner.len() == 0 {
946            return Ok(Wtf8Buf::from(format!("{class_name}()")));
947        }
948        if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
949            let name = (class_name != "set").then_some(class_name);
950            zelf.inner.repr(name, vm)
951        } else {
952            Ok(Wtf8Buf::from(format!("{class_name}(...)")))
953        }
954    }
955}
956
957impl Constructor for PyFrozenSet {
958    type Args = Vec<PyObjectRef>;
959
960    fn slot_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
961        let iterable: OptionalArg<PyObjectRef> = args.bind(vm)?;
962
963        // Optimizations for exact frozenset type
964        if cls.is(vm.ctx.types.frozenset_type) {
965            // Return exact frozenset as-is
966            if let OptionalArg::Present(ref input) = iterable
967                && let Ok(fs) = input.clone().downcast_exact::<PyFrozenSet>(vm)
968            {
969                return Ok(fs.into_pyref().into());
970            }
971
972            // Return empty frozenset singleton
973            if iterable.is_missing() {
974                return Ok(vm.ctx.empty_frozenset.clone().into());
975            }
976        }
977
978        let elements: Vec<PyObjectRef> = if let OptionalArg::Present(iterable) = iterable {
979            iterable.try_to_value(vm)?
980        } else {
981            vec![]
982        };
983
984        // Return empty frozenset singleton for exact frozenset types (when iterable was empty)
985        if elements.is_empty() && cls.is(vm.ctx.types.frozenset_type) {
986            return Ok(vm.ctx.empty_frozenset.clone().into());
987        }
988
989        let payload = Self::py_new(&cls, elements, vm)?;
990        payload.into_ref_with_type(vm, cls).map(Into::into)
991    }
992
993    fn py_new(_cls: &Py<PyType>, elements: Self::Args, vm: &VirtualMachine) -> PyResult<Self> {
994        Self::from_iter(vm, elements)
995    }
996}
997
998#[pyclass(
999    flags(BASETYPE, _MATCH_SELF, HAS_WEAKREF),
1000    with(
1001        Constructor,
1002        AsSequence,
1003        Hashable,
1004        Comparable,
1005        Iterable,
1006        AsNumber,
1007        Representable
1008    )
1009)]
1010impl PyFrozenSet {
1011    fn __len__(&self) -> usize {
1012        self.inner.len()
1013    }
1014
1015    fn __contains__(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
1016        self.inner.contains(needle, vm)
1017    }
1018
1019    #[pymethod]
1020    fn __sizeof__(&self) -> usize {
1021        core::mem::size_of::<Self>() + self.inner.sizeof()
1022    }
1023
1024    #[pymethod]
1025    fn copy(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyRef<Self> {
1026        if zelf.class().is(vm.ctx.types.frozenset_type) {
1027            zelf
1028        } else {
1029            Self {
1030                inner: zelf.inner.copy(),
1031                ..Default::default()
1032            }
1033            .into_ref(&vm.ctx)
1034        }
1035    }
1036
1037    #[pymethod]
1038    fn union(&self, others: PosArgs<ArgIterable>, vm: &VirtualMachine) -> PyResult<Self> {
1039        self.fold_op(others.into_iter(), PySetInner::union, vm)
1040    }
1041
1042    #[pymethod]
1043    fn intersection(&self, others: PosArgs<ArgIterable>, vm: &VirtualMachine) -> PyResult<Self> {
1044        self.fold_op(others.into_iter(), PySetInner::intersection, vm)
1045    }
1046
1047    #[pymethod]
1048    fn difference(&self, others: PosArgs<ArgIterable>, vm: &VirtualMachine) -> PyResult<Self> {
1049        self.fold_op(others.into_iter(), PySetInner::difference, vm)
1050    }
1051
1052    #[pymethod]
1053    fn symmetric_difference(
1054        &self,
1055        others: PosArgs<ArgIterable>,
1056        vm: &VirtualMachine,
1057    ) -> PyResult<Self> {
1058        self.fold_op(others.into_iter(), PySetInner::symmetric_difference, vm)
1059    }
1060
1061    #[pymethod]
1062    fn issubset(&self, other: ArgIterable, vm: &VirtualMachine) -> PyResult<bool> {
1063        self.inner.issubset(other, vm)
1064    }
1065
1066    #[pymethod]
1067    fn issuperset(&self, other: ArgIterable, vm: &VirtualMachine) -> PyResult<bool> {
1068        self.inner.issuperset(other, vm)
1069    }
1070
1071    #[pymethod]
1072    fn isdisjoint(&self, other: ArgIterable, vm: &VirtualMachine) -> PyResult<bool> {
1073        self.inner.isdisjoint(other, vm)
1074    }
1075
1076    fn __or__(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyArithmeticValue<Self>> {
1077        if let Ok(set) = AnySet::try_from_object(vm, other) {
1078            Ok(PyArithmeticValue::Implemented(self.op(
1079                set,
1080                PySetInner::union,
1081                vm,
1082            )?))
1083        } else {
1084            Ok(PyArithmeticValue::NotImplemented)
1085        }
1086    }
1087
1088    fn __and__(
1089        &self,
1090        other: PyObjectRef,
1091        vm: &VirtualMachine,
1092    ) -> PyResult<PyArithmeticValue<Self>> {
1093        if let Ok(other) = AnySet::try_from_object(vm, other) {
1094            Ok(PyArithmeticValue::Implemented(self.op(
1095                other,
1096                PySetInner::intersection,
1097                vm,
1098            )?))
1099        } else {
1100            Ok(PyArithmeticValue::NotImplemented)
1101        }
1102    }
1103
1104    fn __sub__(
1105        &self,
1106        other: PyObjectRef,
1107        vm: &VirtualMachine,
1108    ) -> PyResult<PyArithmeticValue<Self>> {
1109        if let Ok(other) = AnySet::try_from_object(vm, other) {
1110            Ok(PyArithmeticValue::Implemented(self.op(
1111                other,
1112                PySetInner::difference,
1113                vm,
1114            )?))
1115        } else {
1116            Ok(PyArithmeticValue::NotImplemented)
1117        }
1118    }
1119
1120    fn __rsub__(
1121        zelf: PyRef<Self>,
1122        other: PyObjectRef,
1123        vm: &VirtualMachine,
1124    ) -> PyResult<PyArithmeticValue<Self>> {
1125        if let Ok(other) = AnySet::try_from_object(vm, other) {
1126            Ok(PyArithmeticValue::Implemented(Self {
1127                inner: other
1128                    .as_inner()
1129                    .difference(ArgIterable::try_from_object(vm, zelf.into())?, vm)?,
1130                ..Default::default()
1131            }))
1132        } else {
1133            Ok(PyArithmeticValue::NotImplemented)
1134        }
1135    }
1136
1137    fn __xor__(
1138        &self,
1139        other: PyObjectRef,
1140        vm: &VirtualMachine,
1141    ) -> PyResult<PyArithmeticValue<Self>> {
1142        if let Ok(other) = AnySet::try_from_object(vm, other) {
1143            Ok(PyArithmeticValue::Implemented(self.op(
1144                other,
1145                PySetInner::symmetric_difference,
1146                vm,
1147            )?))
1148        } else {
1149            Ok(PyArithmeticValue::NotImplemented)
1150        }
1151    }
1152
1153    #[pymethod]
1154    fn __reduce__(
1155        zelf: PyRef<Self>,
1156        vm: &VirtualMachine,
1157    ) -> PyResult<(PyTypeRef, PyTupleRef, Option<PyDictRef>)> {
1158        reduce_set(zelf.as_ref(), vm)
1159    }
1160
1161    #[pyclassmethod]
1162    fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
1163        PyGenericAlias::from_args(cls, args, vm)
1164    }
1165}
1166
1167impl AsSequence for PyFrozenSet {
1168    fn as_sequence() -> &'static PySequenceMethods {
1169        static AS_SEQUENCE: LazyLock<PySequenceMethods> = LazyLock::new(|| PySequenceMethods {
1170            length: atomic_func!(|seq, _vm| Ok(PyFrozenSet::sequence_downcast(seq).__len__())),
1171            contains: atomic_func!(
1172                |seq, needle, vm| PyFrozenSet::sequence_downcast(seq).__contains__(needle, vm)
1173            ),
1174            ..PySequenceMethods::NOT_IMPLEMENTED
1175        });
1176        &AS_SEQUENCE
1177    }
1178}
1179
1180impl Hashable for PyFrozenSet {
1181    #[inline]
1182    fn hash(zelf: &crate::Py<Self>, vm: &VirtualMachine) -> PyResult<PyHash> {
1183        let hash = match zelf.hash.load(Ordering::Relaxed) {
1184            hash::SENTINEL => {
1185                let hash = zelf.inner.hash(vm)?;
1186                match Radium::compare_exchange(
1187                    &zelf.hash,
1188                    hash::SENTINEL,
1189                    hash::fix_sentinel(hash),
1190                    Ordering::Relaxed,
1191                    Ordering::Relaxed,
1192                ) {
1193                    Ok(_) => hash,
1194                    Err(prev_stored) => prev_stored,
1195                }
1196            }
1197            hash => hash,
1198        };
1199        Ok(hash)
1200    }
1201}
1202
1203impl Comparable for PyFrozenSet {
1204    fn cmp(
1205        zelf: &crate::Py<Self>,
1206        other: &PyObject,
1207        op: PyComparisonOp,
1208        vm: &VirtualMachine,
1209    ) -> PyResult<PyComparisonValue> {
1210        extract_set(other).map_or(Ok(PyComparisonValue::NotImplemented), |other| {
1211            Ok(zelf.inner.compare(other, op, vm)?.into())
1212        })
1213    }
1214}
1215
1216impl Iterable for PyFrozenSet {
1217    fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
1218        Ok(zelf.inner.iter().into_pyobject(vm))
1219    }
1220}
1221
1222impl AsNumber for PyFrozenSet {
1223    fn as_number() -> &'static PyNumberMethods {
1224        static AS_NUMBER: PyNumberMethods = PyNumberMethods {
1225            // Binary ops check both operands are sets (like CPython's set_sub, etc.)
1226            // __rsub__ swaps operands. Result type follows first operand's type.
1227            subtract: Some(|a, b, vm| {
1228                if !AnySet::check(a, vm) || !AnySet::check(b, vm) {
1229                    return Ok(vm.ctx.not_implemented());
1230                }
1231                if let Some(a) = a.downcast_ref::<PyFrozenSet>() {
1232                    a.__sub__(b.to_owned(), vm).to_pyresult(vm)
1233                } else if let Some(a) = a.downcast_ref::<PySet>() {
1234                    // When called via __rsub__, a might be PySet - return set (not frozenset)
1235                    a.__sub__(b.to_owned(), vm).to_pyresult(vm)
1236                } else {
1237                    Ok(vm.ctx.not_implemented())
1238                }
1239            }),
1240            and: Some(|a, b, vm| {
1241                if !AnySet::check(a, vm) || !AnySet::check(b, vm) {
1242                    return Ok(vm.ctx.not_implemented());
1243                }
1244                if let Some(a) = a.downcast_ref::<PyFrozenSet>() {
1245                    a.__and__(b.to_owned(), vm).to_pyresult(vm)
1246                } else if let Some(a) = a.downcast_ref::<PySet>() {
1247                    a.__and__(b.to_owned(), vm).to_pyresult(vm)
1248                } else {
1249                    Ok(vm.ctx.not_implemented())
1250                }
1251            }),
1252            xor: Some(|a, b, vm| {
1253                if !AnySet::check(a, vm) || !AnySet::check(b, vm) {
1254                    return Ok(vm.ctx.not_implemented());
1255                }
1256                if let Some(a) = a.downcast_ref::<PyFrozenSet>() {
1257                    a.__xor__(b.to_owned(), vm).to_pyresult(vm)
1258                } else if let Some(a) = a.downcast_ref::<PySet>() {
1259                    a.__xor__(b.to_owned(), vm).to_pyresult(vm)
1260                } else {
1261                    Ok(vm.ctx.not_implemented())
1262                }
1263            }),
1264            or: Some(|a, b, vm| {
1265                if !AnySet::check(a, vm) || !AnySet::check(b, vm) {
1266                    return Ok(vm.ctx.not_implemented());
1267                }
1268                if let Some(a) = a.downcast_ref::<PyFrozenSet>() {
1269                    a.__or__(b.to_owned(), vm).to_pyresult(vm)
1270                } else if let Some(a) = a.downcast_ref::<PySet>() {
1271                    a.__or__(b.to_owned(), vm).to_pyresult(vm)
1272                } else {
1273                    Ok(vm.ctx.not_implemented())
1274                }
1275            }),
1276            ..PyNumberMethods::NOT_IMPLEMENTED
1277        };
1278        &AS_NUMBER
1279    }
1280}
1281
1282impl Representable for PyFrozenSet {
1283    #[inline]
1284    fn repr_wtf8(zelf: &crate::Py<Self>, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
1285        let inner = &zelf.inner;
1286        let class = zelf.class();
1287        let class_name = class.name();
1288        if inner.len() == 0 {
1289            return Ok(Wtf8Buf::from(format!("{class_name}()")));
1290        }
1291        if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
1292            inner.repr(Some(&class_name), vm)
1293        } else {
1294            Ok(Wtf8Buf::from(format!("{class_name}(...)")))
1295        }
1296    }
1297}
1298
1299struct AnySet {
1300    object: PyObjectRef,
1301}
1302
1303impl Borrow<PyObject> for AnySet {
1304    #[inline(always)]
1305    fn borrow(&self) -> &PyObject {
1306        &self.object
1307    }
1308}
1309
1310impl AnySet {
1311    /// Check if object is a set or frozenset (including subclasses)
1312    /// Equivalent to CPython's PyAnySet_Check
1313    fn check(obj: &PyObject, vm: &VirtualMachine) -> bool {
1314        let ctx = &vm.ctx;
1315        obj.fast_isinstance(ctx.types.set_type) || obj.fast_isinstance(ctx.types.frozenset_type)
1316    }
1317
1318    fn into_iterable(self, vm: &VirtualMachine) -> PyResult<ArgIterable> {
1319        self.object.try_into_value(vm)
1320    }
1321
1322    fn into_iterable_iter(
1323        self,
1324        vm: &VirtualMachine,
1325    ) -> PyResult<impl core::iter::Iterator<Item = ArgIterable>> {
1326        Ok(core::iter::once(self.into_iterable(vm)?))
1327    }
1328
1329    fn as_inner(&self) -> &PySetInner {
1330        match_class!(match self.object.as_object() {
1331            ref set @ PySet => &set.inner,
1332            ref frozen @ PyFrozenSet => &frozen.inner,
1333            _ => unreachable!("AnySet is always PySet or PyFrozenSet"), // should not be called.
1334        })
1335    }
1336}
1337
1338impl TryFromObject for AnySet {
1339    fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
1340        let class = obj.class();
1341        if class.fast_issubclass(vm.ctx.types.set_type)
1342            || class.fast_issubclass(vm.ctx.types.frozenset_type)
1343        {
1344            Ok(Self { object: obj })
1345        } else {
1346            Err(vm.new_type_error(format!("{class} is not a subtype of set or frozenset")))
1347        }
1348    }
1349}
1350
1351#[pyclass(module = false, name = "set_iterator")]
1352pub(crate) struct PySetIterator {
1353    size: DictSize,
1354    internal: PyMutex<PositionIterInternal<PyRc<SetContentType>>>,
1355}
1356
1357impl fmt::Debug for PySetIterator {
1358    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1359        // TODO: implement more detailed, non-recursive Debug formatter
1360        f.write_str("set_iterator")
1361    }
1362}
1363
1364impl PyPayload for PySetIterator {
1365    #[inline]
1366    fn class(ctx: &Context) -> &'static Py<PyType> {
1367        ctx.types.set_iterator_type
1368    }
1369}
1370
1371#[pyclass(flags(DISALLOW_INSTANTIATION), with(IterNext, Iterable))]
1372impl PySetIterator {
1373    #[pymethod]
1374    fn __length_hint__(&self) -> usize {
1375        self.internal.lock().length_hint(|_| self.size.entries_size)
1376    }
1377
1378    #[pymethod]
1379    fn __reduce__(
1380        zelf: PyRef<Self>,
1381        vm: &VirtualMachine,
1382    ) -> PyResult<(PyObjectRef, (PyObjectRef,))> {
1383        let internal = zelf.internal.lock();
1384        Ok((
1385            builtins_iter(vm),
1386            (vm.ctx
1387                .new_list(match &internal.status {
1388                    IterStatus::Exhausted => vec![],
1389                    IterStatus::Active(dict) => {
1390                        dict.keys().into_iter().skip(internal.position).collect()
1391                    }
1392                })
1393                .into(),),
1394        ))
1395    }
1396}
1397
1398impl SelfIter for PySetIterator {}
1399impl IterNext for PySetIterator {
1400    fn next(zelf: &crate::Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
1401        let mut internal = zelf.internal.lock();
1402        let next = if let IterStatus::Active(dict) = &internal.status {
1403            if dict.has_changed_size(&zelf.size) {
1404                internal.status = IterStatus::Exhausted;
1405                return Err(vm.new_runtime_error("set changed size during iteration"));
1406            }
1407            match dict.next_entry(internal.position) {
1408                Some((position, key, _)) => {
1409                    internal.position = position;
1410                    PyIterReturn::Return(key)
1411                }
1412                None => {
1413                    internal.status = IterStatus::Exhausted;
1414                    PyIterReturn::StopIteration(None)
1415                }
1416            }
1417        } else {
1418            PyIterReturn::StopIteration(None)
1419        };
1420        Ok(next)
1421    }
1422}
1423
1424fn vectorcall_set(
1425    zelf_obj: &PyObject,
1426    args: Vec<PyObjectRef>,
1427    nargs: usize,
1428    kwnames: Option<&[PyObjectRef]>,
1429    vm: &VirtualMachine,
1430) -> PyResult {
1431    let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap();
1432    let obj = PySet::default().into_ref_with_type(vm, zelf.to_owned())?;
1433    let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
1434    PySet::slot_init(obj.clone().into(), func_args, vm)?;
1435    Ok(obj.into())
1436}
1437
1438fn vectorcall_frozenset(
1439    zelf_obj: &PyObject,
1440    args: Vec<PyObjectRef>,
1441    nargs: usize,
1442    kwnames: Option<&[PyObjectRef]>,
1443    vm: &VirtualMachine,
1444) -> PyResult {
1445    let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap();
1446    let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
1447    (zelf.slots.new.load().unwrap())(zelf.to_owned(), func_args, vm)
1448}
1449
1450pub fn init(context: &'static Context) {
1451    PySet::extend_class(context, context.types.set_type);
1452    context
1453        .types
1454        .set_type
1455        .slots
1456        .vectorcall
1457        .store(Some(vectorcall_set));
1458
1459    PyFrozenSet::extend_class(context, context.types.frozenset_type);
1460    context
1461        .types
1462        .frozenset_type
1463        .slots
1464        .vectorcall
1465        .store(Some(vectorcall_frozenset));
1466
1467    PySetIterator::extend_class(context, context.types.set_iterator_type);
1468}