@@ -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