@@ -1383,11 +1383,89 @@ impl Constructor for PyType {
13831383 tuple. try_into_typed ( vm) ?
13841384 } ;
13851385
1386- // Validate that all slots are valid identifiers
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 ;
13871402 for slot in slots. iter ( ) {
1403+ // Use isidentifier for validation (handles Unicode properly)
13881404 if !slot. isidentifier ( ) {
13891405 return Err ( vm. new_type_error ( "__slots__ must be identifiers" . to_owned ( ) ) ) ;
13901406 }
1407+
1408+ let slot_name = slot. as_str ( ) ;
1409+
1410+ // Check for duplicate __dict__
1411+ if slot_name == "__dict__" {
1412+ if seen_dict {
1413+ return Err ( vm. new_type_error (
1414+ "__dict__ slot disallowed: we already got one" . to_owned ( ) ,
1415+ ) ) ;
1416+ }
1417+ seen_dict = true ;
1418+ }
1419+
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+ }
1429+
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+ }
1437+ }
1438+
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+ }
1445+
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+ } ;
1463+
1464+ if base_has_weakref {
1465+ return Err ( vm. new_type_error (
1466+ "__weakref__ slot disallowed: we already got one" . to_owned ( ) ,
1467+ ) ) ;
1468+ }
13911469 }
13921470
13931471 // Check if __dict__ is in slots
0 commit comments