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
mapping priors sequence
  • Loading branch information
youknowone committed Jan 1, 2026
commit b408ababb31d50a90e2bbc20bb65ce5459039dfa
58 changes: 27 additions & 31 deletions crates/vm/src/builtins/descriptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,13 +428,15 @@ pub enum SlotFunc {
SeqConcat(SeqConcatFunc),
SeqRepeat(SeqRepeatFunc),
SeqItem(SeqItemFunc),
SeqAssItem(SeqAssItemFunc),
SeqSetItem(SeqAssItemFunc), // __setitem__ (same func type, value = Some)
SeqDelItem(SeqAssItemFunc), // __delitem__ (same func type, value = None)
SeqContains(SeqContainsFunc),

// Mapping sub-slots (mp_*)
MapLength(MapLenFunc),
MapSubscript(MapSubscriptFunc),
MapAssSubscript(MapAssSubscriptFunc),
MapSetSubscript(MapAssSubscriptFunc), // __setitem__ (same func type, value = Some)
MapDelSubscript(MapAssSubscriptFunc), // __delitem__ (same func type, value = None)

// Number sub-slots (nb_*) - grouped by signature
NumBoolean(PyNumberUnaryFunc<bool>), // __bool__
Expand Down Expand Up @@ -468,12 +470,14 @@ impl core::fmt::Debug for SlotFunc {
SlotFunc::SeqConcat(_) => write!(f, "SlotFunc::SeqConcat(...)"),
SlotFunc::SeqRepeat(_) => write!(f, "SlotFunc::SeqRepeat(...)"),
SlotFunc::SeqItem(_) => write!(f, "SlotFunc::SeqItem(...)"),
SlotFunc::SeqAssItem(_) => write!(f, "SlotFunc::SeqAssItem(...)"),
SlotFunc::SeqSetItem(_) => write!(f, "SlotFunc::SeqSetItem(...)"),
SlotFunc::SeqDelItem(_) => write!(f, "SlotFunc::SeqDelItem(...)"),
SlotFunc::SeqContains(_) => write!(f, "SlotFunc::SeqContains(...)"),
// Mapping sub-slots
SlotFunc::MapLength(_) => write!(f, "SlotFunc::MapLength(...)"),
SlotFunc::MapSubscript(_) => write!(f, "SlotFunc::MapSubscript(...)"),
SlotFunc::MapAssSubscript(_) => write!(f, "SlotFunc::MapAssSubscript(...)"),
SlotFunc::MapSetSubscript(_) => write!(f, "SlotFunc::MapSetSubscript(...)"),
SlotFunc::MapDelSubscript(_) => write!(f, "SlotFunc::MapDelSubscript(...)"),
// Number sub-slots
SlotFunc::NumBoolean(_) => write!(f, "SlotFunc::NumBoolean(...)"),
SlotFunc::NumUnary(_) => write!(f, "SlotFunc::NumUnary(...)"),
Expand All @@ -487,13 +491,7 @@ impl core::fmt::Debug for SlotFunc {

impl SlotFunc {
/// Call the wrapped slot function with proper type handling
pub fn call(
&self,
obj: PyObjectRef,
args: FuncArgs,
name: &str,
vm: &VirtualMachine,
) -> PyResult {
pub fn call(&self, obj: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
match self {
SlotFunc::Init(func) => {
func(obj, args, vm)?;
Expand Down Expand Up @@ -606,14 +604,14 @@ impl SlotFunc {
let (index,): (isize,) = args.bind(vm)?;
func(obj.sequence_unchecked(), index, vm)
}
SlotFunc::SeqAssItem(func) => {
if name == "__delitem__" {
let (index,): (isize,) = args.bind(vm)?;
func(obj.sequence_unchecked(), index, None, vm)?;
} else {
let (index, value): (isize, PyObjectRef) = args.bind(vm)?;
func(obj.sequence_unchecked(), index, Some(value), vm)?;
}
SlotFunc::SeqSetItem(func) => {
let (index, value): (isize, PyObjectRef) = args.bind(vm)?;
func(obj.sequence_unchecked(), index, Some(value), vm)?;
Ok(vm.ctx.none())
}
SlotFunc::SeqDelItem(func) => {
let (index,): (isize,) = args.bind(vm)?;
func(obj.sequence_unchecked(), index, None, vm)?;
Ok(vm.ctx.none())
}
SlotFunc::SeqContains(func) => {
Expand All @@ -631,14 +629,14 @@ impl SlotFunc {
let (key,): (PyObjectRef,) = args.bind(vm)?;
func(obj.mapping_unchecked(), &key, vm)
}
SlotFunc::MapAssSubscript(func) => {
if name == "__delitem__" {
let (key,): (PyObjectRef,) = args.bind(vm)?;
func(obj.mapping_unchecked(), &key, None, vm)?;
} else {
let (key, value): (PyObjectRef, PyObjectRef) = args.bind(vm)?;
func(obj.mapping_unchecked(), &key, Some(value), vm)?;
}
SlotFunc::MapSetSubscript(func) => {
let (key, value): (PyObjectRef, PyObjectRef) = args.bind(vm)?;
func(obj.mapping_unchecked(), &key, Some(value), vm)?;
Ok(vm.ctx.none())
}
SlotFunc::MapDelSubscript(func) => {
let (key,): (PyObjectRef,) = args.bind(vm)?;
func(obj.mapping_unchecked(), &key, None, vm)?;
Ok(vm.ctx.none())
}
// Number sub-slots
Expand Down Expand Up @@ -725,7 +723,7 @@ impl Callable for PyWrapper {
)));
}

zelf.wrapped.call(obj, rest, zelf.name.as_str(), vm)
zelf.wrapped.call(obj, rest, vm)
}
}

Expand Down Expand Up @@ -797,9 +795,7 @@ impl Callable for PyMethodWrapper {
zelf.obj.class().name()
)));
}
zelf.wrapper
.wrapped
.call(zelf.obj.clone(), args, zelf.wrapper.name.as_str(), vm)
zelf.wrapper.wrapped.call(zelf.obj.clone(), args, vm)
}
}

Expand Down
12 changes: 0 additions & 12 deletions crates/vm/src/builtins/getset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,18 +118,6 @@ impl PyGetSet {
)))
}
}
fn __set__(
zelf: PyObjectRef,
obj: PyObjectRef,
value: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<()> {
Self::descr_set(&zelf, obj, PySetterValue::Assign(value), vm)
}

fn __delete__(zelf: PyObjectRef, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
Self::descr_set(&zelf, obj, PySetterValue::Delete, vm)
}

#[pygetset]
fn __name__(&self) -> String {
Expand Down
12 changes: 0 additions & 12 deletions crates/vm/src/builtins/property.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,18 +125,6 @@ impl PyProperty {
}
}
}
fn __set__(
zelf: PyObjectRef,
obj: PyObjectRef,
value: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<()> {
Self::descr_set(&zelf, obj, PySetterValue::Assign(value), vm)
}

fn __delete__(zelf: PyObjectRef, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
Self::descr_set(&zelf, obj, PySetterValue::Delete, vm)
}

// Access functions

Expand Down
15 changes: 0 additions & 15 deletions crates/vm/src/stdlib/ctypes/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1650,21 +1650,6 @@ impl PyCField {
}
}

