@@ -5,6 +5,9 @@ use ahash::AHashMap as HashMap;
55use crate :: cell:: { AtomicRef , AtomicRefMut } ;
66use crate :: { Resource , ResourceId , World } ;
77
8+ #[ cfg( feature = "nightly" ) ]
9+ use core:: ptr:: { DynMetadata , Pointee } ;
10+
811/// This implements `Send` and `Sync` unconditionally.
912/// (the trait itself doesn't need to have these bounds and the
1013/// resources are already guaranteed to fulfill it).
@@ -50,14 +53,18 @@ pub unsafe trait CastFrom<T> {
5053
5154/// An iterator for the `MetaTable`.
5255pub struct MetaIter < ' a , T : ?Sized + ' a > {
56+ #[ cfg( not( feature = "nightly" ) ) ]
5357 vtable_fns : & ' a [ fn ( * mut ( ) ) -> * mut T ] ,
58+ #[ cfg( feature = "nightly" ) ]
59+ vtables : & ' a [ DynMetadata < T > ] ,
5460 index : usize ,
5561 tys : & ' a [ TypeId ] ,
5662 // `MetaIter` is invariant over `T`
5763 marker : PhantomData < Invariant < T > > ,
5864 world : & ' a World ,
5965}
6066
67+ #[ cfg( not( feature = "nightly" ) ) ]
6168impl < ' a , T > Iterator for MetaIter < ' a , T >
6269where
6370 T : ?Sized + ' a ,
@@ -97,16 +104,61 @@ where
97104 }
98105}
99106
107+ #[ cfg( feature = "nightly" ) ]
108+ impl < ' a , T > Iterator for MetaIter < ' a , T >
109+ where
110+ T : ?Sized + ' a ,
111+ T : Pointee < Metadata = DynMetadata < T > > ,
112+ {
113+ type Item = AtomicRef < ' a , T > ;
114+
115+ #[ allow( clippy:: borrowed_box) ] // variant of https://github.com/rust-lang/rust-clippy/issues/5770
116+ fn next ( & mut self ) -> Option < <Self as Iterator >:: Item > {
117+ loop {
118+ let resource_id = match self . tys . get ( self . index ) {
119+ Some ( & x) => ResourceId :: from_type_id ( x) ,
120+ None => return None ,
121+ } ;
122+
123+ let index = self . index ;
124+ self . index += 1 ;
125+
126+ // SAFETY: We just read the value and don't replace it.
127+ if let Some ( res) = unsafe { self . world . try_fetch_internal ( resource_id) } {
128+ let vtable = self . vtables [ index] ;
129+ let trait_object = AtomicRef :: map ( res. borrow ( ) , |res : & Box < dyn Resource > | {
130+ let ptr: * const dyn Resource = Box :: as_ref ( res) ;
131+ let trait_ptr = core:: ptr:: from_raw_parts ( ptr. cast :: < ( ) > ( ) , vtable) ;
132+ // SAFETY: For a particular index we store a corresponding
133+ // TypeId and vtable in tys and vtables respectively.
134+ // We rely on `try_fetch_interal` returning a trait object
135+ // with a concrete type that has the provided TypeId. The
136+ // signature of the closure parameter of `AtomicRef::map`
137+ // should ensure we aren't accidentally extending the
138+ // lifetime here. Also see safety note in `MetaTable::get`.
139+ unsafe { & * trait_ptr }
140+ } ) ;
141+
142+ return Some ( trait_object) ;
143+ }
144+ }
145+ }
146+ }
147+
100148/// A mutable iterator for the `MetaTable`.
101149pub struct MetaIterMut < ' a , T : ?Sized + ' a > {
150+ #[ cfg( not( feature = "nightly" ) ) ]
102151 vtable_fns : & ' a [ fn ( * mut ( ) ) -> * mut T ] ,
152+ #[ cfg( feature = "nightly" ) ]
153+ vtables : & ' a [ DynMetadata < T > ] ,
103154 index : usize ,
104155 tys : & ' a [ TypeId ] ,
105156 // `MetaIterMut` is invariant over `T`
106157 marker : PhantomData < Invariant < T > > ,
107158 world : & ' a World ,
108159}
109160
161+ #[ cfg( not( feature = "nightly" ) ) ]
110162impl < ' a , T > Iterator for MetaIterMut < ' a , T >
111163where
112164 T : ?Sized + ' a ,
@@ -149,6 +201,50 @@ where
149201 }
150202}
151203
204+ #[ cfg( feature = "nightly" ) ]
205+ impl < ' a , T > Iterator for MetaIterMut < ' a , T >
206+ where
207+ T : ?Sized + ' a ,
208+ T : Pointee < Metadata = DynMetadata < T > > ,
209+ {
210+ type Item = AtomicRefMut < ' a , T > ;
211+
212+ fn next ( & mut self ) -> Option < <Self as Iterator >:: Item > {
213+ loop {
214+ let resource_id = match self . tys . get ( self . index ) {
215+ Some ( & x) => ResourceId :: from_type_id ( x) ,
216+ None => return None ,
217+ } ;
218+
219+ let index = self . index ;
220+ self . index += 1 ;
221+
222+ // Note: this relies on implementation details of
223+ // try_fetch_internal!
224+ // SAFETY: We don't swap out the Box or expose a mutable reference to it.
225+ if let Some ( res) = unsafe { self . world . try_fetch_internal ( resource_id) } {
226+ let vtable = self . vtables [ index] ;
227+ let trait_object =
228+ AtomicRefMut :: map ( res. borrow_mut ( ) , |res : & mut Box < dyn Resource > | {
229+ let ptr: * mut dyn Resource = Box :: as_mut ( res) ;
230+ let trait_ptr = core:: ptr:: from_raw_parts_mut ( ptr. cast :: < ( ) > ( ) , vtable) ;
231+ // SAFETY: For a particular index we store a corresponding
232+ // TypeId and vtable in tys and vtables respectively.
233+ // We rely on `try_fetch_interal` returning a trait object
234+ // with a concrete type that has the provided TypeId. The
235+ // signature of the closure parameter of `AtomicRefMut::map`
236+ // should ensure we aren't accidentally extending the
237+ // lifetime here. Also see safety note in
238+ // `MetaTable::get_mut`.
239+ unsafe { & mut * trait_ptr }
240+ } ) ;
241+
242+ return Some ( trait_object) ;
243+ }
244+ }
245+ }
246+ }
247+
152248/// Given an address and provenance, produces a pointer to a trait object for
153249/// which `CastFrom<T>` is implemented.
154250///
@@ -159,6 +255,7 @@ where
159255///
160256/// We exclusively operate on pointers here so we only need a single function
161257/// pointer in the meta-table for both `&T` and `&mut T` cases.
258+ #[ cfg( not( feature = "nightly" ) ) ]
162259fn attach_vtable < TraitObject : ?Sized , T > ( value : * mut ( ) ) -> * mut TraitObject
163260where
164261 TraitObject : CastFrom < T > + ' static ,
@@ -245,10 +342,10 @@ where
245342/// }
246343/// ```
247344pub struct MetaTable < T : ?Sized > {
248- // TODO: When `ptr_metadata` is stabilized we can use that to implement this
249- // without a function call (and without trying to make assumptions about the
250- // layout of trait object pointers). https://github.com/rust-lang/rust/issues/81513
345+ #[ cfg( not( feature = "nightly" ) ) ]
251346 vtable_fns : Vec < fn ( * mut ( ) ) -> * mut T > ,
347+ #[ cfg( feature = "nightly" ) ]
348+ vtables : Vec < DynMetadata < T > > ,
252349 indices : HashMap < TypeId , usize > ,
253350 tys : Vec < TypeId > ,
254351 // `MetaTable` is invariant over `T`
@@ -258,12 +355,15 @@ pub struct MetaTable<T: ?Sized> {
258355impl < T : ?Sized > MetaTable < T > {
259356 /// Creates a new `MetaTable`.
260357 pub fn new ( ) -> Self {
358+ // TODO: when ptr_metadata is stablilized this can just be a trait bound: Pointee<Metadata
359+ // = DynMetadata<T>>
261360 assert_unsized :: < T > ( ) ;
262361
263362 Default :: default ( )
264363 }
265364
266365 /// Registers a resource `R` that implements the trait `T`.
366+ #[ cfg( not( feature = "nightly" ) ) ]
267367 pub fn register < R > ( & mut self )
268368 where
269369 R : Resource ,
@@ -289,9 +389,47 @@ impl<T: ?Sized> MetaTable<T> {
289389 }
290390 }
291391
392+ /// Registers a resource `R` that implements the trait `T`.
393+ #[ cfg( feature = "nightly" ) ]
394+ pub fn register < R > ( & mut self )
395+ where
396+ R : Resource ,
397+ T : CastFrom < R > + ' static ,
398+ T : Pointee < Metadata = DynMetadata < T > > ,
399+ {
400+ let ty_id = TypeId :: of :: < R > ( ) ;
401+ // use self.addr() for unpredictable address to use for checking consistency below
402+ let invalid_ptr = core:: ptr:: invalid_mut :: < R > ( ( self as * mut Self ) . addr ( ) ) ;
403+ let trait_ptr = <T as CastFrom < R > >:: cast ( invalid_ptr) ;
404+ // assert that address not changed (to catch some mistakes in CastFrom impl)
405+ assert_eq ! (
406+ invalid_ptr. addr( ) ,
407+ trait_ptr. addr( ) ,
408+ "Bug: `CastFrom` did not cast `self`"
409+ ) ;
410+ let vtable = core:: ptr:: metadata ( trait_ptr) ;
411+
412+ // Important: ensure no entry exists twice!
413+ let len = self . indices . len ( ) ;
414+ match self . indices . entry ( ty_id) {
415+ Entry :: Occupied ( occ) => {
416+ let ind = * occ. get ( ) ;
417+
418+ self . vtables [ ind] = vtable;
419+ }
420+ Entry :: Vacant ( vac) => {
421+ vac. insert ( len) ;
422+
423+ self . vtables . push ( vtable) ;
424+ self . tys . push ( ty_id) ;
425+ }
426+ }
427+ }
428+
292429 /// Tries to convert `world` to a trait object of type `&T`.
293430 /// If `world` doesn't have an implementation for `T` (or it wasn't
294431 /// registered), this will return `None`.
432+ #[ cfg( not( feature = "nightly" ) ) ]
295433 pub fn get < ' a > ( & self , res : & ' a dyn Resource ) -> Option < & ' a T > {
296434 self . indices . get ( & res. type_id ( ) ) . map ( |& ind| {
297435 let vtable_fn = self . vtable_fns [ ind] ;
@@ -307,9 +445,31 @@ impl<T: ?Sized> MetaTable<T> {
307445 } )
308446 }
309447
448+ /// Tries to convert `world` to a trait object of type `&T`.
449+ /// If `world` doesn't have an implementation for `T` (or it wasn't
450+ /// registered), this will return `None`.
451+ #[ cfg( feature = "nightly" ) ]
452+ pub fn get < ' a > ( & self , res : & ' a dyn Resource ) -> Option < & ' a T >
453+ where
454+ T : Pointee < Metadata = DynMetadata < T > > ,
455+ {
456+ self . indices . get ( & res. type_id ( ) ) . map ( |& ind| {
457+ let vtable = self . vtables [ ind] ;
458+ let ptr = <* const dyn Resource >:: cast :: < ( ) > ( res) ;
459+ let trait_ptr = core:: ptr:: from_raw_parts ( ptr, vtable) ;
460+ // SAFETY: We retrieved the `vtable` via TypeId so it will be a
461+ // vtable that corresponds with the erased type that the TypeId
462+ // refers to. `from_raw_parts` will also preserve the provenance and
463+ // address (so we can safely produce a shared reference since we
464+ // started with one).
465+ unsafe { & * trait_ptr }
466+ } )
467+ }
468+
310469 /// Tries to convert `world` to a trait object of type `&mut T`.
311470 /// If `world` doesn't have an implementation for `T` (or it wasn't
312471 /// registered), this will return `None`.
472+ #[ cfg( not( feature = "nightly" ) ) ]
313473 pub fn get_mut < ' a > ( & self , res : & ' a mut dyn Resource ) -> Option < & ' a mut T > {
314474 self . indices . get ( & res. type_id ( ) ) . map ( |& ind| {
315475 let vtable_fn = self . vtable_fns [ ind] ;
@@ -324,10 +484,34 @@ impl<T: ?Sized> MetaTable<T> {
324484 } )
325485 }
326486
487+ /// Tries to convert `world` to a trait object of type `&mut T`.
488+ /// If `world` doesn't have an implementation for `T` (or it wasn't
489+ /// registered), this will return `None`.
490+ #[ cfg( feature = "nightly" ) ]
491+ pub fn get_mut < ' a > ( & self , res : & ' a mut dyn Resource ) -> Option < & ' a mut T >
492+ where
493+ T : Pointee < Metadata = DynMetadata < T > > ,
494+ {
495+ self . indices . get ( & res. type_id ( ) ) . map ( |& ind| {
496+ let vtable = self . vtables [ ind] ;
497+ let ptr = <* mut dyn Resource >:: cast :: < ( ) > ( res) ;
498+ let trait_ptr = core:: ptr:: from_raw_parts_mut ( ptr, vtable) ;
499+ // SAFETY: We retrieved the `vtable` via TypeId so it will be a
500+ // vtable that corresponds with the erased type that the TypeId
501+ // refers to. `from_raw_parts_mut` will also preserve the provenance
502+ // and address (so we can safely produce a mutable reference since
503+ // we started with one).
504+ unsafe { & mut * trait_ptr }
505+ } )
506+ }
507+
327508 /// Iterates all resources that implement `T` and were registered.
328509 pub fn iter < ' a > ( & ' a self , res : & ' a World ) -> MetaIter < ' a , T > {
329510 MetaIter {
511+ #[ cfg( not( feature = "nightly" ) ) ]
330512 vtable_fns : & self . vtable_fns ,
513+ #[ cfg( feature = "nightly" ) ]
514+ vtables : & self . vtables ,
331515 index : 0 ,
332516 world : res,
333517 tys : & self . tys ,
@@ -338,7 +522,10 @@ impl<T: ?Sized> MetaTable<T> {
338522 /// Iterates all resources that implement `T` and were registered mutably.
339523 pub fn iter_mut < ' a > ( & ' a self , res : & ' a World ) -> MetaIterMut < ' a , T > {
340524 MetaIterMut {
525+ #[ cfg( not( feature = "nightly" ) ) ]
341526 vtable_fns : & self . vtable_fns ,
527+ #[ cfg( feature = "nightly" ) ]
528+ vtables : & self . vtables ,
342529 index : 0 ,
343530 world : res,
344531 tys : & self . tys ,
@@ -353,7 +540,10 @@ where
353540{
354541 fn default ( ) -> Self {
355542 MetaTable {
543+ #[ cfg( not( feature = "nightly" ) ) ]
356544 vtable_fns : Default :: default ( ) ,
545+ #[ cfg( feature = "nightly" ) ]
546+ vtables : Default :: default ( ) ,
357547 indices : Default :: default ( ) ,
358548 tys : Default :: default ( ) ,
359549 marker : Default :: default ( ) ,
@@ -362,7 +552,7 @@ where
362552}
363553
364554fn assert_unsized < T : ?Sized > ( ) {
365- use std :: mem:: size_of;
555+ use core :: mem:: size_of;
366556
367557 assert_eq ! ( size_of:: <& T >( ) , 2 * size_of:: <usize >( ) ) ;
368558}
0 commit comments