@@ -151,6 +151,14 @@ macro_rules! make_value_visitor {
151151 {
152152 Ok ( ( ) )
153153 }
154+ /// Visits the given value as the pointer of a `Box`. There is nothing to recurse into.
155+ /// The type of `v` will be a raw pointer, but this is a field of `Box<T>` and the
156+ /// pointee type is the actual `T`.
157+ #[ inline( always) ]
158+ fn visit_box( & mut self , _v: & Self :: V ) -> InterpResult <' tcx>
159+ {
160+ Ok ( ( ) )
161+ }
154162 /// Visits this value as an aggregate, you are getting an iterator yielding
155163 /// all the fields (still in an `InterpResult`, you have to do error handling yourself).
156164 /// Recurses into the fields.
@@ -221,6 +229,47 @@ macro_rules! make_value_visitor {
221229 // Slices do not need special handling here: they have `Array` field
222230 // placement with length 0, so we enter the `Array` case below which
223231 // indirectly uses the metadata to determine the actual length.
232+
233+ // However, `Box`... let's talk about `Box`.
234+ ty:: Adt ( def, ..) if def. is_box( ) => {
235+ // `Box` is a hybrid primitive-library-defined type that one the one hand is
236+ // a dereferenceable pointer, on the other hand has *basically arbitrary
237+ // user-defined layout* since the user controls the 'allocator' field. So it
238+ // cannot be treated like a normal pointer, since it does not fit into an
239+ // `Immediate`. Yeah, it is quite terrible. But many visitors want to do
240+ // something with "all boxed pointers", so we handle this mess for them.
241+ //
242+ // When we hit a `Box`, we do not do the usual `visit_aggregate`; instead,
243+ // we (a) call `visit_box` on the pointer value, and (b) recurse on the
244+ // allocator field. We also assert tons of things to ensure we do not miss
245+ // any other fields.
246+
247+ // `Box` has two fields: the pointer we care about, and the allocator.
248+ assert_eq!( v. layout( ) . fields. count( ) , 2 , "`Box` must have exactly 2 fields" ) ;
249+ let ( unique_ptr, alloc) =
250+ ( v. project_field( self . ecx( ) , 0 ) ?, v. project_field( self . ecx( ) , 1 ) ?) ;
251+ // Unfortunately there is some type junk in the way here: `unique_ptr` is a `Unique`...
252+ // (which means another 2 fields, the second of which is a `PhantomData`)
253+ assert_eq!( unique_ptr. layout( ) . fields. count( ) , 2 ) ;
254+ let ( nonnull_ptr, phantom) = (
255+ unique_ptr. project_field( self . ecx( ) , 0 ) ?,
256+ unique_ptr. project_field( self . ecx( ) , 1 ) ?,
257+ ) ;
258+ assert!(
259+ phantom. layout( ) . ty. ty_adt_def( ) . is_some_and( |adt| adt. is_phantom_data( ) ) ,
260+ "2nd field of `Unique` should be PhantomData but is {:?}" ,
261+ phantom. layout( ) . ty,
262+ ) ;
263+ // ... that contains a `NonNull`... (gladly, only a single field here)
264+ assert_eq!( nonnull_ptr. layout( ) . fields. count( ) , 1 ) ;
265+ let raw_ptr = nonnull_ptr. project_field( self . ecx( ) , 0 ) ?; // the actual raw ptr
266+ // ... whose only field finally is a raw ptr we can dereference.
267+ self . visit_box( & raw_ptr) ?;
268+
269+ // The second `Box` field is the allocator, which we recursively check for validity
270+ // like in regular structs.
271+ self . visit_field( v, 1 , & alloc) ?;
272+ }
224273 _ => { } ,
225274 } ;
226275
0 commit comments