#[pymethod]
fn __set__(
zelf: PyObjectRef,
obj: PyObjectRef,
value: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<()> {
Self::descr_set(&zelf, obj, PySetterValue::Assign(value), vm)
}

#[pymethod]
fn __delete__(zelf: PyObjectRef, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
Self::descr_set(&zelf, obj, PySetterValue::Delete, vm)
}

#[pygetset]
fn offset(&self) -> isize {
self.offset
Expand Down
61 changes: 54 additions & 7 deletions crates/vm/src/types/slot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1319,7 +1319,33 @@ impl PyType {
update_sub_slot!(as_sequence, item, sequence_getitem_wrapper, SeqItem)
}
SlotAccessor::SqAssItem => {
update_sub_slot!(as_sequence, ass_item, sequence_setitem_wrapper, SeqAssItem)
// SqAssItem is shared by __setitem__ (SeqSetItem) and __delitem__ (SeqDelItem)
if ADD {
let has_own = {
let guard = self.attributes.read();
let setitem = ctx.intern_str("__setitem__");
let delitem = ctx.intern_str("__delitem__");
guard.contains_key(setitem) || guard.contains_key(delitem)
};
if has_own {
self.slots
.as_sequence
.ass_item
.store(Some(sequence_setitem_wrapper));
} else if let Some(func) = self.lookup_slot_in_mro(name, ctx, |sf| match sf {
SlotFunc::SeqSetItem(f) | SlotFunc::SeqDelItem(f) => Some(*f),
_ => None,
}) {
self.slots.as_sequence.ass_item.store(Some(func));
} else {
self.slots
.as_sequence
.ass_item
.store(Some(sequence_setitem_wrapper));
}
} else {
accessor.inherit_from_mro(self);
}
}
SlotAccessor::SqContains => {
update_sub_slot!(
Expand All @@ -1338,12 +1364,33 @@ impl PyType {
update_sub_slot!(as_mapping, subscript, mapping_getitem_wrapper, MapSubscript)
}
SlotAccessor::MpAssSubscript => {
update_sub_slot!(
as_mapping,
ass_subscript,
mapping_setitem_wrapper,
MapAssSubscript
)
// MpAssSubscript is shared by __setitem__ (MapSetSubscript) and __delitem__ (MapDelSubscript)
if ADD {
let has_own = {
let guard = self.attributes.read();
let setitem = ctx.intern_str("__setitem__");
let delitem = ctx.intern_str("__delitem__");
guard.contains_key(setitem) || guard.contains_key(delitem)
};
if has_own {
self.slots
.as_mapping
.ass_subscript
.store(Some(mapping_setitem_wrapper));
} else if let Some(func) = self.lookup_slot_in_mro(name, ctx, |sf| match sf {
SlotFunc::MapSetSubscript(f) | SlotFunc::MapDelSubscript(f) => Some(*f),
_ => None,
}) {
self.slots.as_mapping.ass_subscript.store(Some(func));
} else {
self.slots
.as_mapping
.ass_subscript
.store(Some(mapping_setitem_wrapper));
}
} else {
accessor.inherit_from_mro(self);
}
}

// Reserved slots - no-op
Expand Down
63 changes: 41 additions & 22 deletions crates/vm/src/types/slot_defs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -389,13 +389,20 @@ impl SlotAccessor {
Self::SqConcat | Self::SqInplaceConcat => matches!(slot_func, SlotFunc::SeqConcat(_)),
Self::SqRepeat | Self::SqInplaceRepeat => matches!(slot_func, SlotFunc::SeqRepeat(_)),
Self::SqItem => matches!(slot_func, SlotFunc::SeqItem(_)),
Self::SqAssItem => matches!(slot_func, SlotFunc::SeqAssItem(_)),
Self::SqAssItem => {
matches!(slot_func, SlotFunc::SeqSetItem(_) | SlotFunc::SeqDelItem(_))
}
Self::SqContains => matches!(slot_func, SlotFunc::SeqContains(_)),

// Mapping
Self::MpLength => matches!(slot_func, SlotFunc::MapLength(_)),
Self::MpSubscript => matches!(slot_func, SlotFunc::MapSubscript(_)),
Self::MpAssSubscript => matches!(slot_func, SlotFunc::MapAssSubscript(_)),
Self::MpAssSubscript => {
matches!(
slot_func,
SlotFunc::MapSetSubscript(_) | SlotFunc::MapDelSubscript(_)
)
}

// New and reserved slots
Self::TpNew => false,
Expand Down Expand Up @@ -751,7 +758,7 @@ impl SlotAccessor {
Self::SqConcat => slots.as_sequence.concat.load().map(SlotFunc::SeqConcat),
Self::SqRepeat => slots.as_sequence.repeat.load().map(SlotFunc::SeqRepeat),
Self::SqItem => slots.as_sequence.item.load().map(SlotFunc::SeqItem),
Self::SqAssItem => slots.as_sequence.ass_item.load().map(SlotFunc::SeqAssItem),
Self::SqAssItem => slots.as_sequence.ass_item.load().map(SlotFunc::SeqSetItem),
Self::SqContains => slots.as_sequence.contains.load().map(SlotFunc::SeqContains),
Self::SqInplaceConcat => slots
.as_sequence
Expand All @@ -775,7 +782,7 @@ impl SlotAccessor {
.as_mapping
.ass_subscript
.load()
.map(SlotFunc::MapAssSubscript),
.map(SlotFunc::MapSetSubscript),

// Reserved slots
_ => None,
Expand All @@ -793,6 +800,16 @@ impl SlotAccessor {
match self {
Self::TpSetattro => return slots.setattro.load().map(SlotFunc::DelAttro),
Self::TpDescrSet => return slots.descr_set.load().map(SlotFunc::DescrDel),
Self::SqAssItem => {
return slots.as_sequence.ass_item.load().map(SlotFunc::SeqDelItem);
}
Self::MpAssSubscript => {
return slots
.as_mapping
.ass_subscript
.load()
.map(SlotFunc::MapDelSubscript);
}
_ => {}
}
}
Expand Down Expand Up @@ -1060,62 +1077,64 @@ pub static SLOT_DEFS: &[SlotDef] = &[
op: Some(SlotOp::Delete),
doc: "Delete an attribute of instance.",
},
// Sequence protocol (sq_*)
// Mapping protocol (mp_*) - must come before Sequence protocol
// so that mp_subscript wins over sq_item for __getitem__
// (see CPython typeobject.c:10995-11006)
SlotDef {
name: "__len__",
accessor: SlotAccessor::SqLength,
accessor: SlotAccessor::MpLength,
op: None,
doc: "Return len(self).",
},
SlotDef {
name: "__getitem__",
accessor: SlotAccessor::SqItem,
accessor: SlotAccessor::MpSubscript,
op: None,
doc: "Return self[key].",
},
SlotDef {
name: "__setitem__",
accessor: SlotAccessor::SqAssItem,
accessor: SlotAccessor::MpAssSubscript,
op: None,
doc: "Set self[key] to value.",
},
SlotDef {
name: "__delitem__",
accessor: SlotAccessor::SqAssItem,
op: None,
accessor: SlotAccessor::MpAssSubscript,
op: Some(SlotOp::Delete),
doc: "Delete self[key].",
},
SlotDef {
name: "__contains__",
accessor: SlotAccessor::SqContains,
op: None,
doc: "Return key in self.",
},
// Mapping protocol (mp_*)
// Sequence protocol (sq_*)
SlotDef {
name: "__len__",
accessor: SlotAccessor::MpLength,
accessor: SlotAccessor::SqLength,
op: None,
doc: "Return len(self).",
},
SlotDef {
name: "__getitem__",
accessor: SlotAccessor::MpSubscript,
accessor: SlotAccessor::SqItem,
op: None,
doc: "Return self[key].",
},
SlotDef {
name: "__setitem__",
accessor: SlotAccessor::MpAssSubscript,
accessor: SlotAccessor::SqAssItem,
op: None,
doc: "Set self[key] to value.",
},
SlotDef {
name: "__delitem__",
accessor: SlotAccessor::MpAssSubscript,
op: None,
accessor: SlotAccessor::SqAssItem,
op: Some(SlotOp::Delete),
doc: "Delete self[key].",
},
SlotDef {
name: "__contains__",
accessor: SlotAccessor::SqContains,
op: None,
doc: "Return key in self.",
},
// Number protocol - binary ops with left/right variants
SlotDef {
name: "__add__",
Expand Down