@@ -7,18 +7,82 @@ use orx_concurrent_vec::ConcurrentVec;
7
7
8
8
use crate :: Database ;
9
9
10
+ /// A `Views` struct is associated with some specific database type
11
+ /// (a `DatabaseImpl<U>` for some existential `U`). It contains functions
12
+ /// to downcast from that type to `dyn DbView` for various traits `DbView`.
13
+ /// None of these types are known at compilation time, they are all checked
14
+ /// dynamically through `TypeId` magic.
15
+ ///
16
+ /// You can think of the struct as looking like:
17
+ ///
18
+ /// ```rust,ignore
19
+ /// struct Views<ghost Db> {
20
+ /// source_type_id: TypeId, // `TypeId` for `Db`
21
+ /// view_casters: Arc<ConcurrentVec<exists<DbView> {
22
+ /// ViewCaster<Db, DbView>
23
+ /// }>>,
24
+ /// }
25
+ /// ```
10
26
#[ derive( Clone ) ]
11
27
pub struct Views {
12
28
source_type_id : TypeId ,
13
29
view_casters : Arc < ConcurrentVec < ViewCaster > > ,
14
30
}
15
31
32
+ /// A ViewCaster contains a trait object that can cast from the
33
+ /// (ghost) `Db` type of `Views` to some (ghost) `DbView` type.
34
+ ///
35
+ /// You can think of the struct as looking like:
36
+ ///
37
+ /// ```rust,ignore
38
+ /// struct ViewCaster<ghost Db, ghost DbView> {
39
+ /// target_type_id: TypeId, // TypeId of DbView
40
+ /// type_name: &'static str, // type name of DbView
41
+ /// cast_to: OpaqueBoxDyn, // a `Box<dyn CastTo<DbView>>` that expects a `Db`
42
+ /// free_box: Box<dyn Free>, // the same box as above, but upcast to `dyn Free`
43
+ /// }
44
+ /// ```
45
+ ///
46
+ /// As you can see, we have to work very hard to manage things
47
+ /// in a way that miri is happy with. What is going on here?
48
+ ///
49
+ /// * The `cast_to` is the cast object, but we can't actually name its type, so
50
+ /// we transmute it into some opaque bytes. We can transmute it back once we
51
+ /// are in a function monormophized over some function `T` that has the same type-id
52
+ /// as `target_type_id`.
53
+ /// * The problem is that dropping `cast_to` has no effect and we need
54
+ /// to free the box! To do that, we *also* upcast the box to a `Box<dyn Free>`.
55
+ /// This trait has no purpose but to carry a destructor.
16
56
struct ViewCaster {
57
+ /// The id of the target type `DbView` that we can cast to.
17
58
target_type_id : TypeId ,
59
+
60
+ /// The name of the target type `DbView` that we can cast to.
18
61
type_name : & ' static str ,
19
- func : fn ( & Dummy ) -> & Dummy ,
62
+
63
+ /// A "type-obscured" `Box<dyn CastTo<DbView>>`, where `DbView`
64
+ /// is the type whose id is encoded in `target_type_id`.
65
+ cast_to : OpaqueBoxDyn ,
66
+
67
+ /// An upcasted version of `cast_to`; the only purpose of this field is
68
+ /// to be dropped in the destructor, see `ViewCaster` comment.
69
+ #[ allow( dead_code) ]
70
+ free_box : Box < dyn Free > ,
20
71
}
21
72
73
+ type OpaqueBoxDyn = [ u8 ; std:: mem:: size_of :: < Box < dyn CastTo < Dummy > > > ( ) ] ;
74
+
75
+ trait CastTo < DbView : ?Sized > : Free {
76
+ /// # Safety requirement
77
+ ///
78
+ /// `db` must have a data pointer whose type is the `Db` type for `Self`
79
+ unsafe fn cast < ' db > ( & self , db : & ' db dyn Database ) -> & ' db DbView ;
80
+
81
+ fn into_box_free ( self : Box < Self > ) -> Box < dyn Free > ;
82
+ }
83
+
84
+ trait Free : Send + Sync { }
85
+
22
86
#[ allow( dead_code) ]
23
87
enum Dummy { }
24
88
@@ -45,10 +109,21 @@ impl Views {
45
109
return ;
46
110
}
47
111
112
+ let cast_to: Box < dyn CastTo < DbView > > = Box :: new ( func) ;
113
+ let cast_to: OpaqueBoxDyn =
114
+ unsafe { std:: mem:: transmute :: < Box < dyn CastTo < DbView > > , OpaqueBoxDyn > ( cast_to) } ;
115
+
116
+ // Create a second copy of `cast_to` (which is now `Copy`) and upcast it to a `Box<dyn Any>`.
117
+ // We will drop this box to run the destructor.
118
+ let free_box: Box < dyn Free > = unsafe {
119
+ std:: mem:: transmute :: < OpaqueBoxDyn , Box < dyn CastTo < DbView > > > ( cast_to) . into_box_free ( )
120
+ } ;
121
+
48
122
self . view_casters . push ( ViewCaster {
49
123
target_type_id,
50
124
type_name : std:: any:: type_name :: < DbView > ( ) ,
51
- func : unsafe { std:: mem:: transmute :: < fn ( & Db ) -> & DbView , fn ( & Dummy ) -> & Dummy > ( func) } ,
125
+ cast_to,
126
+ free_box,
52
127
} ) ;
53
128
}
54
129
@@ -73,8 +148,12 @@ impl Views {
73
148
// While the compiler doesn't know what `X` is at this point, we know it's the
74
149
// same as the true type of `db_data_ptr`, and the memory representation for `()`
75
150
// and `&X` are the same (since `X` is `Sized`).
76
- let func: fn ( & ( ) ) -> & DbView = unsafe { std:: mem:: transmute ( caster. func ) } ;
77
- return Some ( func ( data_ptr ( db) ) ) ;
151
+ let cast_to: & OpaqueBoxDyn = & caster. cast_to ;
152
+ unsafe {
153
+ let cast_to =
154
+ std:: mem:: transmute :: < & OpaqueBoxDyn , & Box < dyn CastTo < DbView > > > ( cast_to) ;
155
+ return Some ( cast_to. cast ( db) ) ;
156
+ } ;
78
157
}
79
158
}
80
159
@@ -98,8 +177,32 @@ impl std::fmt::Debug for ViewCaster {
98
177
99
178
/// Given a wide pointer `T`, extracts the data pointer (typed as `()`).
100
179
/// This is safe because `()` gives no access to any data and has no validity requirements in particular.
101
- fn data_ptr < T : ?Sized > ( t : & T ) -> & ( ) {
180
+ unsafe fn data_ptr < T : ?Sized , U > ( t : & T ) -> & U {
102
181
let t: * const T = t;
103
- let u: * const ( ) = t as * const ( ) ;
182
+ let u: * const U = t as * const U ;
104
183
unsafe { & * u }
105
184
}
185
+
186
+ impl < Db , DbView > CastTo < DbView > for fn ( & Db ) -> & DbView
187
+ where
188
+ Db : Database ,
189
+ DbView : ?Sized + Any ,
190
+ {
191
+ unsafe fn cast < ' db > ( & self , db : & ' db dyn Database ) -> & ' db DbView {
192
+ // This tests the safety requirement:
193
+ debug_assert_eq ! ( db. type_id( ) , TypeId :: of:: <Db >( ) ) ;
194
+
195
+ // SAFETY:
196
+ //
197
+ // Caller guarantees that the input is of type `Db`
198
+ // (we test it in the debug-assertion above).
199
+ let db = unsafe { data_ptr :: < dyn Database , Db > ( db) } ;
200
+ ( * self ) ( db)
201
+ }
202
+
203
+ fn into_box_free ( self : Box < Self > ) -> Box < dyn Free > {
204
+ self
205
+ }
206
+ }
207
+
208
+ impl < T : Send + Sync > Free for T { }
0 commit comments