Skip to content

Commit 3ab63ef

Browse files
committed
another fix
1 parent dd23a4d commit 3ab63ef

File tree

1 file changed

+57
-35
lines changed

1 file changed

+57
-35
lines changed

vm/src/builtins/type.rs

Lines changed: 57 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1446,63 +1446,85 @@ impl Representable for PyType {
14461446
}
14471447
}
14481448

1449-
fn find_base_dict_descr(cls: &Py<PyType>, vm: &VirtualMachine) -> Option<PyObjectRef> {
1450-
let dict_attr = identifier!(vm, __dict__);
1451-
1452-
// First check if the class itself has a user-defined __dict__ property
1453-
if let Some(descr) = cls.attributes.read().get(&dict_attr) {
1454-
// If it's a property (user-defined), return it
1455-
if descr.class().is(vm.ctx.types.property_type) {
1456-
return Some(descr.clone());
1449+
// Equivalent to CPython's get_builtin_base_with_dict
1450+
fn get_builtin_base_with_dict(typ: &Py<PyType>, vm: &VirtualMachine) -> Option<PyTypeRef> {
1451+
let mut current = Some(typ.to_owned());
1452+
while let Some(t) = current {
1453+
// In CPython: type->tp_dictoffset != 0 && !(type->tp_flags & Py_TPFLAGS_HEAPTYPE)
1454+
// Special case: type itself is a builtin with dict support
1455+
if t.is(vm.ctx.types.type_type) {
1456+
return Some(t);
1457+
}
1458+
// We check HAS_DICT flag (equivalent to tp_dictoffset != 0) and HEAPTYPE
1459+
if t.slots.flags.contains(PyTypeFlags::HAS_DICT)
1460+
&& !t.slots.flags.contains(PyTypeFlags::HEAPTYPE)
1461+
{
1462+
return Some(t);
14571463
}
1458-
// Skip getset descriptors in the current class to avoid recursion
1464+
current = t.__base__();
14591465
}
1466+
None
1467+
}
14601468

1461-
// Then check bases (like original implementation)
1462-
cls.iter_base_chain().skip(1).find_map(|cls| {
1463-
// TODO: should actually be some translation of:
1464-
// cls.slot_dictoffset != 0 && !cls.flags.contains(HEAPTYPE)
1465-
if cls.is(vm.ctx.types.type_type) {
1466-
cls.get_attr(dict_attr)
1467-
} else {
1468-
None
1469-
}
1470-
})
1469+
// Equivalent to CPython's get_dict_descriptor
1470+
fn get_dict_descriptor(base: &Py<PyType>, vm: &VirtualMachine) -> Option<PyObjectRef> {
1471+
let dict_attr = identifier!(vm, __dict__);
1472+
// Use _PyType_Lookup (which is lookup_ref in RustPython)
1473+
base.lookup_ref(&dict_attr, vm)
14711474
}
14721475

14731476
fn subtype_get_dict(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
1474-
// TODO: obj.class().as_pyref() need to be supported
1475-
let ret = match find_base_dict_descr(obj.class(), vm) {
1476-
Some(descr) => vm.call_get_descriptor(&descr, obj).unwrap_or_else(|| {
1477+
let base = get_builtin_base_with_dict(obj.class(), vm);
1478+
1479+
if let Some(base_type) = base {
1480+
if let Some(descr) = get_dict_descriptor(&base_type, vm) {
1481+
// Call the descriptor's tp_descr_get
1482+
vm.call_get_descriptor(&descr, obj.clone())
1483+
.unwrap_or_else(|| {
1484+
Err(vm.new_type_error(format!(
1485+
"this __dict__ descriptor does not support '{}' objects",
1486+
obj.class().name()
1487+
)))
1488+
})
1489+
} else {
14771490
Err(vm.new_type_error(format!(
14781491
"this __dict__ descriptor does not support '{}' objects",
1479-
descr.class()
1492+
obj.class().name()
14801493
)))
1481-
})?,
1482-
None => object::object_get_dict(obj, vm)?.into(),
1483-
};
1484-
Ok(ret)
1494+
}
1495+
} else {
1496+
// PyObject_GenericGetDict
1497+
object::object_get_dict(obj, vm).map(Into::into)
1498+
}
14851499
}
14861500

14871501
fn subtype_set_dict(obj: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
1488-
let cls = obj.class();
1489-
match find_base_dict_descr(cls, vm) {
1490-
Some(descr) => {
1502+
// Following CPython's subtype_setdict exactly
1503+
let base = get_builtin_base_with_dict(obj.class(), vm);
1504+
1505+
if let Some(base_type) = base {
1506+
if let Some(descr) = get_dict_descriptor(&base_type, vm) {
1507+
// Call the descriptor's tp_descr_set
14911508
let descr_set = descr
14921509
.class()
14931510
.mro_find_map(|cls| cls.slots.descr_set.load())
14941511
.ok_or_else(|| {
14951512
vm.new_type_error(format!(
14961513
"this __dict__ descriptor does not support '{}' objects",
1497-
cls.name()
1514+
obj.class().name()
14981515
))
14991516
})?;
15001517
descr_set(&descr, obj, PySetterValue::Assign(value), vm)
1518+
} else {
1519+
Err(vm.new_type_error(format!(
1520+
"this __dict__ descriptor does not support '{}' objects",
1521+
obj.class().name()
1522+
)))
15011523
}
1502-
None => {
1503-
object::object_set_dict(obj, value.try_into_value(vm)?, vm)?;
1504-
Ok(())
1505-
}
1524+
} else {
1525+
// PyObject_GenericSetDict
1526+
object::object_set_dict(obj, value.try_into_value(vm)?, vm)?;
1527+
Ok(())
15061528
}
15071529
}
15081530

0 commit comments

Comments
 (0)