@@ -11,6 +11,7 @@ use rustc_middle::{
1111 Ty ,
1212 } ,
1313} ;
14+ use rustc_span:: def_id:: DefId ;
1415
1516use crate :: * ;
1617
@@ -99,9 +100,9 @@ impl<'tcx> Tree {
99100/// Policy for a new borrow.
100101#[ derive( Debug , Clone , Copy ) ]
101102struct NewPermission {
102- /// Whether this borrow requires a read access on its parent .
103- /// `perform_read_access` is `true` for all pointers marked `dereferenceable`.
104- perform_read_access : bool ,
103+ /// Optionally ignore the actual size to do a zero-size reborrow .
104+ /// If this is set then `dereferenceable` is not enforced .
105+ zero_size : bool ,
105106 /// Which permission should the pointer start with.
106107 initial_state : Permission ,
107108 /// Whether this pointer is part of the arguments of a function call.
@@ -128,28 +129,27 @@ impl<'tcx> NewPermission {
128129 // `&`s, which are excluded above.
129130 _ => return None ,
130131 } ;
131- // This field happens to be redundant since right now we always do a read,
132- // but it could be useful in the future.
133- let perform_read_access = true ;
134132
135133 let protector = ( kind == RetagKind :: FnEntry ) . then_some ( ProtectorKind :: StrongProtector ) ;
136- Some ( Self { perform_read_access , initial_state, protector } )
134+ Some ( Self { zero_size : false , initial_state, protector } )
137135 }
138136
139- // Boxes are not handled by `from_ref_ty`, they need special behavior
140- // implemented here.
141- fn from_box_ty (
137+ /// Compute permission for `Box`-like type (`Box` always, and also `Unique` if enabled).
138+ /// These pointers allow deallocation so need a different kind of protector not handled
139+ /// by `from_ref_ty`.
140+ fn from_unique_ty (
142141 ty : Ty < ' tcx > ,
143142 kind : RetagKind ,
144143 cx : & crate :: MiriInterpCx < ' _ , ' tcx > ,
144+ zero_size : bool ,
145145 ) -> Option < Self > {
146146 let pointee = ty. builtin_deref ( true ) . unwrap ( ) . ty ;
147147 pointee. is_unpin ( * cx. tcx , cx. param_env ( ) ) . then_some ( ( ) ) . map ( |( ) | {
148148 // Regular `Unpin` box, give it `noalias` but only a weak protector
149149 // because it is valid to deallocate it within the function.
150150 let ty_is_freeze = ty. is_freeze ( * cx. tcx , cx. param_env ( ) ) ;
151151 Self {
152- perform_read_access : true ,
152+ zero_size ,
153153 initial_state : Permission :: new_unique_2phase ( ty_is_freeze) ,
154154 protector : ( kind == RetagKind :: FnEntry ) . then_some ( ProtectorKind :: WeakProtector ) ,
155155 }
@@ -201,6 +201,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
201201 Ok ( ( ) )
202202 } ;
203203
204+ trace ! ( "Reborrow of size {:?}" , ptr_size) ;
204205 let ( alloc_id, base_offset, parent_prov) = if ptr_size > Size :: ZERO {
205206 this. ptr_get_alloc_id ( place. ptr ) ?
206207 } else {
@@ -276,8 +277,8 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
276277 let range = alloc_range ( base_offset, ptr_size) ;
277278 let mut tree_borrows = alloc_extra. borrow_tracker_tb ( ) . borrow_mut ( ) ;
278279
279- if new_perm . perform_read_access {
280- // Count this reborrow as a read access
280+ // All reborrows incur a (possibly zero-sized) read access to the parent
281+ {
281282 let global = & this. machine . borrow_tracker . as_ref ( ) . unwrap ( ) ;
282283 let span = this. machine . current_span ( ) ;
283284 tree_borrows. perform_access (
@@ -308,12 +309,19 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
308309 // We want a place for where the ptr *points to*, so we get one.
309310 let place = this. ref_to_mplace ( val) ?;
310311
311- // Get a lower bound of the size of this place.
312- // (When `extern type` are involved, use the size of the known prefix.)
313- let size = this
314- . size_and_align_of_mplace ( & place) ?
315- . map ( |( size, _) | size)
316- . unwrap_or ( place. layout . size ) ;
312+ // Determine the size of the reborrow.
313+ // For most types this is the entire size of the place, however
314+ // - when `extern type` is involved we use the size of the known prefix,
315+ // - if the pointer is not reborrowed (raw pointer) or if `zero_size` is set
316+ // then we override the size to do a zero-length reborrow.
317+ let reborrow_size = match new_perm {
318+ Some ( NewPermission { zero_size : false , .. } ) =>
319+ this. size_and_align_of_mplace ( & place) ?
320+ . map ( |( size, _) | size)
321+ . unwrap_or ( place. layout . size ) ,
322+ _ => Size :: from_bytes ( 0 ) ,
323+ } ;
324+ trace ! ( "Creating new permission: {:?} with size {:?}" , new_perm, reborrow_size) ;
317325
318326 // This new tag is not guaranteed to actually be used.
319327 //
@@ -324,7 +332,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
324332 let new_tag = this. machine . borrow_tracker . as_mut ( ) . unwrap ( ) . get_mut ( ) . new_ptr ( ) ;
325333
326334 // Compute the actual reborrow.
327- let reborrowed = this. tb_reborrow ( & place, size , new_perm, new_tag) ?;
335+ let reborrowed = this. tb_reborrow ( & place, reborrow_size , new_perm, new_tag) ?;
328336
329337 // Adjust pointer.
330338 let new_place = place. map_provenance ( |p| {
@@ -359,10 +367,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
359367 val : & ImmTy < ' tcx , Provenance > ,
360368 ) -> InterpResult < ' tcx , ImmTy < ' tcx , Provenance > > {
361369 let this = self . eval_context_mut ( ) ;
362- let new_perm = if let & ty :: Ref ( _ , pointee , mutability ) = val. layout . ty . kind ( ) {
363- NewPermission :: from_ref_ty ( pointee , mutability , kind , this )
364- } else {
365- None
370+ let new_perm = match val. layout . ty . kind ( ) {
371+ & ty :: Ref ( _ , pointee , mutability ) =>
372+ NewPermission :: from_ref_ty ( pointee , mutability , kind , this ) ,
373+ _ => None ,
366374 } ;
367375 this. tb_retag_reference ( val, new_perm)
368376 }
@@ -374,15 +382,19 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
374382 place : & PlaceTy < ' tcx , Provenance > ,
375383 ) -> InterpResult < ' tcx > {
376384 let this = self . eval_context_mut ( ) ;
377- let retag_fields = this. machine . borrow_tracker . as_mut ( ) . unwrap ( ) . get_mut ( ) . retag_fields ;
378- let mut visitor = RetagVisitor { ecx : this, kind, retag_fields } ;
385+ let options = this. machine . borrow_tracker . as_mut ( ) . unwrap ( ) . get_mut ( ) ;
386+ let retag_fields = options. retag_fields ;
387+ let unique_did =
388+ options. unique_is_unique . then ( || this. tcx . lang_items ( ) . ptr_unique ( ) ) . flatten ( ) ;
389+ let mut visitor = RetagVisitor { ecx : this, kind, retag_fields, unique_did } ;
379390 return visitor. visit_value ( place) ;
380391
381392 // The actual visitor.
382393 struct RetagVisitor < ' ecx , ' mir , ' tcx > {
383394 ecx : & ' ecx mut MiriInterpCx < ' mir , ' tcx > ,
384395 kind : RetagKind ,
385396 retag_fields : RetagFields ,
397+ unique_did : Option < DefId > ,
386398 }
387399 impl < ' ecx , ' mir , ' tcx > RetagVisitor < ' ecx , ' mir , ' tcx > {
388400 #[ inline( always) ] // yes this helps in our benchmarks
@@ -407,8 +419,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
407419 self . ecx
408420 }
409421
422+ /// Regardless of how `Unique` is handled, Boxes are always reborrowed.
423+ /// When `Unique` is also reborrowed, then it behaves exactly like `Box`
424+ /// except for the fact that `Box` has a non-zero-sized reborrow.
410425 fn visit_box ( & mut self , place : & PlaceTy < ' tcx , Provenance > ) -> InterpResult < ' tcx > {
411- let new_perm = NewPermission :: from_box_ty ( place. layout . ty , self . kind , self . ecx ) ;
426+ let new_perm = NewPermission :: from_unique_ty (
427+ place. layout . ty ,
428+ self . kind ,
429+ self . ecx ,
430+ /* zero_size */ false ,
431+ ) ;
412432 self . retag_ptr_inplace ( place, new_perm)
413433 }
414434
@@ -440,6 +460,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
440460 // even if field retagging is not enabled. *shrug*)
441461 self . walk_value ( place) ?;
442462 }
463+ ty:: Adt ( adt, _) if self . unique_did == Some ( adt. did ( ) ) => {
464+ let place = inner_ptr_of_unique ( self . ecx , place) ?;
465+ let new_perm = NewPermission :: from_unique_ty (
466+ place. layout . ty ,
467+ self . kind ,
468+ self . ecx ,
469+ /* zero_size */ true ,
470+ ) ;
471+ self . retag_ptr_inplace ( & place, new_perm) ?;
472+ }
443473 _ => {
444474 // Not a reference/pointer/box. Only recurse if configured appropriately.
445475 let recurse = match self . retag_fields {
@@ -456,7 +486,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
456486 }
457487 }
458488 }
459-
460489 Ok ( ( ) )
461490 }
462491 }
@@ -486,7 +515,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
486515 // FIXME: do we truly want a 2phase borrow here?
487516 let new_perm = Some ( NewPermission {
488517 initial_state : Permission :: new_unique_2phase ( /*freeze*/ false ) ,
489- perform_read_access : true ,
518+ zero_size : false ,
490519 protector : Some ( ProtectorKind :: StrongProtector ) ,
491520 } ) ;
492521 let val = this. tb_retag_reference ( & val, new_perm) ?;
@@ -552,3 +581,27 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
552581 tree_borrows. give_pointer_debug_name ( tag, nth_parent, name)
553582 }
554583}
584+
585+ /// Takes a place for a `Unique` and turns it into a place with the inner raw pointer.
586+ /// I.e. input is what you get from the visitor upon encountering an `adt` that is `Unique`,
587+ /// and output can be used by `retag_ptr_inplace`.
588+ fn inner_ptr_of_unique < ' tcx > (
589+ ecx : & mut MiriInterpCx < ' _ , ' tcx > ,
590+ place : & PlaceTy < ' tcx , Provenance > ,
591+ ) -> InterpResult < ' tcx , PlaceTy < ' tcx , Provenance > > {
592+ // Follows the same layout as `interpret/visitor.rs:walk_value` for `Box` in
593+ // `rustc_const_eval`, just with one fewer layer.
594+ // Here we have a `Unique(NonNull(*mut), PhantomData)`
595+ assert_eq ! ( place. layout. fields. count( ) , 2 , "Unique must have exactly 2 fields" ) ;
596+ let ( nonnull, phantom) = ( ecx. place_field ( place, 0 ) ?, ecx. place_field ( place, 1 ) ?) ;
597+ assert ! (
598+ phantom. layout. ty. ty_adt_def( ) . is_some_and( |adt| adt. is_phantom_data( ) ) ,
599+ "2nd field of `Unique` should be `PhantomData` but is `{:?}`" ,
600+ phantom. layout. ty,
601+ ) ;
602+ // Now down to `NonNull(*mut)`
603+ assert_eq ! ( nonnull. layout. fields. count( ) , 1 , "NonNull must have exactly 1 field" ) ;
604+ let ptr = ecx. place_field ( & nonnull, 0 ) ?;
605+ // Finally a plain `*mut`
606+ Ok ( ptr)
607+ }
0 commit comments