@@ -410,48 +410,42 @@ impl PyObject {
410410
411411 fn abstract_issubclass ( & self , cls : & PyObject , vm : & VirtualMachine ) -> PyResult < bool > {
412412 let mut derived = self ;
413- let mut first_item: PyObjectRef ;
414- let tuple;
415413
416414 // First loop: handle single inheritance without recursion
417- loop {
415+ let bases = loop {
418416 if derived. is ( cls) {
419417 return Ok ( true ) ;
420418 }
421419
422- match derived. abstract_get_bases ( vm) ? {
423- Some ( bases) => {
424- let n = bases. len ( ) ;
425- match n {
426- 0 => return Ok ( false ) ,
427- 1 => {
428- // Avoid recursion in the single inheritance case
429- first_item = bases. fast_getitem ( 0 ) . clone ( ) ;
430- derived = & first_item;
431- continue ;
432- }
433- _ => {
434- // Multiple inheritance - break out to handle recursively
435- tuple = bases;
436- break ;
437- }
438- }
420+ let Some ( bases) = derived. abstract_get_bases ( vm) ? else {
421+ return Ok ( false ) ;
422+ } ;
423+ let n = bases. len ( ) ;
424+ match n {
425+ 0 => return Ok ( false ) ,
426+ 1 => {
427+ // Avoid recursion in the single inheritance case
428+ // # safety
429+ // Intention: bases.as_slice()[0].as_object();
430+ // Though type-system cannot guarantee, derived does live long enough in the loop.
431+ derived = unsafe { & * ( bases. as_slice ( ) [ 0 ] . as_object ( ) as * const _ ) } ;
432+ continue ;
439433 }
440- None => {
441- // abstract_get_bases returned None (no valid __bases__ or not a tuple)
442- return Ok ( false ) ;
434+ _ => {
435+ // Multiple inheritance - break out to handle recursively
436+ break bases ;
443437 }
444438 }
445- }
439+ } ;
446440
447441 // Second loop: handle multiple inheritance with recursion
448442 // At this point we know n >= 2
449- let n = tuple . len ( ) ;
443+ let n = bases . len ( ) ;
450444 assert ! ( n >= 2 ) ;
451445
452446 for i in 0 ..n {
453447 let result = vm. with_recursion ( "in __issubclass__" , || {
454- tuple . fast_getitem ( i ) . abstract_issubclass ( cls, vm)
448+ bases . as_slice ( ) [ i ] . abstract_issubclass ( cls, vm)
455449 } ) ?;
456450 if result {
457451 return Ok ( true ) ;
@@ -463,24 +457,25 @@ impl PyObject {
463457
464458 fn recursive_issubclass ( & self , cls : & PyObject , vm : & VirtualMachine ) -> PyResult < bool > {
465459 // Fast path for both being types (matches CPython's PyType_Check)
466- if let ( Ok ( obj) , Ok ( cls) ) = ( self . try_to_ref :: < PyType > ( vm) , cls. try_to_ref :: < PyType > ( vm) ) {
460+ if let Some ( cls) = PyType :: check ( cls)
461+ && let Some ( derived) = PyType :: check ( self )
462+ {
467463 // PyType_IsSubtype equivalent
468- Ok ( obj. fast_issubclass ( cls) )
469- } else {
470- // Check if derived is a class
471- self . check_class ( self , vm, || {
472- "issubclass() arg 1 must be a class" . to_string ( )
473- } ) ?;
474-
475- // Check if cls is a class, tuple, or union (matches CPython's order and message)
476- if !cls. class ( ) . is ( vm. ctx . types . union_type ) {
477- self . check_class ( cls, vm, || {
478- "issubclass() arg 2 must be a class, a tuple of classes, or a union" . to_string ( )
479- } ) ?;
480- }
464+ return Ok ( derived. is_subtype ( cls) ) ;
465+ }
466+ // Check if derived is a class
467+ self . check_class ( self , vm, || {
468+ "issubclass() arg 1 must be a class" . to_string ( )
469+ } ) ?;
481470
482- self . abstract_issubclass ( cls, vm)
471+ // Check if cls is a class, tuple, or union (matches CPython's order and message)
472+ if !cls. class ( ) . is ( vm. ctx . types . union_type ) {
473+ self . check_class ( cls, vm, || {
474+ "issubclass() arg 2 must be a class, a tuple of classes, or a union" . to_string ( )
475+ } ) ?;
483476 }
477+
478+ self . abstract_issubclass ( cls, vm)
484479 }
485480
486481 /// Real issubclass check without going through __subclasscheck__
@@ -504,8 +499,9 @@ impl PyObject {
504499 // Check for Union type - CPython handles this before tuple
505500 let cls_to_check = if cls. class ( ) . is ( vm. ctx . types . union_type ) {
506501 // Get the __args__ attribute which contains the union members
507- if let Ok ( args) = cls. get_attr ( identifier ! ( vm, __args__) , vm) {
508- args
502+ // Match CPython's _Py_union_args which directly accesses the args field
503+ if let Ok ( union) = cls. try_to_ref :: < crate :: builtins:: PyUnion > ( vm) {
504+ union. get_args ( ) . clone ( ) . into ( )
509505 } else {
510506 cls. to_owned ( )
511507 }
@@ -556,7 +552,7 @@ impl PyObject {
556552 Ok ( retval)
557553 } else {
558554 // Not a type object, check if it's a valid class
559- self . check_cls ( cls, vm, || {
555+ self . check_class ( cls, vm, || {
560556 format ! (
561557 "isinstance() arg 2 must be a type, a tuple of types, or a union, not {}" ,
562558 cls. class( )
@@ -589,25 +585,23 @@ impl PyObject {
589585 }
590586
591587 // PyType_CheckExact(cls) optimization
592- if cls. class ( ) . is ( vm. ctx . types . type_type ) {
588+ if cls. is ( vm. ctx . types . type_type ) {
593589 // When cls is exactly a type (not a subclass), use real_is_instance
594590 // to avoid going through __instancecheck__ (matches CPython behavior)
595591 return self . real_is_instance ( cls, vm) ;
596592 }
597593
598594 // Check for Union type (e.g., int | str) - CPython checks this before tuple
599- if cls. class ( ) . is ( vm. ctx . types . union_type ) {
600- if let Ok ( args) = cls. get_attr ( identifier ! ( vm, __args__) , vm) {
601- if let Ok ( tuple) = args. try_to_ref :: < PyTuple > ( vm) {
602- for typ in tuple {
603- if vm
604- . with_recursion ( "in __instancecheck__" , || self . is_instance ( typ, vm) ) ?
605- {
606- return Ok ( true ) ;
607- }
595+ if cls. is ( vm. ctx . types . union_type ) {
596+ // Match CPython's _Py_union_args which directly accesses the args field
597+ if let Ok ( union) = cls. try_to_ref :: < crate :: builtins:: PyUnion > ( vm) {
598+ let tuple = union. get_args ( ) ;
599+ for typ in tuple. iter ( ) {
600+ if vm. with_recursion ( "in __instancecheck__" , || self . is_instance ( typ, vm) ) ? {
601+ return Ok ( true ) ;
608602 }
609- return Ok ( false ) ;
610603 }
604+ return Ok ( false ) ;
611605 }
612606 }
613607
0 commit comments