@@ -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,49 @@ where
149201 }
150202}
151203
204+ impl < ' a , T > Iterator for MetaIterMut < ' a , T >
205+ where
206+ T : ?Sized + ' a ,
207+ T : Pointee < Metadata = DynMetadata < T > > ,
208+ {
209+ type Item = AtomicRefMut < ' a , T > ;
210+
211+ fn next ( & mut self ) -> Option < <Self as Iterator >:: Item > {
212+ loop {
213+ let resource_id = match self . tys . get ( self . index ) {
214+ Some ( & x) => ResourceId :: from_type_id ( x) ,
215+ None => return None ,
216+ } ;
217+
218+ let index = self . index ;
219+ self . index += 1 ;
220+
221+ // Note: this relies on implementation details of
222+ // try_fetch_internal!
223+ // SAFETY: We don't swap out the Box or expose a mutable reference to it.
224+ if let Some ( res) = unsafe { self . world . try_fetch_internal ( resource_id) } {
225+ let vtable = self . vtables [ index] ;
226+ let trait_object =
227+ AtomicRefMut :: map ( res. borrow_mut ( ) , |res : & mut Box < dyn Resource > | {
228+ let ptr: * mut dyn Resource = Box :: as_mut ( res) ;
229+ let trait_ptr = core:: ptr:: from_raw_parts_mut ( ptr. cast :: < ( ) > ( ) , vtable) ;
230+ // SAFETY: For a particular index we store a corresponding
231+ // TypeId and vtable in tys and vtables respectively.
232+ // We rely on `try_fetch_interal` returning a trait object
233+ // with a concrete type that has the provided TypeId. The
234+ // signature of the closure parameter of `AtomicRefMut::map`
235+ // should ensure we aren't accidentally extending the
236+ // lifetime here. Also see safety note in
237+ // `MetaTable::get_mut`.
238+ unsafe { & mut * trait_ptr }
239+ } ) ;
240+
241+ return Some ( trait_object) ;
242+ }
243+ }
244+ }
245+ }
246+
152247/// Given an address and provenance, produces a pointer to a trait object for
153248/// which `CastFrom<T>` is implemented.
154249///
@@ -159,6 +254,7 @@ where
159254///
160255/// We exclusively operate on pointers here so we only need a single function
161256/// pointer in the meta-table for both `&T` and `&mut T` cases.
257+ #[ cfg( not( feature = "nightly" ) ) ]
162258fn attach_vtable < TraitObject : ?Sized , T > ( value : * mut ( ) ) -> * mut TraitObject
163259where
164260 TraitObject : CastFrom < T > + ' static ,
@@ -245,10 +341,10 @@ where
245341/// }
246342/// ```
247343pub 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
344+ #[ cfg( not( feature = "nightly" ) ) ]
251345 vtable_fns : Vec < fn ( * mut ( ) ) -> * mut T > ,
346+ #[ cfg( feature = "nightly" ) ]
347+ vtables : Vec < DynMetadata < T > > ,
252348 indices : HashMap < TypeId , usize > ,
253349 tys : Vec < TypeId > ,
254350 // `MetaTable` is invariant over `T`
@@ -258,12 +354,15 @@ pub struct MetaTable<T: ?Sized> {
258354impl < T : ?Sized > MetaTable < T > {
259355 /// Creates a new `MetaTable`.
260356 pub fn new ( ) -> Self {
357+ // TODO: when ptr_metadata is stablilized this can just be a trait bound: Pointee<Metadata
358+ // = DynMetadata<T>>
261359 assert_unsized :: < T > ( ) ;
262360
263361 Default :: default ( )
264362 }
265363
266364 /// Registers a resource `R` that implements the trait `T`.
365+ #[ cfg( not( feature = "nightly" ) ) ]
267366 pub fn register < R > ( & mut self )
268367 where
269368 R : Resource ,
@@ -289,9 +388,47 @@ impl<T: ?Sized> MetaTable<T> {
289388 }
290389 }
291390
391+ /// Registers a resource `R` that implements the trait `T`.
392+ #[ cfg( feature = "nightly" ) ]
393+ pub fn register < R > ( & mut self )
394+ where
395+ R : Resource ,
396+ T : CastFrom < R > + ' static ,
397+ T : Pointee < Metadata = DynMetadata < T > > ,
398+ {
399+ let ty_id = TypeId :: of :: < R > ( ) ;
400+ // use self.addr() for unpredictable address to use for checking consistency below
401+ let invalid_ptr = core:: ptr:: invalid_mut :: < R > ( ( self as * mut Self ) . addr ( ) ) ;
402+ let trait_ptr = <T as CastFrom < R > >:: cast ( invalid_ptr) ;
403+ // assert that address not changed (to catch some mistakes in CastFrom impl)
404+ assert_eq ! (
405+ invalid_ptr. addr( ) ,
406+ trait_ptr. addr( ) ,
407+ "Bug: `CastFrom` did not cast `self`"
408+ ) ;
409+ let vtable = core:: ptr:: metadata ( trait_ptr) ;
410+
411+ // Important: ensure no entry exists twice!
412+ let len = self . indices . len ( ) ;
413+ match self . indices . entry ( ty_id) {
414+ Entry :: Occupied ( occ) => {
415+ let ind = * occ. get ( ) ;
416+
417+ self . vtables [ ind] = vtable;
418+ }
419+ Entry :: Vacant ( vac) => {
420+ vac. insert ( len) ;
421+
422+ self . vtables . push ( vtable) ;
423+ self . tys . push ( ty_id) ;
424+ }
425+ }
426+ }
427+
292428 /// Tries to convert `world` to a trait object of type `&T`.
293429 /// If `world` doesn't have an implementation for `T` (or it wasn't
294430 /// registered), this will return `None`.
431+ #[ cfg( not( feature = "nightly" ) ) ]
295432 pub fn get < ' a > ( & self , res : & ' a dyn Resource ) -> Option < & ' a T > {
296433 self . indices . get ( & res. type_id ( ) ) . map ( |& ind| {
297434 let vtable_fn = self . vtable_fns [ ind] ;
@@ -307,9 +444,31 @@ impl<T: ?Sized> MetaTable<T> {
307444 } )
308445 }
309446
447+ /// Tries to convert `world` to a trait object of type `&T`.
448+ /// If `world` doesn't have an implementation for `T` (or it wasn't
449+ /// registered), this will return `None`.
450+ #[ cfg( feature = "nightly" ) ]
451+ pub fn get < ' a > ( & self , res : & ' a dyn Resource ) -> Option < & ' a T >
452+ where
453+ T : Pointee < Metadata = DynMetadata < T > > ,
454+ {
455+ self . indices . get ( & res. type_id ( ) ) . map ( |& ind| {
456+ let vtable = self . vtables [ ind] ;
457+ let ptr = <* const dyn Resource >:: cast :: < ( ) > ( res) ;
458+ let trait_ptr = core:: ptr:: from_raw_parts ( ptr, vtable) ;
459+ // SAFETY: We retrieved the `vtable` via TypeId so it will be a
460+ // vtable that corresponds with the erased type that the TypeId
461+ // refers to. `from_raw_parts` will also preserve the provenance and
462+ // address (so we can safely produce a shared reference since we
463+ // started with one).
464+ unsafe { & * trait_ptr }
465+ } )
466+ }
467+
310468 /// Tries to convert `world` to a trait object of type `&mut T`.
311469 /// If `world` doesn't have an implementation for `T` (or it wasn't
312470 /// registered), this will return `None`.
471+ #[ cfg( not( feature = "nightly" ) ) ]
313472 pub fn get_mut < ' a > ( & self , res : & ' a mut dyn Resource ) -> Option < & ' a mut T > {
314473 self . indices . get ( & res. type_id ( ) ) . map ( |& ind| {
315474 let vtable_fn = self . vtable_fns [ ind] ;
@@ -324,10 +483,34 @@ impl<T: ?Sized> MetaTable<T> {
324483 } )
325484 }
326485
486+ /// Tries to convert `world` to a trait object of type `&mut T`.
487+ /// If `world` doesn't have an implementation for `T` (or it wasn't
488+ /// registered), this will return `None`.
489+ #[ cfg( feature = "nightly" ) ]
490+ pub fn get_mut < ' a > ( & self , res : & ' a mut dyn Resource ) -> Option < & ' a mut T >
491+ where
492+ T : Pointee < Metadata = DynMetadata < T > > ,
493+ {
494+ self . indices . get ( & res. type_id ( ) ) . map ( |& ind| {
495+ let vtable = self . vtables [ ind] ;
496+ let ptr = <* mut dyn Resource >:: cast :: < ( ) > ( res) ;
497+ let trait_ptr = core:: ptr:: from_raw_parts_mut ( ptr, vtable) ;
498+ // SAFETY: We retrieved the `vtable` via TypeId so it will be a
499+ // vtable that corresponds with the erased type that the TypeId
500+ // refers to. `from_raw_parts_mut` will also preserve the provenance
501+ // and address (so we can safely produce a mutable reference since
502+ // we started with one).
503+ unsafe { & mut * trait_ptr }
504+ } )
505+ }
506+
327507 /// Iterates all resources that implement `T` and were registered.
328508 pub fn iter < ' a > ( & ' a self , res : & ' a World ) -> MetaIter < ' a , T > {
329509 MetaIter {
510+ #[ cfg( not( feature = "nightly" ) ) ]
330511 vtable_fns : & self . vtable_fns ,
512+ #[ cfg( feature = "nightly" ) ]
513+ vtables : & self . vtables ,
331514 index : 0 ,
332515 world : res,
333516 tys : & self . tys ,
@@ -338,7 +521,10 @@ impl<T: ?Sized> MetaTable<T> {
338521 /// Iterates all resources that implement `T` and were registered mutably.
339522 pub fn iter_mut < ' a > ( & ' a self , res : & ' a World ) -> MetaIterMut < ' a , T > {
340523 MetaIterMut {
524+ #[ cfg( not( feature = "nightly" ) ) ]
341525 vtable_fns : & self . vtable_fns ,
526+ #[ cfg( feature = "nightly" ) ]
527+ vtables : & self . vtables ,
342528 index : 0 ,
343529 world : res,
344530 tys : & self . tys ,
@@ -353,7 +539,10 @@ where
353539{
354540 fn default ( ) -> Self {
355541 MetaTable {
542+ #[ cfg( not( feature = "nightly" ) ) ]
356543 vtable_fns : Default :: default ( ) ,
544+ #[ cfg( feature = "nightly" ) ]
545+ vtables : Default :: default ( ) ,
357546 indices : Default :: default ( ) ,
358547 tys : Default :: default ( ) ,
359548 marker : Default :: default ( ) ,
@@ -362,7 +551,7 @@ where
362551}
363552
364553fn assert_unsized < T : ?Sized > ( ) {
365- use std :: mem:: size_of;
554+ use core :: mem:: size_of;
366555
367556 assert_eq ! ( size_of:: <& T >( ) , 2 * size_of:: <usize >( ) ) ;
368557}
0 commit comments