@@ -11,6 +11,7 @@ use rustc_middle::{
11
11
Ty ,
12
12
} ,
13
13
} ;
14
+ use rustc_span:: def_id:: DefId ;
14
15
15
16
use crate :: * ;
16
17
@@ -99,9 +100,9 @@ impl<'tcx> Tree {
99
100
/// Policy for a new borrow.
100
101
#[ derive( Debug , Clone , Copy ) ]
101
102
struct 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 ,
105
106
/// Which permission should the pointer start with.
106
107
initial_state : Permission ,
107
108
/// Whether this pointer is part of the arguments of a function call.
@@ -128,28 +129,27 @@ impl<'tcx> NewPermission {
128
129
// `&`s, which are excluded above.
129
130
_ => return None ,
130
131
} ;
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 ;
134
132
135
133
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 } )
137
135
}
138
136
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 (
142
141
ty : Ty < ' tcx > ,
143
142
kind : RetagKind ,
144
143
cx : & crate :: MiriInterpCx < ' _ , ' tcx > ,
144
+ zero_size : bool ,
145
145
) -> Option < Self > {
146
146
let pointee = ty. builtin_deref ( true ) . unwrap ( ) . ty ;
147
147
pointee. is_unpin ( * cx. tcx , cx. param_env ( ) ) . then_some ( ( ) ) . map ( |( ) | {
148
148
// Regular `Unpin` box, give it `noalias` but only a weak protector
149
149
// because it is valid to deallocate it within the function.
150
150
let ty_is_freeze = ty. is_freeze ( * cx. tcx , cx. param_env ( ) ) ;
151
151
Self {
152
- perform_read_access : true ,
152
+ zero_size ,
153
153
initial_state : Permission :: new_unique_2phase ( ty_is_freeze) ,
154
154
protector : ( kind == RetagKind :: FnEntry ) . then_some ( ProtectorKind :: WeakProtector ) ,
155
155
}
@@ -201,6 +201,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
201
201
Ok ( ( ) )
202
202
} ;
203
203
204
+ trace ! ( "Reborrow of size {:?}" , ptr_size) ;
204
205
let ( alloc_id, base_offset, parent_prov) = if ptr_size > Size :: ZERO {
205
206
this. ptr_get_alloc_id ( place. ptr ) ?
206
207
} else {
@@ -276,8 +277,8 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
276
277
let range = alloc_range ( base_offset, ptr_size) ;
277
278
let mut tree_borrows = alloc_extra. borrow_tracker_tb ( ) . borrow_mut ( ) ;
278
279
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
+ {
281
282
let global = & this. machine . borrow_tracker . as_ref ( ) . unwrap ( ) ;
282
283
let span = this. machine . current_span ( ) ;
283
284
tree_borrows. perform_access (
@@ -308,12 +309,19 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
308
309
// We want a place for where the ptr *points to*, so we get one.
309
310
let place = this. ref_to_mplace ( val) ?;
310
311
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) ;
317
325
318
326
// This new tag is not guaranteed to actually be used.
319
327
//
@@ -324,7 +332,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
324
332
let new_tag = this. machine . borrow_tracker . as_mut ( ) . unwrap ( ) . get_mut ( ) . new_ptr ( ) ;
325
333
326
334
// 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) ?;
328
336
329
337
// Adjust pointer.
330
338
let new_place = place. map_provenance ( |p| {
@@ -359,10 +367,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
359
367
val : & ImmTy < ' tcx , Provenance > ,
360
368
) -> InterpResult < ' tcx , ImmTy < ' tcx , Provenance > > {
361
369
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 ,
366
374
} ;
367
375
this. tb_retag_reference ( val, new_perm)
368
376
}
@@ -374,15 +382,19 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
374
382
place : & PlaceTy < ' tcx , Provenance > ,
375
383
) -> InterpResult < ' tcx > {
376
384
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 } ;
379
390
return visitor. visit_value ( place) ;
380
391
381
392
// The actual visitor.
382
393
struct RetagVisitor < ' ecx , ' mir , ' tcx > {
383
394
ecx : & ' ecx mut MiriInterpCx < ' mir , ' tcx > ,
384
395
kind : RetagKind ,
385
396
retag_fields : RetagFields ,
397
+ unique_did : Option < DefId > ,
386
398
}
387
399
impl < ' ecx , ' mir , ' tcx > RetagVisitor < ' ecx , ' mir , ' tcx > {
388
400
#[ inline( always) ] // yes this helps in our benchmarks
@@ -407,8 +419,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
407
419
self . ecx
408
420
}
409
421
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.
410
425
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
+ ) ;
412
432
self . retag_ptr_inplace ( place, new_perm)
413
433
}
414
434
@@ -440,6 +460,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
440
460
// even if field retagging is not enabled. *shrug*)
441
461
self . walk_value ( place) ?;
442
462
}
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
+ }
443
473
_ => {
444
474
// Not a reference/pointer/box. Only recurse if configured appropriately.
445
475
let recurse = match self . retag_fields {
@@ -456,7 +486,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
456
486
}
457
487
}
458
488
}
459
-
460
489
Ok ( ( ) )
461
490
}
462
491
}
@@ -486,7 +515,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
486
515
// FIXME: do we truly want a 2phase borrow here?
487
516
let new_perm = Some ( NewPermission {
488
517
initial_state : Permission :: new_unique_2phase ( /*freeze*/ false ) ,
489
- perform_read_access : true ,
518
+ zero_size : false ,
490
519
protector : Some ( ProtectorKind :: StrongProtector ) ,
491
520
} ) ;
492
521
let val = this. tb_retag_reference ( & val, new_perm) ?;
@@ -552,3 +581,27 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
552
581
tree_borrows. give_pointer_debug_name ( tag, nth_parent, name)
553
582
}
554
583
}
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