Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 4eaf535

Browse files
Treat RETURN_PLACE as a normal Local
Copy its value to the `return_place` upon leaving a call frame
1 parent 8ce3f84 commit 4eaf535

File tree

7 files changed

+42
-70
lines changed

7 files changed

+42
-70
lines changed

src/librustc_middle/mir/interpret/error.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -361,8 +361,6 @@ pub enum UndefinedBehaviorInfo {
361361
InvalidUndefBytes(Option<Pointer>),
362362
/// Working with a local that is not currently live.
363363
DeadLocal,
364-
/// Trying to read from the return place of a function.
365-
ReadFromReturnPlace,
366364
}
367365

368366
impl fmt::Debug for UndefinedBehaviorInfo {
@@ -424,7 +422,6 @@ impl fmt::Debug for UndefinedBehaviorInfo {
424422
"using uninitialized data, but this operation requires initialized memory"
425423
),
426424
DeadLocal => write!(f, "accessing a dead local variable"),
427-
ReadFromReturnPlace => write!(f, "reading from return place"),
428425
}
429426
}
430427
}

src/librustc_mir/interpret/eval_context.rs

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -623,35 +623,30 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
623623
let frame = M::init_frame_extra(self, pre_frame)?;
624624
self.stack_mut().push(frame);
625625

