Skip to content

Commit 8a89e3c

Browse files
Auto-format: cargo fmt --all
1 parent 55f99ee commit 8a89e3c

1 file changed

Lines changed: 115 additions & 114 deletions

File tree

crates/vm/src/builtins/type.rs

Lines changed: 115 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1352,143 +1352,144 @@ impl Constructor for PyType {
13521352
attributes.insert(identifier!(vm, __hash__), vm.ctx.none.clone().into());
13531353
}
13541354

1355-
let (heaptype_slots, add_dict): (Option<PyRef<PyTuple<PyStrRef>>>, bool) =
1356-
if let Some(x) = attributes.get(identifier!(vm, __slots__)) {
1357-
// Check if __slots__ is bytes - not allowed
1358-
if x.class().is(vm.ctx.types.bytes_type) {
1359-
return Err(vm.new_type_error(
1360-
"__slots__ items must be strings, not 'bytes'".to_owned(),
1361-
));
1362-
}
1363-
1364-
let slots = if x.class().is(vm.ctx.types.str_type) {
1365-
let x = unsafe { x.downcast_unchecked_ref::<PyStr>() };
1366-
PyTuple::new_ref_typed(vec![x.to_owned()], &vm.ctx)
1367-
} else {
1368-
let iter = x.get_iter(vm)?;
1369-
let elements = {
1370-
let mut elements = Vec::new();
1371-
while let PyIterReturn::Return(element) = iter.next(vm)? {
1372-
// Check if any slot item is bytes
1373-
if element.class().is(vm.ctx.types.bytes_type) {
1374-
return Err(vm.new_type_error(
1375-
"__slots__ items must be strings, not 'bytes'".to_owned(),
1376-
));
1377-
}
1378-
elements.push(element);
1379-
}
1380-
elements
1381-
};
1382-
let tuple = elements.into_pytuple(vm);
1383-
tuple.try_into_typed(vm)?
1384-
};
1385-
1386-
// Check if base has itemsize > 0 - can't add arbitrary slots to variable-size types
1387-
// Types like int, bytes, tuple have itemsize > 0 and don't allow custom slots
1388-
// But types like weakref.ref have itemsize = 0 and DO allow slots
1389-
let has_custom_slots = slots
1390-
.iter()
1391-
.any(|s| s.as_str() != "__dict__" && s.as_str() != "__weakref__");
1392-
if has_custom_slots && base.slots.itemsize > 0 {
1393-
return Err(vm.new_type_error(format!(
1394-
"nonempty __slots__ not supported for subtype of '{}'",
1395-
base.name()
1396-
)));
1397-
}
1398-
1399-
// Validate slot names and track duplicates
1400-
let mut seen_dict = false;
1401-
let mut seen_weakref = false;
1402-
for slot in slots.iter() {
1403-
// Use isidentifier for validation (handles Unicode properly)
1404-
if !slot.isidentifier() {
1405-
return Err(vm.new_type_error("__slots__ must be identifiers".to_owned()));
1406-
}
1407-
1408-
let slot_name = slot.as_str();
1355+
let (heaptype_slots, add_dict): (Option<PyRef<PyTuple<PyStrRef>>>, bool) = if let Some(x) =
1356+
attributes.get(identifier!(vm, __slots__))
1357+
{
1358+
// Check if __slots__ is bytes - not allowed
1359+
if x.class().is(vm.ctx.types.bytes_type) {
1360+
return Err(
1361+
vm.new_type_error("__slots__ items must be strings, not 'bytes'".to_owned())
1362+
);
1363+
}
14091364

1410-
// Check for duplicate __dict__
1411-
if slot_name == "__dict__" {
1412-
if seen_dict {
1365+
let slots = if x.class().is(vm.ctx.types.str_type) {
1366+
let x = unsafe { x.downcast_unchecked_ref::<PyStr>() };
1367+
PyTuple::new_ref_typed(vec![x.to_owned()], &vm.ctx)
1368+
} else {
1369+
let iter = x.get_iter(vm)?;
1370+
let elements = {
1371+
let mut elements = Vec::new();
1372+
while let PyIterReturn::Return(element) = iter.next(vm)? {
1373+
// Check if any slot item is bytes
1374+
if element.class().is(vm.ctx.types.bytes_type) {
14131375
return Err(vm.new_type_error(
1414-
"__dict__ slot disallowed: we already got one".to_owned(),
1376+
"__slots__ items must be strings, not 'bytes'".to_owned(),
14151377
));
14161378
}
1417-
seen_dict = true;
1379+
elements.push(element);
14181380
}
1381+
elements
1382+
};
1383+
let tuple = elements.into_pytuple(vm);
1384+
tuple.try_into_typed(vm)?
1385+
};
14191386

1420-
// Check for duplicate __weakref__
1421-
if slot_name == "__weakref__" {
1422-
if seen_weakref {
1423-
return Err(vm.new_type_error(
1424-
"__weakref__ slot disallowed: we already got one".to_owned(),
1425-
));
1426-
}
1427-
seen_weakref = true;
1428-
}
1387+
// Check if base has itemsize > 0 - can't add arbitrary slots to variable-size types
1388+
// Types like int, bytes, tuple have itemsize > 0 and don't allow custom slots
1389+
// But types like weakref.ref have itemsize = 0 and DO allow slots
1390+
let has_custom_slots = slots
1391+
.iter()
1392+
.any(|s| s.as_str() != "__dict__" && s.as_str() != "__weakref__");
1393+
if has_custom_slots && base.slots.itemsize > 0 {
1394+
return Err(vm.new_type_error(format!(
1395+
"nonempty __slots__ not supported for subtype of '{}'",
1396+
base.name()
1397+
)));
1398+
}
14291399

1430-
// Check if slot name conflicts with class attributes
1431-
if attributes.contains_key(vm.ctx.intern_str(slot_name)) {
1432-
return Err(vm.new_value_error(format!(
1433-
"'{}' in __slots__ conflicts with a class variable",
1434-
slot_name
1435-
)));
1436-
}
1400+
// Validate slot names and track duplicates
1401+
let mut seen_dict = false;
1402+
let mut seen_weakref = false;
1403+
for slot in slots.iter() {
1404+
// Use isidentifier for validation (handles Unicode properly)
1405+
if !slot.isidentifier() {
1406+
return Err(vm.new_type_error("__slots__ must be identifiers".to_owned()));
14371407
}
14381408

1439-
// Check if base class already has __dict__ - can't redefine it
1440-
if seen_dict && base.slots.flags.has_feature(PyTypeFlags::HAS_DICT) {
1441-
return Err(
1442-
vm.new_type_error("__dict__ slot disallowed: we already got one".to_owned())
1443-
);
1444-
}
1409+
let slot_name = slot.as_str();
14451410

1446-
// Check if base class already has __weakref__ - can't redefine it
1447-
// A base has weakref support if:
1448-
// 1. It's a heap type without explicit __slots__ (automatic weakref), OR
1449-
// 2. It's a heap type with __weakref__ in its __slots__
1450-
if seen_weakref {
1451-
let base_has_weakref = if let Some(ref ext) = base.heaptype_ext {
1452-
match &ext.slots {
1453-
// Heap type without __slots__ - has automatic weakref
1454-
None => true,
1455-
// Heap type with __slots__ - check if __weakref__ is in slots
1456-
Some(base_slots) => base_slots.iter().any(|s| s.as_str() == "__weakref__"),
1457-
}
1458-
} else {
1459-
// Builtin type - check if it has __weakref__ descriptor
1460-
let weakref_name = vm.ctx.intern_str("__weakref__");
1461-
base.attributes.read().contains_key(weakref_name)
1462-
};
1411+
// Check for duplicate __dict__
1412+
if slot_name == "__dict__" {
1413+
if seen_dict {
1414+
return Err(vm.new_type_error(
1415+
"__dict__ slot disallowed: we already got one".to_owned(),
1416+
));
1417+
}
1418+
seen_dict = true;
1419+
}
14631420

1464-
if base_has_weakref {
1421+
// Check for duplicate __weakref__
1422+
if slot_name == "__weakref__" {
1423+
if seen_weakref {
14651424
return Err(vm.new_type_error(
14661425
"__weakref__ slot disallowed: we already got one".to_owned(),
14671426
));
14681427
}
1428+
seen_weakref = true;
14691429
}
14701430

1471-
// Check if __dict__ is in slots
1472-
let dict_name = "__dict__";
1473-
let has_dict = slots.iter().any(|s| s.as_str() == dict_name);
1474-
1475-
// Filter out __dict__ from slots
1476-
let filtered_slots = if has_dict {
1477-
let filtered: Vec<PyStrRef> = slots
1478-
.iter()
1479-
.filter(|s| s.as_str() != dict_name)
1480-
.cloned()
1481-
.collect();
1482-
PyTuple::new_ref_typed(filtered, &vm.ctx)
1431+
// Check if slot name conflicts with class attributes
1432+
if attributes.contains_key(vm.ctx.intern_str(slot_name)) {
1433+
return Err(vm.new_value_error(format!(
1434+
"'{}' in __slots__ conflicts with a class variable",
1435+
slot_name
1436+
)));
1437+
}
1438+
}
1439+
1440+
// Check if base class already has __dict__ - can't redefine it
1441+
if seen_dict && base.slots.flags.has_feature(PyTypeFlags::HAS_DICT) {
1442+
return Err(
1443+
vm.new_type_error("__dict__ slot disallowed: we already got one".to_owned())
1444+
);
1445+
}
1446+
1447+
// Check if base class already has __weakref__ - can't redefine it
1448+
// A base has weakref support if:
1449+
// 1. It's a heap type without explicit __slots__ (automatic weakref), OR
1450+
// 2. It's a heap type with __weakref__ in its __slots__
1451+
if seen_weakref {
1452+
let base_has_weakref = if let Some(ref ext) = base.heaptype_ext {
1453+
match &ext.slots {
1454+
// Heap type without __slots__ - has automatic weakref
1455+
None => true,
1456+
// Heap type with __slots__ - check if __weakref__ is in slots
1457+
Some(base_slots) => base_slots.iter().any(|s| s.as_str() == "__weakref__"),
1458+
}
14831459
} else {
1484-
slots
1460+
// Builtin type - check if it has __weakref__ descriptor
1461+
let weakref_name = vm.ctx.intern_str("__weakref__");
1462+
base.attributes.read().contains_key(weakref_name)
14851463
};
14861464

1487-
(Some(filtered_slots), has_dict)
1465+
if base_has_weakref {
1466+
return Err(vm.new_type_error(
1467+
"__weakref__ slot disallowed: we already got one".to_owned(),
1468+
));
1469+
}
1470+
}
1471+
1472+
// Check if __dict__ is in slots
1473+
let dict_name = "__dict__";
1474+
let has_dict = slots.iter().any(|s| s.as_str() == dict_name);
1475+
1476+
// Filter out __dict__ from slots
1477+
let filtered_slots = if has_dict {
1478+
let filtered: Vec<PyStrRef> = slots
1479+
.iter()
1480+
.filter(|s| s.as_str() != dict_name)
1481+
.cloned()
1482+
.collect();
1483+
PyTuple::new_ref_typed(filtered, &vm.ctx)
14881484
} else {
1489-
(None, false)
1485+
slots
14901486
};
14911487

1488+
(Some(filtered_slots), has_dict)
1489+
} else {
1490+
(None, false)
1491+
};
1492+
14921493
// FIXME: this is a temporary fix. multi bases with multiple slots will break object
14931494
let base_member_count = bases
14941495
.iter()

0 commit comments

Comments
 (0)