Skip to content

Commit fbea3e5

Browse files
committed
Auto merge of #1330 - RalfJung:retag-return-place, r=RalfJung
retag return place @eddyb suggested that return places should be treated like unique references for Stacked Borrows. That is implemented by this patch, but it is unfortunately quite the hack because otherwise we are retagging *references*, not places. @eddyb does this roughly correspond to what you had in mind? (Except for whatever it is you think should happen with argument passing, which is a much bigger issue.) Also, do you think there is any way we can *test* this? Needs rust-lang/rust#71100 to land.
2 parents 669191b + 3548dcf commit fbea3e5

File tree

3 files changed

+60
-15
lines changed

3 files changed

+60
-15
lines changed

rust-version

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
47f49695dfb4fe9e584239fdc59c771887148a57
1+
df768c5c8fcb361c4dc94b4c776d6a78c12862e1

src/machine.rs

+21-9
Original file line numberDiff line numberDiff line change
@@ -481,30 +481,42 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'tcx> {
481481
kind: mir::RetagKind,
482482
place: PlaceTy<'tcx, Tag>,
483483
) -> InterpResult<'tcx> {
484-
if ecx.memory.extra.stacked_borrows.is_none() {
485-
// No tracking.
486-
Ok(())
487-
} else {
484+
if ecx.memory.extra.stacked_borrows.is_some() {
488485
ecx.retag(kind, place)
486+
} else {
487+
Ok(())
489488
}
490489
}
491490

492491
#[inline(always)]
493-
fn stack_push(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx, FrameData<'tcx>> {
492+
fn init_frame_extra(
493+
ecx: &mut InterpCx<'mir, 'tcx, Self>,
494+
frame: Frame<'mir, 'tcx, Tag>,
495+
) -> InterpResult<'tcx, Frame<'mir, 'tcx, Tag, FrameData<'tcx>>> {
494496
let stacked_borrows = ecx.memory.extra.stacked_borrows.as_ref();
495497
let call_id = stacked_borrows.map_or(NonZeroU64::new(1).unwrap(), |stacked_borrows| {
496498
stacked_borrows.borrow_mut().new_call()
497499
});
498-
Ok(FrameData { call_id, catch_unwind: None })
500+
let extra = FrameData { call_id, catch_unwind: None };
501+
Ok(frame.with_extra(extra))
502+
}
503+
504+
#[inline(always)]
505+
fn after_stack_push(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
506+
if ecx.memory.extra.stacked_borrows.is_some() {
507+
ecx.retag_return_place()
508+
} else {
509+
Ok(())
510+
}
499511
}
500512

501513
#[inline(always)]
502-
fn stack_pop(
514+
fn after_stack_pop(
503515
ecx: &mut InterpCx<'mir, 'tcx, Self>,
504-
extra: FrameData<'tcx>,
516+
frame: Frame<'mir, 'tcx, Tag, FrameData<'tcx>>,
505517
unwinding: bool,
506518
) -> InterpResult<'tcx, StackPopJump> {
507-
ecx.handle_stack_pop(extra, unwinding)
519+
ecx.handle_stack_pop(frame.extra, unwinding)
508520
}
509521

510522
#[inline(always)]

src/stacked_borrows.rs

+38-5
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use log::trace;
1111
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
1212
use rustc_middle::mir::RetagKind;
1313
use rustc_middle::ty;
14-
use rustc_target::abi::Size;
14+
use rustc_target::abi::{LayoutOf, Size};
1515
use rustc_hir::Mutability;
1616

1717
use crate::*;
@@ -569,7 +569,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
569569
val: ImmTy<'tcx, Tag>,
570570
kind: RefKind,
571571
protect: bool,
572-
) -> InterpResult<'tcx, Immediate<Tag>> {
572+
) -> InterpResult<'tcx, ImmTy<'tcx, Tag>> {
573573
let this = self.eval_context_mut();
574574
// We want a place for where the ptr *points to*, so we get one.
575575
let place = this.ref_to_mplace(val)?;
@@ -582,7 +582,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
582582
let place = this.mplace_access_checked(place)?;
583583
if size == Size::ZERO {
584584
// Nothing to do for ZSTs.
585-
return Ok(*val);
585+
return Ok(val);
586586
}
587587

588588
// Compute new borrow.
@@ -603,7 +603,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
603603
let new_place = place.replace_tag(new_tag);
604604

605605
// Return new pointer.
606-
Ok(new_place.to_ref())
606+
Ok(ImmTy::from_immediate(new_place.to_ref(), val.layout))
607607
}
608608
}
609609

@@ -640,9 +640,42 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
640640
// Fast path.
641641
let val = this.read_immediate(this.place_to_op(place)?)?;
642642
let val = this.retag_reference(val, mutbl, protector)?;
643-
this.write_immediate(val, place)?;
643+
this.write_immediate(*val, place)?;
644644
}
645645

646646
Ok(())
647647
}
648+
649+
/// After a stack frame got pushed, retag the return place so that we are sure
650+
/// it does not alias with anything.
651+
///
652+
/// This is a HACK because there is nothing in MIR that would make the retag
653+
/// explicit. Also see https://github.com/rust-lang/rust/issues/71117.
654+
fn retag_return_place(&mut self) -> InterpResult<'tcx> {
655+
let this = self.eval_context_mut();
656+
let return_place = if let Some(return_place) = this.frame_mut().return_place {
657+
return_place
658+
} else {
659+
// No return place, nothing to do.
660+
return Ok(());
661+
};
662+
if return_place.layout.is_zst() {
663+
// There may not be any memory here, nothing to do.
664+
return Ok(());
665+
}
666+
// We need this to be in-memory to use tagged pointers.
667+
let return_place = this.force_allocation(return_place)?;
668+
669+
// We have to turn the place into a pointer to use the existing code.
670+
// (The pointer type does not matter, so we use a raw pointer.)
671+
let ptr_layout = this.layout_of(this.tcx.mk_mut_ptr(return_place.layout.ty))?;
672+
let val = ImmTy::from_immediate(return_place.to_ref(), ptr_layout);
673+
// Reborrow it.
674+
let val = this.retag_reference(val, RefKind::Unique { two_phase: false }, /*protector*/ true)?;
675+
// And use reborrowed pointer for return place.
676+
let return_place = this.ref_to_mplace(val)?;
677+
this.frame_mut().return_place = Some(return_place.into());
678+
679+
Ok(())
680+
}
648681
}

0 commit comments

Comments
 (0)