626-
// don't allocate at all for trivial constants
627-
if body.local_decls.len() > 1 {
628-
// Locals are initially uninitialized.
629-
let dummy = LocalState { value: LocalValue::Uninitialized, layout: Cell::new(None) };
630-
let mut locals = IndexVec::from_elem(dummy, &body.local_decls);
631-
// Return place is handled specially by the `eval_place` functions, and the
632-
// entry in `locals` should never be used. Make it dead, to be sure.
633-
locals[mir::RETURN_PLACE].value = LocalValue::Dead;
634-
// Now mark those locals as dead that we do not want to initialize
635-
match self.tcx.def_kind(instance.def_id()) {
636-
// statics and constants don't have `Storage*` statements, no need to look for them
637-
//
638-
// FIXME: The above is likely untrue. See
639-
// <https://github.com/rust-lang/rust/pull/70004#issuecomment-602022110>. Is it
640-
// okay to ignore `StorageDead`/`StorageLive` annotations during CTFE?
641-
Some(DefKind::Static | DefKind::Const | DefKind::AssocConst) => {}
642-
_ => {
643-
// Mark locals that use `Storage*` annotations as dead on function entry.
644-
let always_live = AlwaysLiveLocals::new(self.body());
645-
for local in locals.indices() {
646-
if !always_live.contains(local) {
647-
locals[local].value = LocalValue::Dead;
648-
}
626+
// Locals are initially uninitialized.
627+
let dummy = LocalState { value: LocalValue::Uninitialized, layout: Cell::new(None) };
628+
let mut locals = IndexVec::from_elem(dummy, &body.local_decls);
629+
630+
// Now mark those locals as dead that we do not want to initialize
631+
match self.tcx.def_kind(instance.def_id()) {
632+
// statics and constants don't have `Storage*` statements, no need to look for them
633+
//
634+
// FIXME: The above is likely untrue. See
635+
// <https://github.com/rust-lang/rust/pull/70004#issuecomment-602022110>. Is it
636+
// okay to ignore `StorageDead`/`StorageLive` annotations during CTFE?
637+
Some(DefKind::Static | DefKind::Const | DefKind::AssocConst) => {}
638+
_ => {
639+
// Mark locals that use `Storage*` annotations as dead on function entry.
640+
let always_live = AlwaysLiveLocals::new(self.body());
641+
for local in locals.indices() {
642+
if !always_live.contains(local) {
643+
locals[local].value = LocalValue::Dead;
649644
}
650645
}
651646
}
652-
// done
653-
self.frame_mut().locals = locals;
654647
}
648+
// done
649+
self.frame_mut().locals = locals;
655650

656651
M::after_stack_push(self)?;
657652
info!("ENTERING({}) {}", self.frame_idx(), self.frame().instance);
@@ -729,6 +724,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
729724
let frame =
730725
self.stack_mut().pop().expect("tried to pop a stack frame, but there were none");
731726

727+
if let Some(return_place) = frame.return_place {
728+
// Copy the return value to the caller's stack frame.
729+
let op = self.access_local(&frame, mir::RETURN_PLACE, None)?;
730+
self.copy_op(op, return_place)?;
731+
}
732+
732733
// Now where do we jump next?
733734

734735
// Usually we want to clean up (deallocate locals), but in a few rare cases we don't.

src/librustc_mir/interpret/operand.rs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
419419
local: mir::Local,
420420
layout: Option<TyAndLayout<'tcx>>,
421421
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
422-
assert_ne!(local, mir::RETURN_PLACE);
423422
let layout = self.layout_of_local(frame, local, layout)?;
424423
let op = if layout.is_zst() {
425424
// Do not read from ZST, they might not be initialized
@@ -454,15 +453,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
454453
place: mir::Place<'tcx>,
455454
layout: Option<TyAndLayout<'tcx>>,
456455
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
457-
let base_op = match place.local {
458-
mir::RETURN_PLACE => throw_ub!(ReadFromReturnPlace),
459-
local => {
460-
// Do not use the layout passed in as argument if the base we are looking at
461-
// here is not the entire place.
462-
let layout = if place.projection.is_empty() { layout } else { None };
463-
464-
self.access_local(self.frame(), local, layout)?
465-
}
456+
let base_op = {
457+
// Do not use the layout passed in as argument if the base we are looking at
458+
// here is not the entire place.
459+
let layout = if place.projection.is_empty() { layout } else { None };
460+
461+
self.access_local(self.frame(), place.local, layout)?
466462
};
467463

468464
let op = place

src/librustc_mir/interpret/place.rs

Lines changed: 8 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,10 @@ impl<Tag: ::std::fmt::Debug> Place<Tag> {
276276
}
277277

278278
impl<'tcx, Tag: ::std::fmt::Debug> PlaceTy<'tcx, Tag> {
279+
pub fn null(cx: &impl HasDataLayout, layout: TyAndLayout<'tcx>) -> Self {
280+
Self { place: Place::null(cx), layout }
281+
}
282+
279283
#[inline]
280284
pub fn assert_mem_place(self) -> MPlaceTy<'tcx, Tag> {
281285
MPlaceTy { mplace: self.place.assert_mem_place(), layout: self.layout }
@@ -636,35 +640,10 @@ where
636640
&mut self,
637641
place: mir::Place<'tcx>,
638642
) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
639-
let mut place_ty = match place.local {
640-
mir::RETURN_PLACE => {
641-
// `return_place` has the *caller* layout, but we want to use our
642-
// `layout to verify our assumption. The caller will validate
643-
// their layout on return.
644-
PlaceTy {
645-
place: match self.frame().return_place {
646-
Some(p) => *p,
647-
// Even if we don't have a return place, we sometimes need to
648-
// create this place, but any attempt to read from / write to it
649-
// (even a ZST read/write) needs to error, so let us make this
650-
// a NULL place.
651-
//
652-
// FIXME: Ideally we'd make sure that the place projections also
653-
// bail out.
654-
None => Place::null(&*self),
655-
},
656-
layout: self.layout_of(
657-
self.subst_from_current_frame_and_normalize_erasing_regions(
658-
self.frame().body.return_ty(),
659-
),
660-
)?,
661-
}
662-
}
663-
local => PlaceTy {
664-
// This works even for dead/uninitialized locals; we check further when writing
665-
place: Place::Local { frame: self.frame_idx(), local },
666-
layout: self.layout_of_local(self.frame(), local, None)?,
667-
},
643+
let mut place_ty = PlaceTy {
644+
// This works even for dead/uninitialized locals; we check further when writing
645+
place: Place::Local { frame: self.frame_idx(), local: place.local },
646+
layout: self.layout_of_local(self.frame(), place.local, None)?,
668647
};
669648

670649
for elem in place.projection.iter() {

src/librustc_mir/interpret/terminator.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
1919
use rustc_middle::mir::TerminatorKind::*;
2020
match terminator.kind {
2121
Return => {
22-
self.frame().return_place.map(|r| self.dump_place(*r));
2322
self.pop_stack_frame(/* unwinding */ false)?
2423
}
2524

src/test/ui/consts/const-eval/ub-nonnull.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ LL | / const OUT_OF_BOUNDS_PTR: NonNull<u8> = { unsafe {
1313
LL | | let ptr: &[u8; 256] = mem::transmute(&0u8); // &0 gets promoted so it does not dangle
1414
LL | | // Use address-of-element for pointer arithmetic. This could wrap around to NULL!
1515
LL | | let out_of_bounds_ptr = &ptr[255];
16-
| | ^^^^^^^^^ Memory access failed: pointer must be in-bounds at offset 256, but is outside bounds of alloc8 which has size 1
16+
| | ^^^^^^^^^ Memory access failed: pointer must be in-bounds at offset 256, but is outside bounds of alloc11 which has size 1
1717
LL | | mem::transmute(out_of_bounds_ptr)
1818
LL | | } };
1919
| |____-

src/test/ui/consts/miri_unleashed/mutable_const.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ LL | / const MUTATING_BEHIND_RAW: () = {
1111
LL | | // Test that `MUTABLE_BEHIND_RAW` is actually immutable, by doing this at const time.
1212
LL | | unsafe {
1313
LL | | *MUTABLE_BEHIND_RAW = 99
14-
| | ^^^^^^^^^^^^^^^^^^^^^^^^ writing to alloc1 which is read-only
14+
| | ^^^^^^^^^^^^^^^^^^^^^^^^ writing to alloc2 which is read-only
1515
LL | | }
1616
LL | | };
1717
| |__-

0 commit comments

Comments
 (0)