@@ -3,7 +3,7 @@ use crate::common::{
3
3
hash:: { PyHash , PyUHash } ,
4
4
lock:: PyMutex ,
5
5
} ;
6
- use crate :: object:: { Traverse , TraverseFn } ;
6
+ use crate :: object:: { MaybeTraverse , Traverse , TraverseFn } ;
7
7
use crate :: {
8
8
AsObject , Context , Py , PyObject , PyObjectRef , PyPayload , PyRef , PyResult , TryFromObject ,
9
9
atomic_func,
@@ -449,6 +449,24 @@ impl Representable for PyTuple {
449
449
}
450
450
}
451
451
452
+ impl PyRef < PyTuple > {
453
+ pub fn try_into_typed < T : PyPayload > (
454
+ self ,
455
+ vm : & VirtualMachine ,
456
+ ) -> PyResult < PyRef < PyTupleTyped < PyRef < T > > > > {
457
+ PyRef :: < PyTupleTyped < PyRef < T > > > :: try_from_untyped ( self , vm)
458
+ }
459
+ /// # Safety
460
+ ///
461
+ /// The caller must ensure that all elements in the tuple are valid instances
462
+ /// of type `T` before calling this method. This is typically verified by
463
+ /// calling `try_into_typed` first.
464
+ unsafe fn into_typed_unchecked < T : PyPayload > ( self ) -> PyRef < PyTupleTyped < PyRef < T > > > {
465
+ let obj: PyObjectRef = self . into ( ) ;
466
+ unsafe { obj. downcast_unchecked :: < PyTupleTyped < PyRef < T > > > ( ) }
467
+ }
468
+ }
469
+
452
470
#[ pyclass( module = false , name = "tuple_iterator" , traverse) ]
453
471
#[ derive( Debug ) ]
454
472
pub ( crate ) struct PyTupleIterator {
@@ -500,53 +518,75 @@ pub(crate) fn init(context: &Context) {
500
518
PyTupleIterator :: extend_class ( context, context. types . tuple_iterator_type ) ;
501
519
}
502
520
503
- pub struct PyTupleTyped < T : TransmuteFromObject > {
521
+ #[ repr( transparent) ]
522
+ pub struct PyTupleTyped < R > {
504
523
// SAFETY INVARIANT: T must be repr(transparent) over PyObjectRef, and the
505
524
// elements must be logically valid when transmuted to T
506
- tuple : PyTupleRef ,
507
- _marker : PhantomData < Vec < T > > ,
525
+ tuple : PyTuple ,
526
+ _marker : PhantomData < R > ,
508
527
}
509
528
510
- unsafe impl < T > Traverse for PyTupleTyped < T >
529
+ unsafe impl < R > Traverse for PyTupleTyped < R >
511
530
where
512
- T : TransmuteFromObject + Traverse ,
531
+ R : TransmuteFromObject ,
513
532
{
514
533
fn traverse ( & self , tracer_fn : & mut TraverseFn < ' _ > ) {
515
534
self . tuple . traverse ( tracer_fn) ;
516
535
}
517
536
}
518
537
519
- impl < T : TransmuteFromObject > TryFromObject for PyTupleTyped < T > {
520
- fn try_from_object ( vm : & VirtualMachine , obj : PyObjectRef ) -> PyResult < Self > {
521
- let tuple = PyTupleRef :: try_from_object ( vm, obj) ?;
522
- for elem in & * tuple {
523
- T :: check ( vm, elem) ?
524
- }
525
- // SAFETY: the contract of TransmuteFromObject upholds the variant on `tuple`
526
- Ok ( Self {
527
- tuple,
528
- _marker : PhantomData ,
529
- } )
538
+ impl < R : TransmuteFromObject + Traverse > MaybeTraverse for PyTupleTyped < R > {
539
+ const IS_TRACE : bool = true ;
540
+ fn try_traverse ( & self , tracer_fn : & mut TraverseFn < ' _ > ) {
541
+ self . traverse ( tracer_fn) ;
530
542
}
531
543
}
532
544
533
- impl < T : TransmuteFromObject > AsRef < [ T ] > for PyTupleTyped < T > {
534
- fn as_ref ( & self ) -> & [ T ] {
535
- self . as_slice ( )
545
+ impl < T : PyPayload > PyTupleTyped < PyRef < T > > {
546
+ pub fn new_ref ( elements : Vec < PyRef < T > > , ctx : & Context ) -> PyRef < Self > {
547
+ // SAFETY: PyRef<T> has the same layout as PyObjectRef
548
+ unsafe {
549
+ let elements: Vec < PyObjectRef > =
550
+ std:: mem:: transmute :: < Vec < PyRef < T > > , Vec < PyObjectRef > > ( elements) ;
551
+ let tuple = PyTuple :: new_ref ( elements, ctx) ;
552
+ tuple. into_typed_unchecked :: < T > ( )
553
+ }
536
554
}
537
555
}
538
556
539
- impl < T : TransmuteFromObject > PyTupleTyped < T > {
540
- pub fn empty ( vm : & VirtualMachine ) -> Self {
541
- Self {
542
- tuple : vm. ctx . empty_tuple . clone ( ) ,
543
- _marker : PhantomData ,
557
+ impl < T : PyPayload > PyRef < PyTupleTyped < PyRef < T > > > {
558
+ pub fn into_untyped ( self ) -> PyRef < PyTuple > {
559
+ // SAFETY: PyTupleTyped is transparent over PyTuple
560
+ unsafe { std:: mem:: transmute :: < PyRef < PyTupleTyped < PyRef < T > > > , PyRef < PyTuple > > ( self ) }
561
+ }
562
+
563
+ pub fn try_from_untyped ( tuple : PyTupleRef , vm : & VirtualMachine ) -> PyResult < Self > {
564
+ // Check that all elements are of the correct type
565
+ for elem in tuple. as_slice ( ) {
566
+ <PyRef < T > as TransmuteFromObject >:: check ( vm, elem) ?;
544
567
}
568
+ // SAFETY: We just verified all elements are of type T, and PyTupleTyped has the same layout as PyTuple
569
+ Ok ( unsafe { std:: mem:: transmute :: < PyRef < PyTuple > , PyRef < PyTupleTyped < PyRef < T > > > > ( tuple) } )
545
570
}
571
+ }
546
572
573
+ impl < T : PyPayload > Py < PyTupleTyped < PyRef < T > > > {
574
+ pub fn as_untyped ( & self ) -> & Py < PyTuple > {
575
+ // SAFETY: PyTupleTyped is transparent over PyTuple
576
+ unsafe { std:: mem:: transmute :: < & Py < PyTupleTyped < PyRef < T > > > , & Py < PyTuple > > ( self ) }
577
+ }
578
+ }
579
+
580
+ impl < T : PyPayload > AsRef < [ PyRef < T > ] > for PyTupleTyped < PyRef < T > > {
581
+ fn as_ref ( & self ) -> & [ PyRef < T > ] {
582
+ self . as_slice ( )
583
+ }
584
+ }
585
+
586
+ impl < T : PyPayload > PyTupleTyped < PyRef < T > > {
547
587
#[ inline]
548
- pub fn as_slice ( & self ) -> & [ T ] {
549
- unsafe { & * ( self . tuple . as_slice ( ) as * const [ PyObjectRef ] as * const [ T ] ) }
588
+ pub fn as_slice ( & self ) -> & [ PyRef < T > ] {
589
+ unsafe { & * ( self . tuple . as_slice ( ) as * const [ PyObjectRef ] as * const [ PyRef < T > ] ) }
550
590
}
551
591
552
592
#[ inline]
@@ -560,32 +600,16 @@ impl<T: TransmuteFromObject> PyTupleTyped<T> {
560
600
}
561
601
}
562
602
563
- impl < T : TransmuteFromObject > Clone for PyTupleTyped < T > {
564
- fn clone ( & self ) -> Self {
565
- Self {
566
- tuple : self . tuple . clone ( ) ,
567
- _marker : PhantomData ,
568
- }
569
- }
570
- }
571
-
572
- impl < T : TransmuteFromObject + fmt:: Debug > fmt:: Debug for PyTupleTyped < T > {
603
+ impl < R : fmt:: Debug > fmt:: Debug for PyTupleTyped < R > {
573
604
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
574
- self . as_slice ( ) . fmt ( f)
575
- }
576
- }
577
-
578
- impl < T : TransmuteFromObject > From < PyTupleTyped < T > > for PyTupleRef {
579
- #[ inline]
580
- fn from ( tup : PyTupleTyped < T > ) -> Self {
581
- tup. tuple
605
+ self . tuple . as_slice ( ) . fmt ( f)
582
606
}
583
607
}
584
608
585
- impl < T : TransmuteFromObject > ToPyObject for PyTupleTyped < T > {
609
+ impl < T : PyPayload > From < PyRef < PyTupleTyped < PyRef < T > > > > for PyTupleRef {
586
610
#[ inline]
587
- fn to_pyobject ( self , _vm : & VirtualMachine ) -> PyObjectRef {
588
- self . tuple . into ( )
611
+ fn from ( tup : PyRef < PyTupleTyped < PyRef < T > > > ) -> Self {
612
+ tup . into_untyped ( )
589
613
}
590
614
}
591
615
0 commit comments