slot for numeric ops (#6619)

* fix sequence_repeat wrappers

* slot for numeric ops

* fix bpo-37619
This commit is contained in:
Jeong, YunWon authored and GitHub committed 2026-01-01 22:39:16 +09:00
1 parent d3e2fa47cc
commit b19312b403
17 files changed
+60 -90

No files matched your search

-5
View File
@@ -1060,7 +1060,6 @@ mod array {
self.delitem_inner(&needle, vm)
}
#[pymethod]
fn __add__(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
if let Some(other) = other.downcast_ref::<Self>() {
self.read()
@@ -1074,7 +1073,6 @@ mod array {
}
}
#[pymethod]
fn __iadd__(
zelf: PyRef<Self>,
other: PyObjectRef,
@@ -1093,15 +1091,12 @@ mod array {
Ok(zelf)
}
#[pymethod(name = "__rmul__")]
#[pymethod]
fn __mul__(&self, value: isize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
self.read()
.mul(value, vm)
.map(|x| Self::from(x).into_ref(&vm.ctx))
}
#[pymethod]
fn __imul__(zelf: PyRef<Self>, value: isize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
zelf.try_resizable(vm)?.imul(value, vm)?;
Ok(zelf)
+2 -13
View File
@@ -215,7 +215,6 @@ impl PyByteArray {
size_of::<Self>() + self.borrow_buf().len() * size_of::<u8>()
}
#[pymethod]
fn __add__(&self, other: ArgBytesLike) -> Self {
self.inner().add(&other.borrow_buf()).into()
}
@@ -229,7 +228,6 @@ impl PyByteArray {
self.inner().contains(needle, vm)
}
#[pymethod]
fn __iadd__(
zelf: PyRef<Self>,
other: ArgBytesLike,
@@ -524,29 +522,20 @@ impl PyByteArray {
self.inner().title().into()
}
#[pymethod(name = "__rmul__")]
#[pymethod]
fn __mul__(&self, value: ArgSize, vm: &VirtualMachine) -> PyResult<Self> {
self.repeat(value.into(), vm)
}
#[pymethod]
fn __imul__(zelf: PyRef<Self>, value: ArgSize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
Self::irepeat(&zelf, value.into(), vm)?;
Ok(zelf)
}
#[pymethod(name = "__mod__")]
fn mod_(&self, values: PyObjectRef, vm: &VirtualMachine) -> PyResult<Self> {
fn __mod__(&self, values: PyObjectRef, vm: &VirtualMachine) -> PyResult<Self> {
let formatted = self.inner().cformat(values, vm)?;
Ok(formatted.into())
}
#[pymethod]
fn __rmod__(&self, _values: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.not_implemented()
}
#[pymethod]
fn reverse(&self) {
self.borrow_buf_mut().reverse();
@@ -822,7 +811,7 @@ impl AsNumber for PyByteArray {
static AS_NUMBER: PyNumberMethods = PyNumberMethods {
remainder: Some(|a, b, vm| {
if let Some(a) = a.downcast_ref::<PyByteArray>() {
a.mod_(b.to_owned(), vm).to_pyresult(vm)
a.__mod__(b.to_owned(), vm).to_pyresult(vm)
} else {
Ok(vm.ctx.not_implemented())
}
+2 -11
View File
@@ -224,7 +224,6 @@ impl PyBytes {
size_of::<Self>() + self.len() * size_of::<u8>()
}
#[pymethod]
fn __add__(&self, other: ArgBytesLike) -> Vec<u8> {
self.inner.add(&other.borrow_buf())
}
@@ -512,23 +511,15 @@ impl PyBytes {
self.inner.title().into()
}
#[pymethod(name = "__rmul__")]
#[pymethod]
fn __mul__(zelf: PyRef<Self>, value: ArgIndex, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
zelf.repeat(value.into_int_ref().try_to_primitive(vm)?, vm)
}
#[pymethod(name = "__mod__")]
fn mod_(&self, values: PyObjectRef, vm: &VirtualMachine) -> PyResult<Self> {
fn __mod__(&self, values: PyObjectRef, vm: &VirtualMachine) -> PyResult<Self> {
let formatted = self.inner.cformat(values, vm)?;
Ok(formatted.into())
}
#[pymethod]
fn __rmod__(&self, _values: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.not_implemented()
}
#[pymethod]
fn __getnewargs__(&self, vm: &VirtualMachine) -> PyTupleRef {
let param: Vec<PyObjectRef> = self.elements().map(|x| x.to_pyobject(vm)).collect();
@@ -677,7 +668,7 @@ impl AsNumber for PyBytes {
static AS_NUMBER: PyNumberMethods = PyNumberMethods {
remainder: Some(|a, b, vm| {
if let Some(a) = a.downcast_ref::<PyBytes>() {
a.mod_(b.to_owned(), vm).to_pyresult(vm)
a.__mod__(b.to_owned(), vm).to_pyresult(vm)
} else {
Ok(vm.ctx.not_implemented())
}
+12 -3
View File
@@ -5,7 +5,7 @@ use crate::{
class::PyClassImpl,
common::hash::PyHash,
convert::{ToPyObject, ToPyResult},
function::{FuncArgs, PyMethodDef, PyMethodFlags, PySetterValue},
function::{ArgSize, FuncArgs, PyMethodDef, PyMethodFlags, PySetterValue},
protocol::{PyNumberBinaryFunc, PyNumberTernaryFunc, PyNumberUnaryFunc},
types::{
Callable, Comparable, DelFunc, DescrGetFunc, DescrSetFunc, GenericMethod, GetDescriptor,
@@ -593,8 +593,8 @@ impl SlotFunc {
func(obj.sequence_unchecked(), &other, vm)
}
SlotFunc::SeqRepeat(func) => {
let (n,): (isize,) = args.bind(vm)?;
func(obj.sequence_unchecked(), n, vm)
let (n,): (ArgSize,) = args.bind(vm)?;
func(obj.sequence_unchecked(), n.into(), vm)
}
SlotFunc::SeqItem(func) => {
let (index,): (isize,) = args.bind(vm)?;
@@ -774,6 +774,15 @@ impl Callable for PyMethodWrapper {
type Args = FuncArgs;
fn call(zelf: &Py<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
// bpo-37619: Check type compatibility before calling wrapped slot
if !zelf.obj.fast_isinstance(zelf.wrapper.typ) {
return Err(vm.new_type_error(format!(
"descriptor '{}' requires a '{}' object but received a '{}'",
zelf.wrapper.name.as_str(),
zelf.wrapper.typ.name(),
zelf.obj.class().name()
)));
}
zelf.wrapper.wrapped.call(zelf.obj.clone(), args, vm)
}
}
-11
View File
@@ -293,7 +293,6 @@ impl PyDict {
Ok(())
}
#[pymethod]
fn __or__(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
let other_dict: Result<PyDictRef, _> = other.downcast();
if let Ok(other) = other_dict {
@@ -403,13 +402,11 @@ impl PyRef<PyDict> {
PyDictReverseKeyIterator::new(self)
}
#[pymethod]
fn __ior__(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult<Self> {
self.merge_object(other, vm)?;
Ok(self)
}
#[pymethod]
fn __ror__(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
let other_dict: Result<Self, _> = other.downcast();
if let Ok(other) = other_dict {
@@ -1046,38 +1043,30 @@ trait ViewSetOps: DictView {
PySetInner::from_iter(iter, vm)
}
#[pymethod(name = "__rxor__")]
#[pymethod]
fn __xor__(zelf: PyRef<Self>, other: ArgIterable, vm: &VirtualMachine) -> PyResult<PySet> {
let zelf = Self::to_set(zelf, vm)?;
let inner = zelf.symmetric_difference(other, vm)?;
Ok(PySet { inner })
}
#[pymethod(name = "__rand__")]
#[pymethod]
fn __and__(zelf: PyRef<Self>, other: ArgIterable, vm: &VirtualMachine) -> PyResult<PySet> {
let zelf = Self::to_set(zelf, vm)?;
let inner = zelf.intersection(other, vm)?;
Ok(PySet { inner })
}
#[pymethod(name = "__ror__")]
#[pymethod]
fn __or__(zelf: PyRef<Self>, other: ArgIterable, vm: &VirtualMachine) -> PyResult<PySet> {
let zelf = Self::to_set(zelf, vm)?;
let inner = zelf.union(other, vm)?;
Ok(PySet { inner })
}
#[pymethod]
fn __sub__(zelf: PyRef<Self>, other: ArgIterable, vm: &VirtualMachine) -> PyResult<PySet> {
let zelf = Self::to_set(zelf, vm)?;
let inner = zelf.difference(other, vm)?;
Ok(PySet { inner })
}
#[pymethod]
fn __rsub__(zelf: PyRef<Self>, other: ArgIterable, vm: &VirtualMachine) -> PyResult<PySet> {
let left = PySetInner::from_iter(other.iter(vm)?, vm)?;
let right = ArgIterable::try_from_object(vm, Self::iter(zelf, vm)?)?;
-2
View File
@@ -236,12 +236,10 @@ impl PyGenericAlias {
Err(vm.new_type_error("issubclass() argument 2 cannot be a parameterized generic"))
}
#[pymethod]
fn __ror__(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
type_::or_(other, zelf, vm)
}
#[pymethod]
fn __or__(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
type_::or_(zelf, other, vm)
}
-5
View File
@@ -145,7 +145,6 @@ impl PyList {
Ok(Self::from(elements).into_ref(&vm.ctx))
}
#[pymethod]
fn __add__(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
self.concat(&other, vm)
}
@@ -160,7 +159,6 @@ impl PyList {
Ok(zelf.to_owned().into())
}
#[pymethod]
fn __iadd__(
zelf: PyRef<Self>,
other: PyObjectRef,
@@ -240,13 +238,10 @@ impl PyList {
self._setitem(&needle, value, vm)
}
#[pymethod]
#[pymethod(name = "__rmul__")]
fn __mul__(&self, n: ArgSize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
self.repeat(n.into(), vm)
}
#[pymethod]
fn __imul__(zelf: PyRef<Self>, n: ArgSize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
Self::irepeat(zelf, n.into(), vm)
}
-3
View File
@@ -191,7 +191,6 @@ impl PyMappingProxy {
)
}
#[pymethod]
fn __ior__(&self, _args: PyObjectRef, vm: &VirtualMachine) -> PyResult {
Err(vm.new_type_error(format!(
r#""'|=' is not supported by {}; use '|' instead""#,
@@ -199,8 +198,6 @@ impl PyMappingProxy {
)))
}
#[pymethod(name = "__ror__")]
#[pymethod]
fn __or__(&self, args: PyObjectRef, vm: &VirtualMachine) -> PyResult {
vm._or(self.copy(vm)?.as_ref(), args.as_ref())
}
+2 -11
View File
@@ -541,7 +541,6 @@ impl Py<PyStr> {
)
)]
impl PyStr {
#[pymethod]
fn __add__(zelf: PyRef<Self>, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if let Some(other) = other.downcast_ref::<Self>() {
let bytes = zelf.as_wtf8().py_add(other.as_wtf8());
@@ -636,8 +635,6 @@ impl PyStr {
core::mem::size_of::<Self>() + self.byte_len() * core::mem::size_of::<u8>()
}
#[pymethod(name = "__rmul__")]
#[pymethod]
fn __mul__(zelf: PyRef<Self>, value: ArgSize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
Self::repeat(zelf, value.into(), vm)
}
@@ -947,16 +944,10 @@ impl PyStr {
&& self.char_all(|c| GeneralCategory::of(c) == GeneralCategory::DecimalNumber)
}
#[pymethod(name = "__mod__")]
fn modulo(&self, values: PyObjectRef, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
fn __mod__(&self, values: PyObjectRef, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
cformat_string(vm, self.as_wtf8(), values)
}
#[pymethod]
fn __rmod__(&self, _values: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.not_implemented()
}
#[pymethod]
fn format(&self, args: FuncArgs, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
let format_str =
@@ -1564,7 +1555,7 @@ impl AsNumber for PyStr {
static AS_NUMBER: PyNumberMethods = PyNumberMethods {
remainder: Some(|a, b, vm| {
if let Some(a) = a.downcast_ref::<PyStr>() {
a.modulo(b.to_owned(), vm).to_pyresult(vm)
a.__mod__(b.to_owned(), vm).to_pyresult(vm)
} else {
Ok(vm.ctx.not_implemented())
}
-3
View File
@@ -263,7 +263,6 @@ impl<T> PyTuple<PyRef<T>> {
with(AsMapping, AsNumber, AsSequence, Hashable, Comparable, Iterable, Constructor, Representable)
)]
impl PyTuple {
#[pymethod]
fn __add__(
zelf: PyRef<Self>,
other: PyObjectRef,
@@ -302,8 +301,6 @@ impl PyTuple {
self.elements.len()
}
#[pymethod(name = "__rmul__")]
#[pymethod]
fn __mul__(zelf: PyRef<Self>, value: ArgSize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
Self::repeat(zelf, value.into(), vm)
}
-2
View File
@@ -951,12 +951,10 @@ impl PyType {
)
}
#[pymethod]
pub fn __ror__(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
or_(other, zelf, vm)
}
#[pymethod]
pub fn __or__(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
or_(zelf, other, vm)
}
-2
View File
@@ -136,8 +136,6 @@ impl PyUnion {
}
}
#[pymethod(name = "__ror__")]
#[pymethod]
fn __or__(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
type_::or_(zelf, other, vm)
}
-4
View File
@@ -332,8 +332,6 @@ mod _collections {
Ok(deque)
}
#[pymethod]
#[pymethod(name = "__rmul__")]
fn __mul__(&self, n: isize, vm: &VirtualMachine) -> PyResult<Self> {
let deque = self._mul(n, vm)?;
Ok(Self {
@@ -343,7 +341,6 @@ mod _collections {
})
}
#[pymethod]
fn __imul__(zelf: PyRef<Self>, n: isize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
let mul_deque = zelf._mul(n, vm)?;
*zelf.borrow_deque_mut() = mul_deque;
@@ -379,7 +376,6 @@ mod _collections {
}
}
#[pymethod]
fn __iadd__(
zelf: PyRef<Self>,
other: PyObjectRef,
-1
View File
@@ -137,7 +137,6 @@ impl PyCPointerType {
)))
}
#[pymethod]
fn __mul__(cls: PyTypeRef, n: isize, vm: &VirtualMachine) -> PyResult {
use super::array::array_type_from_ctype;
-1
View File
@@ -488,7 +488,6 @@ impl PyCSimpleType {
}
}
#[pymethod]
fn __mul__(cls: PyTypeRef, n: isize, vm: &VirtualMachine) -> PyResult {
PyCSimple::repeat(cls, n, vm)
}
-1
View File
@@ -393,7 +393,6 @@ impl PyCStructType {
Ok(())
}
#[pymethod]
fn __mul__(cls: PyTypeRef, n: isize, vm: &VirtualMachine) -> PyResult {
use super::array::array_type_from_ctype;
+42 -12
View File
@@ -438,6 +438,16 @@ fn sequence_contains_wrapper(
contains_wrapper(seq.obj, needle, vm)
}
#[inline(never)]
fn sequence_repeat_wrapper(seq: PySequence<'_>, n: isize, vm: &VirtualMachine) -> PyResult {
vm.call_special_method(seq.obj, identifier!(vm, __mul__), (n,))
}
#[inline(never)]
fn sequence_inplace_repeat_wrapper(seq: PySequence<'_>, n: isize, vm: &VirtualMachine) -> PyResult {
vm.call_special_method(seq.obj, identifier!(vm, __imul__), (n,))
}
fn repr_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyRef<PyStr>> {
let ret = vm.call_special_method(zelf, identifier!(vm, __repr__), ())?;
ret.downcast::<PyStr>().map_err(|obj| {
@@ -1294,12 +1304,16 @@ impl PyType {
accessor.inherit_from_mro(self);
}
}
SlotAccessor::SqRepeat | SlotAccessor::SqInplaceRepeat => {
// Sequence repeat uses sq_repeat slot - no generic wrapper needed
// (handled by number protocol fallback)
if !ADD {
accessor.inherit_from_mro(self);
}
SlotAccessor::SqRepeat => {
update_sub_slot!(as_sequence, repeat, sequence_repeat_wrapper, SeqRepeat)
}
SlotAccessor::SqInplaceRepeat => {
update_sub_slot!(
as_sequence,
inplace_repeat,
sequence_inplace_repeat_wrapper,
SeqRepeat
)
}
SlotAccessor::SqItem => {
update_sub_slot!(as_sequence, item, sequence_getitem_wrapper, SeqItem)
@@ -1348,28 +1362,44 @@ impl PyType {
) -> Option<T> {
use crate::builtins::descriptor::PyWrapper;
// Helper to check if a class is a subclass of another by checking MRO
let is_subclass_of = |subclass_mro: &[PyRef<PyType>], superclass: &Py<PyType>| -> bool {
subclass_mro.iter().any(|c| c.is(superclass))
};
// Helper to extract slot from an attribute if it's a wrapper descriptor
let try_extract = |attr: &PyObjectRef| -> Option<T> {
// and the wrapper's type is compatible with the given class.
// bpo-37619: wrapper descriptor from wrong class should not be used directly.
let try_extract = |attr: &PyObjectRef, for_class_mro: &[PyRef<PyType>]| -> Option<T> {
if attr.class().is(ctx.types.wrapper_descriptor_type) {
attr.downcast_ref::<PyWrapper>()
.and_then(|wrapper| extract(&wrapper.wrapped))
attr.downcast_ref::<PyWrapper>().and_then(|wrapper| {
// Only extract slot if for_class is a subclass of wrapper.typ
if is_subclass_of(for_class_mro, wrapper.typ) {
extract(&wrapper.wrapped)
} else {
None
}
})
} else {
None
}
};
let mro = self.mro.read();
// Look up in self's dict first
if let Some(attr) = self.attributes.read().get(name).cloned() {
if let Some(func) = try_extract(&attr) {
if let Some(func) = try_extract(&attr, &mro) {
return Some(func);
}
return None;
}
// Look up in MRO (mro[0] is self, so skip it)
for cls in self.mro.read()[1..].iter() {
for (i, cls) in mro[1..].iter().enumerate() {
if let Some(attr) = cls.attributes.read().get(name).cloned() {
if let Some(func) = try_extract(&attr) {
// Use the slice starting from this class in MRO
if let Some(func) = try_extract(&attr, &mro[i + 1..]) {
return Some(func);
}
return None;