diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 7c2100fcbe38a..c1daa2fac338e 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -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(()) } @@ -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(()) diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index ebe77a1abfd8b..9855c72197edc 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -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.