Skip to content

Commit

Permalink
StorageLive: refresh storage (instead of UB) when local is already live
Browse files Browse the repository at this point in the history
  • Loading branch information
RalfJung committed Jun 8, 2024
1 parent d8fde50 commit 9b05e15
Show file tree
Hide file tree
Showing 3 changed files with 10 additions and 11 deletions.
2 changes: 0 additions & 2 deletions compiler/rustc_const_eval/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,6 @@ const_eval_division_by_zero =
dividing by zero
const_eval_division_overflow =
overflow in signed division (dividing MIN by -1)
const_eval_double_storage_live =
StorageLive on a local that was already live
const_eval_dyn_call_not_a_method =
`dyn` call trying to call something that is not a method
Expand Down
8 changes: 3 additions & 5 deletions compiler/rustc_const_eval/src/interpret/eval_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1103,11 +1103,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
Operand::Immediate(Immediate::Uninit)
});

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

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

// It is entirely okay for this local to be already dead (at least that's how we currently generate MIR)
// If the local is already dead, this is a NOP.
let old = mem::replace(&mut self.frame_mut().locals[local].value, LocalValue::Dead);
self.deallocate_local(old)?;
Ok(())
Expand Down
11 changes: 7 additions & 4 deletions compiler/rustc_middle/src/mir/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,16 +361,19 @@ pub enum StatementKind<'tcx> {
/// At any point during the execution of a function, each local is either allocated or
/// unallocated. Except as noted below, all locals except function parameters are initially
/// unallocated. `StorageLive` statements cause memory to be allocated for the local while
/// `StorageDead` statements cause the memory to be freed. Using a local in any way (not only
/// reading/writing from it) while it is unallocated is UB.
/// `StorageDead` statements cause the memory to be freed. In other words,
/// `StorageLive`/`StorageDead` act like the heap operations `allocate`/`deallocate`, but for
/// stack-allocated local variables. Using a local in any way (not only reading/writing from it)
/// while it is unallocated is UB.
///
/// Some locals have no `StorageLive` or `StorageDead` statements within the entire MIR body.
/// These locals are implicitly allocated for the full duration of the function. There is a
/// convenience method at `rustc_mir_dataflow::storage::always_storage_live_locals` for
/// computing these locals.
///
/// If the local is already allocated, calling `StorageLive` again is UB. However, for an
/// unallocated local an additional `StorageDead` all is simply a nop.
/// If the local is already allocated, calling `StorageLive` again will implicitly free the
/// local and then allocate fresh uninitilized memory. If a local is already deallocated,
/// calling `StorageDead` again is a NOP.
StorageLive(Local),

/// See `StorageLive` above.
Expand Down

0 comments on commit 9b05e15

Please sign in to comment.