Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
unify slots
  • Loading branch information
youknowone committed Dec 28, 2025
commit a03d228c250491f1b1a9855fff7e27f502e427e6
1 change: 1 addition & 0 deletions .cspell.dict/cpython.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ prec
preinitialized
pythonw
PYTHREAD_NAME
releasebuffer
SA_ONSTACK
SOABI
stackdepth
Expand Down
13 changes: 7 additions & 6 deletions crates/vm/src/builtins/complex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@ use crate::{
class::PyClassImpl,
common::format::FormatSpec,
convert::{IntoPyException, ToPyObject, ToPyResult},
function::{
FuncArgs, OptionalArg, OptionalOption,
PyArithmeticValue::{self, *},
PyComparisonValue,
},
function::{FuncArgs, OptionalArg, PyComparisonValue},
protocol::PyNumberMethods,
stdlib::warnings,
types::{AsNumber, Comparable, Constructor, Hashable, PyComparisonOp, Representable},
Expand Down Expand Up @@ -489,7 +485,12 @@ impl AsNumber for PyComplex {
}),
absolute: Some(|number, vm| {
let value = PyComplex::number_downcast(number).value;
value.norm().to_pyresult(vm)
let result = value.norm();
// Check for overflow: hypot returns inf for finite inputs that overflow
if result.is_infinite() && value.re.is_finite() && value.im.is_finite() {
return Err(vm.new_overflow_error("absolute value too large".to_owned()));
}
result.to_pyresult(vm)
}),
boolean: Some(|number, _vm| Ok(!PyComplex::number_downcast(number).value.is_zero())),
true_divide: Some(|a, b, vm| PyComplex::number_op(a, b, inner_div, vm)),
Expand Down
22 changes: 18 additions & 4 deletions crates/vm/src/builtins/descriptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -437,10 +437,12 @@ pub enum SlotFunc {
MapAssSubscript(MapAssSubscriptFunc),

// Number sub-slots (nb_*) - grouped by signature
NumBoolean(PyNumberUnaryFunc<bool>), // __bool__
NumUnary(PyNumberUnaryFunc), // __int__, __float__, __index__
NumBinary(PyNumberBinaryFunc), // __add__, __sub__, __mul__, etc.
NumTernary(PyNumberTernaryFunc), // __pow__
NumBoolean(PyNumberUnaryFunc<bool>), // __bool__
NumUnary(PyNumberUnaryFunc), // __int__, __float__, __index__
NumBinary(PyNumberBinaryFunc), // __add__, __sub__, __mul__, etc.
NumBinaryRight(PyNumberBinaryFunc), // __radd__, __rsub__, etc. (swapped args)
NumTernary(PyNumberTernaryFunc), // __pow__
NumTernaryRight(PyNumberTernaryFunc), // __rpow__ (swapped first two args)
}

