@@ -3,7 +3,7 @@ use crate::common::{
33 hash:: { PyHash , PyUHash } ,
44 lock:: PyMutex ,
55} ;
6- use crate :: object:: { Traverse , TraverseFn } ;
6+ use crate :: object:: { MaybeTraverse , Traverse , TraverseFn } ;
77use crate :: {
88 AsObject , Context , Py , PyObject , PyObjectRef , PyPayload , PyRef , PyResult , TryFromObject ,
99 atomic_func,
@@ -449,6 +449,24 @@ impl Representable for PyTuple {
449449 }
450450}
451451
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+
452470#[ pyclass( module = false , name = "tuple_iterator" , traverse) ]
453471#[ derive( Debug ) ]
454472pub ( crate ) struct PyTupleIterator {
@@ -500,53 +518,75 @@ pub(crate) fn init(context: &Context) {
500518 PyTupleIterator :: extend_class ( context, context. types . tuple_iterator_type ) ;
501519}
502520
503- pub struct PyTupleTyped < T : TransmuteFromObject > {
521+ #[ repr( transparent) ]
522+ pub struct PyTupleTyped < R > {
504523 // SAFETY INVARIANT: T must be repr(transparent) over PyObjectRef, and the
505524 // elements must be logically valid when transmuted to T
506- tuple : PyTupleRef ,
507- _marker : PhantomData < Vec < T > > ,
525+ tuple : PyTuple ,
526+ _marker : PhantomData < R > ,
508527}
509528
510- unsafe impl < T > Traverse for PyTupleTyped < T >
529+ unsafe impl < R > Traverse for PyTupleTyped < R >
511530where
512- T : TransmuteFromObject + Traverse ,
531+ R : TransmuteFromObject ,
513532{
514533 fn traverse ( & self , tracer_fn : & mut TraverseFn < ' _ > ) {
515534 self . tuple . traverse ( tracer_fn) ;
516535 }
517536}
518537
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) ;
530542 }
531543}
532544
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+ }
536554 }
537555}
538556
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) ?;
544567 }
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) } )
545570 }
571+ }
546572
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 > > {
547587 #[ 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 > ] ) }
550590 }
551591
552592 #[ inline]
@@ -560,32 +600,16 @@ impl<T: TransmuteFromObject> PyTupleTyped<T> {
560600 }
561601}
562602
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 > {
573604 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)
582606 }
583607}
584608
585- impl < T : TransmuteFromObject > ToPyObject for PyTupleTyped < T > {
609+ impl < T : PyPayload > From < PyRef < PyTupleTyped < PyRef < T > > > > for PyTupleRef {
586610 #[ 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 ( )
589613 }
590614}
591615
0 commit comments