Skip to content

Commit 9b05e15

Browse files
committed
StorageLive: refresh storage (instead of UB) when local is already live
1 parent d8fde50 commit 9b05e15

File tree

3 files changed

+10
-11
lines changed

3 files changed

+10
-11
lines changed

compiler/rustc_const_eval/messages.ftl

-2
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,6 @@ const_eval_division_by_zero =
7373
dividing by zero
7474
const_eval_division_overflow =
7575
overflow in signed division (dividing MIN by -1)
76-
const_eval_double_storage_live =
77-
StorageLive on a local that was already live
7876
7977
const_eval_dyn_call_not_a_method =
8078
`dyn` call trying to call something that is not a method

compiler/rustc_const_eval/src/interpret/eval_context.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -1103,11 +1103,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
11031103
Operand::Immediate(Immediate::Uninit)
11041104
});
11051105

1106-
// StorageLive expects the local to be dead, and marks it live.
1106+
// If the local is already live, deallocate its old memory.
11071107
let old = mem::replace(&mut self.frame_mut().locals[local].value, local_val);
1108-
if !matches!(old, LocalValue::Dead) {
1109-
throw_ub_custom!(fluent::const_eval_double_storage_live);
1110-
}
1108+
self.deallocate_local(old)?;
11111109
Ok(())
11121110
}
11131111

@@ -1121,7 +1119,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
11211119
assert!(local != mir::RETURN_PLACE, "Cannot make return place dead");
11221120
trace!("{:?} is now dead", local);
11231121

1124-
// It is entirely okay for this local to be already dead (at least that's how we currently generate MIR)
1122+
// If the local is already dead, this is a NOP.
11251123
let old = mem::replace(&mut self.frame_mut().locals[local].value, LocalValue::Dead);
11261124
self.deallocate_local(old)?;
11271125
Ok(())

compiler/rustc_middle/src/mir/syntax.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -361,16 +361,19 @@ pub enum StatementKind<'tcx> {
361361
/// At any point during the execution of a function, each local is either allocated or
362362
/// unallocated. Except as noted below, all locals except function parameters are initially
363363
/// unallocated. `StorageLive` statements cause memory to be allocated for the local while
364-
/// `StorageDead` statements cause the memory to be freed. Using a local in any way (not only
365-
/// reading/writing from it) while it is unallocated is UB.
364+
/// `StorageDead` statements cause the memory to be freed. In other words,
365+
/// `StorageLive`/`StorageDead` act like the heap operations `allocate`/`deallocate`, but for
366+
/// stack-allocated local variables. Using a local in any way (not only reading/writing from it)
367+
/// while it is unallocated is UB.
366368
///
367369
/// Some locals have no `StorageLive` or `StorageDead` statements within the entire MIR body.
368370
/// These locals are implicitly allocated for the full duration of the function. There is a
369371
/// convenience method at `rustc_mir_dataflow::storage::always_storage_live_locals` for
370372
/// computing these locals.
371373
///
372-
/// If the local is already allocated, calling `StorageLive` again is UB. However, for an
373-
/// unallocated local an additional `StorageDead` all is simply a nop.
374+
/// If the local is already allocated, calling `StorageLive` again will implicitly free the
375+
/// local and then allocate fresh uninitilized memory. If a local is already deallocated,
376+
/// calling `StorageDead` again is a NOP.
374377
StorageLive(Local),
375378

376379
/// See `StorageLive` above.

0 commit comments

Comments
 (0)