@@ -90,6 +90,14 @@ pub enum RefKind {
9090 Raw ,
9191}
9292
93+ /// What kind of access is being performed?
94+ #[ derive( Copy , Clone , Debug , Hash , PartialEq , Eq ) ]
95+ pub enum AccessKind {
96+ Read ,
97+ Write ,
98+ Dealloc ,
99+ }
100+
93101/// Extra global state in the memory, available to the memory access hooks
94102#[ derive( Debug ) ]
95103pub struct BarrierTracking {
@@ -221,13 +229,13 @@ impl<'tcx> Stack {
221229 fn access (
222230 & mut self ,
223231 bor : Borrow ,
224- is_write : bool ,
232+ kind : AccessKind ,
225233 barrier_tracking : & BarrierTracking ,
226234 ) -> EvalResult < ' tcx > {
227235 // Check if we can match the frozen "item".
228236 // Not possible on writes!
229237 if self . is_frozen ( ) {
230- if !is_write {
238+ if kind == AccessKind :: Read {
231239 // When we are frozen, we just accept all reads. No harm in this.
232240 // The deref already checked that `Uniq` items are in the stack, and that
233241 // the location is frozen if it should be.
@@ -247,26 +255,41 @@ impl<'tcx> Stack {
247255 ) ) )
248256 }
249257 ( BorStackItem :: Uniq ( itm_t) , Borrow :: Uniq ( bor_t) ) if itm_t == bor_t => {
250- // Found matching unique item.
251- return Ok ( ( ) )
258+ // Found matching unique item. Continue after the match.
252259 }
253- ( BorStackItem :: Shr , _) if !is_write => {
260+ ( BorStackItem :: Shr , _) if kind == AccessKind :: Read => {
254261 // When reading, everything can use a shared item!
255262 // We do not want to do this when writing: Writing to an `&mut`
256263 // should reaffirm its exclusivity (i.e., make sure it is
257- // on top of the stack).
258- return Ok ( ( ) )
264+ // on top of the stack). Continue after the match.
259265 }
260266 ( BorStackItem :: Shr , Borrow :: Shr ( _) ) => {
261- // Found matching shared item.
262- return Ok ( ( ) )
267+ // Found matching shared item. Continue after the match.
263268 }
264269 _ => {
265- // Pop this. This ensures U2.
270+ // Pop this, go on . This ensures U2.
266271 let itm = self . borrows . pop ( ) . unwrap ( ) ;
267272 trace ! ( "access: Popping {:?}" , itm) ;
273+ continue
274+ }
275+ }
276+ // If we got here, we found a matching item. Congratulations!
277+ // However, we are not done yet: If this access is deallocating, we must make sure
278+ // there are no active barriers remaining on the stack.
279+ if kind == AccessKind :: Dealloc {
280+ for & itm in self . borrows . iter ( ) . rev ( ) {
281+ match itm {
282+ BorStackItem :: FnBarrier ( call) if barrier_tracking. is_active ( call) => {
283+ return err ! ( MachineError ( format!(
284+ "Deallocating with active barrier ({})" , call
285+ ) ) )
286+ }
287+ _ => { } ,
288+ }
268289 }
269290 }
291+ // NOW we are done.
292+ return Ok ( ( ) )
270293 }
271294 // If we got here, we did not find our item.
272295 err ! ( MachineError ( format!(
@@ -352,18 +375,16 @@ impl<'tcx> Stacks {
352375 & self ,
353376 ptr : Pointer < Borrow > ,
354377 size : Size ,
355- is_write : bool ,
356- barrier_tracking : & BarrierTracking ,
378+ kind : AccessKind ,
357379 ) -> EvalResult < ' tcx > {
358- trace ! ( "{} access of tag {:?}: {:?}, size {}" ,
359- if is_write { "read" } else { "write" } ,
360- ptr. tag, ptr, size. bytes( ) ) ;
380+ trace ! ( "{:?} access of tag {:?}: {:?}, size {}" , kind, ptr. tag, ptr, size. bytes( ) ) ;
361381 // Even reads can have a side-effect, by invalidating other references.
362382 // This is fundamentally necessary since `&mut` asserts that there
363383 // are no accesses through other references, not even reads.
384+ let barrier_tracking = self . barrier_tracking . borrow ( ) ;
364385 let mut stacks = self . stacks . borrow_mut ( ) ;
365386 for stack in stacks. iter_mut ( ptr. offset , size) {
366- stack. access ( ptr. tag , is_write , barrier_tracking) ?;
387+ stack. access ( ptr. tag , kind , & * barrier_tracking) ?;
367388 }
368389 Ok ( ( ) )
369390 }
@@ -377,16 +398,24 @@ impl<'tcx> Stacks {
377398 mut barrier : Option < CallId > ,
378399 new_bor : Borrow ,
379400 new_kind : RefKind ,
380- barrier_tracking : & BarrierTracking ,
381401 ) -> EvalResult < ' tcx > {
382402 assert_eq ! ( new_bor. is_unique( ) , new_kind == RefKind :: Unique ) ;
383403 trace ! ( "reborrow for tag {:?} to {:?} as {:?}: {:?}, size {}" ,
384404 ptr. tag, new_bor, new_kind, ptr, size. bytes( ) ) ;
385405 if new_kind == RefKind :: Raw {
386406 // No barrier for raw, including `&UnsafeCell`. They can rightfully
387407 // alias with `&mut`.
408+ // FIXME: This means that the `dereferencable` attribute on non-frozen shared
409+ // references is incorrect! They are dereferencable when the function is
410+ // called, but might become non-dereferencable during the coruse of execution.
411+ // Also see [1], [2].
412+ //
413+ // [1]: <https://internals.rust-lang.org/t/
414+ // is-it-possible-to-be-memory-safe-with-deallocated-self/8457/8>,
415+ // [2]: <https://lists.llvm.org/pipermail/llvm-dev/2018-July/124555.html>
388416 barrier = None ;
389417 }
418+ let barrier_tracking = self . barrier_tracking . borrow ( ) ;
390419 let mut stacks = self . stacks . borrow_mut ( ) ;
391420 for stack in stacks. iter_mut ( ptr. offset , size) {
392421 // Access source `ptr`, create new ref.
@@ -410,7 +439,12 @@ impl<'tcx> Stacks {
410439 continue ;
411440 }
412441 // We need to do some actual work.
413- stack. access ( ptr. tag , new_kind == RefKind :: Unique , barrier_tracking) ?;
442+ let access_kind = if new_kind == RefKind :: Unique {
443+ AccessKind :: Write
444+ } else {
445+ AccessKind :: Read
446+ } ;
447+ stack. access ( ptr. tag , access_kind, & * barrier_tracking) ?;
414448 if let Some ( call) = barrier {
415449 stack. barrier ( call) ;
416450 }
@@ -440,7 +474,7 @@ impl AllocationExtra<Borrow, MemoryState> for Stacks {
440474 ptr : Pointer < Borrow > ,
441475 size : Size ,
442476 ) -> EvalResult < ' tcx > {
443- alloc. extra . access ( ptr, size, /*is_write*/ false , & * alloc . extra . barrier_tracking . borrow ( ) )
477+ alloc. extra . access ( ptr, size, AccessKind :: Read )
444478 }
445479
446480 #[ inline( always) ]
@@ -449,7 +483,7 @@ impl AllocationExtra<Borrow, MemoryState> for Stacks {
449483 ptr : Pointer < Borrow > ,
450484 size : Size ,
451485 ) -> EvalResult < ' tcx > {
452- alloc. extra . access ( ptr, size, /*is_write*/ true , & * alloc . extra . barrier_tracking . borrow ( ) )
486+ alloc. extra . access ( ptr, size, AccessKind :: Write )
453487 }
454488
455489 #[ inline( always) ]
@@ -458,9 +492,7 @@ impl AllocationExtra<Borrow, MemoryState> for Stacks {
458492 ptr : Pointer < Borrow > ,
459493 size : Size ,
460494 ) -> EvalResult < ' tcx > {
461- // This is like mutating
462- alloc. extra . access ( ptr, size, /*is_write*/ true , & * alloc. extra . barrier_tracking . borrow ( ) )
463- // FIXME: Error out of there are any barriers?
495+ alloc. extra . access ( ptr, size, AccessKind :: Dealloc )
464496 }
465497}
466498
@@ -627,12 +659,12 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for MiriEvalContext<'a, 'mir, 'tcx> {
627659 // Reference that cares about freezing. We need a frozen-sensitive reborrow.
628660 self . visit_freeze_sensitive ( place, size, |cur_ptr, size, frozen| {
629661 let kind = if frozen { RefKind :: Frozen } else { RefKind :: Raw } ;
630- alloc. extra . reborrow ( cur_ptr, size, barrier, new_bor, kind, & * self . memory ( ) . extra . borrow ( ) )
662+ alloc. extra . reborrow ( cur_ptr, size, barrier, new_bor, kind)
631663 } ) ?;
632664 } else {
633665 // Just treat this as one big chunk.
634666 let kind = if new_bor. is_unique ( ) { RefKind :: Unique } else { RefKind :: Raw } ;
635- alloc. extra . reborrow ( ptr, size, barrier, new_bor, kind, & * self . memory ( ) . extra . borrow ( ) ) ?;
667+ alloc. extra . reborrow ( ptr, size, barrier, new_bor, kind) ?;
636668 }
637669 Ok ( new_ptr)
638670 }
0 commit comments