impl std::fmt::Debug for SlotFunc {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Expand Down Expand Up @@ -476,7 +478,9 @@ impl std::fmt::Debug for SlotFunc {
SlotFunc::NumBoolean(_) => write!(f, "SlotFunc::NumBoolean(...)"),
SlotFunc::NumUnary(_) => write!(f, "SlotFunc::NumUnary(...)"),
SlotFunc::NumBinary(_) => write!(f, "SlotFunc::NumBinary(...)"),
SlotFunc::NumBinaryRight(_) => write!(f, "SlotFunc::NumBinaryRight(...)"),
SlotFunc::NumTernary(_) => write!(f, "SlotFunc::NumTernary(...)"),
SlotFunc::NumTernaryRight(_) => write!(f, "SlotFunc::NumTernaryRight(...)"),
}
}
}
Expand Down Expand Up @@ -637,12 +641,22 @@ impl SlotFunc {
let (other,): (PyObjectRef,) = args.bind(vm)?;
func(&obj, &other, vm)
}
SlotFunc::NumBinaryRight(func) => {
let (other,): (PyObjectRef,) = args.bind(vm)?;
func(&other, &obj, vm) // Swapped: other op obj
}
SlotFunc::NumTernary(func) => {
let (y, z): (PyObjectRef, crate::function::OptionalArg<PyObjectRef>) =
args.bind(vm)?;
let z = z.unwrap_or_else(|| vm.ctx.none());
func(&obj, &y, &z, vm)
}
SlotFunc::NumTernaryRight(func) => {
let (y, z): (PyObjectRef, crate::function::OptionalArg<PyObjectRef>) =
args.bind(vm)?;
let z = z.unwrap_or_else(|| vm.ctx.none());
func(&y, &obj, &z, vm) // Swapped: y ** obj % z
}
}
}
}
Expand Down
3 changes: 1 addition & 2 deletions crates/vm/src/builtins/float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ use crate::{
common::{float_ops, format::FormatSpec, hash},
convert::{IntoPyException, ToPyObject, ToPyResult},
function::{
ArgBytesLike, FuncArgs, OptionalArg, OptionalOption,
PyArithmeticValue::{self, *},
ArgBytesLike, FuncArgs, OptionalArg, OptionalOption, PyArithmeticValue::*,
PyComparisonValue,
},
protocol::PyNumberMethods,
Expand Down
3 changes: 1 addition & 2 deletions crates/vm/src/builtins/int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ use crate::{
},
convert::{IntoPyException, ToPyObject, ToPyResult},
function::{
ArgByteOrder, ArgIntoBool, FuncArgs, OptionalArg, OptionalOption, PyArithmeticValue,
PyComparisonValue,
ArgByteOrder, ArgIntoBool, FuncArgs, OptionalArg, PyArithmeticValue, PyComparisonValue,
},
protocol::{PyNumberMethods, handle_bytes_to_int_err},
types::{AsNumber, Comparable, Constructor, Hashable, PyComparisonOp, Representable},
Expand Down
2 changes: 1 addition & 1 deletion crates/vm/src/builtins/type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ impl PyType {
/// Inherit slots from base type. inherit_slots
pub(crate) fn inherit_slots(&self, base: &Self) {
// Use SLOT_DEFS to iterate all slots
// Note: as_buffer is handled in inherit_static_slots (not AtomicCell)
// Note: as_buffer is handled in inherit_readonly_slots (not AtomicCell)
for def in SLOT_DEFS {
def.accessor.copyslot_if_none(self, base);
}
Expand Down
4 changes: 2 additions & 2 deletions crates/vm/src/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use rustpython_common::static_cell;
/// Iterates SLOT_DEFS and creates a PyWrapper for each slot that:
/// 1. Has a function set in the type's slots
/// 2. Doesn't already have an attribute in the type's dict
fn add_operators(class: &'static Py<PyType>, ctx: &Context) {
pub fn add_operators(class: &'static Py<PyType>, ctx: &Context) {
for def in SLOT_DEFS.iter() {
// Skip __new__ - it has special handling
if def.name == "__new__" {
Expand All @@ -35,7 +35,7 @@ fn add_operators(class: &'static Py<PyType>, ctx: &Context) {
}

// Get the slot function wrapped in SlotFunc
let Some(slot_func) = def.accessor.get_slot_func(&class.slots) else {
let Some(slot_func) = def.accessor.get_slot_func_with_op(&class.slots, def.op) else {
continue;
};

Expand Down
113 changes: 111 additions & 2 deletions crates/vm/src/protocol/number.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ pub struct PyNumberSlots {
pub int: AtomicCell<Option<PyNumberUnaryFunc>>,
pub float: AtomicCell<Option<PyNumberUnaryFunc>>,

// Right variants (internal - not exposed in SlotAccessor)
pub right_add: AtomicCell<Option<PyNumberBinaryFunc>>,
pub right_subtract: AtomicCell<Option<PyNumberBinaryFunc>>,
pub right_multiply: AtomicCell<Option<PyNumberBinaryFunc>>,
Expand Down Expand Up @@ -295,8 +296,7 @@ pub struct PyNumberSlots {

impl From<&PyNumberMethods> for PyNumberSlots {
fn from(value: &PyNumberMethods) -> Self {
// right_* functions will use the same left function as PyNumberMethods
// allows both f(self, other) and f(other, self)
// right_* slots use the same function as left ops for native types
Self {
add: AtomicCell::new(value.add),
subtract: AtomicCell::new(value.subtract),
Expand Down Expand Up @@ -352,6 +352,115 @@ impl From<&PyNumberMethods> for PyNumberSlots {
}

impl PyNumberSlots {
/// Copy from static PyNumberMethods
pub fn copy_from(&self, methods: &PyNumberMethods) {
if let Some(f) = methods.add {
self.add.store(Some(f));
}
if let Some(f) = methods.subtract {
self.subtract.store(Some(f));
}
if let Some(f) = methods.multiply {
self.multiply.store(Some(f));
}
if let Some(f) = methods.remainder {
self.remainder.store(Some(f));
}
if let Some(f) = methods.divmod {
self.divmod.store(Some(f));
}
if let Some(f) = methods.power {
self.power.store(Some(f));
}
if let Some(f) = methods.negative {
self.negative.store(Some(f));
}
if let Some(f) = methods.positive {
self.positive.store(Some(f));
}
if let Some(f) = methods.absolute {
self.absolute.store(Some(f));
}
if let Some(f) = methods.boolean {
self.boolean.store(Some(f));
}
if let Some(f) = methods.invert {
self.invert.store(Some(f));
}
if let Some(f) = methods.lshift {
self.lshift.store(Some(f));
}
if let Some(f) = methods.rshift {
self.rshift.store(Some(f));
}
if let Some(f) = methods.and {
self.and.store(Some(f));
}
if let Some(f) = methods.xor {
self.xor.store(Some(f));
}
if let Some(f) = methods.or {
self.or.store(Some(f));
}
if let Some(f) = methods.int {
self.int.store(Some(f));
}
if let Some(f) = methods.float {
self.float.store(Some(f));
}
if let Some(f) = methods.inplace_add {
self.inplace_add.store(Some(f));
}
if let Some(f) = methods.inplace_subtract {
self.inplace_subtract.store(Some(f));
}
if let Some(f) = methods.inplace_multiply {
self.inplace_multiply.store(Some(f));
}
if let Some(f) = methods.inplace_remainder {
self.inplace_remainder.store(Some(f));
}
if let Some(f) = methods.inplace_power {
self.inplace_power.store(Some(f));
}
if let Some(f) = methods.inplace_lshift {
self.inplace_lshift.store(Some(f));
}
if let Some(f) = methods.inplace_rshift {
self.inplace_rshift.store(Some(f));
}
if let Some(f) = methods.inplace_and {
self.inplace_and.store(Some(f));
}
if let Some(f) = methods.inplace_xor {
self.inplace_xor.store(Some(f));
}
if let Some(f) = methods.inplace_or {
self.inplace_or.store(Some(f));
}
if let Some(f) = methods.floor_divide {
self.floor_divide.store(Some(f));
}
if let Some(f) = methods.true_divide {
self.true_divide.store(Some(f));
}
if let Some(f) = methods.inplace_floor_divide {
self.inplace_floor_divide.store(Some(f));
}
if let Some(f) = methods.inplace_true_divide {
self.inplace_true_divide.store(Some(f));
}
if let Some(f) = methods.index {
self.index.store(Some(f));
}
if let Some(f) = methods.matrix_multiply {
self.matrix_multiply.store(Some(f));
}
if let Some(f) = methods.inplace_matrix_multiply {
self.inplace_matrix_multiply.store(Some(f));
}
}

pub fn left_binary_op(&self, op_slot: PyNumberBinaryOp) -> Option<PyNumberBinaryFunc> {
use PyNumberBinaryOp::*;
match op_slot {
Expand Down
Loading