Skip to content

Commit 4d79d39

Browse files
committed
avoid reading from ZST locals
1 parent 5731945 commit 4d79d39

File tree

3 files changed

+29
-56
lines changed

3 files changed

+29
-56
lines changed

src/librustc_mir/interpret/eval_context.rs

+19-51
Original file line numberDiff line numberDiff line change
@@ -108,13 +108,13 @@ pub enum StackPopCleanup {
108108
/// State of a local variable including a memoized layout
109109
#[derive(Clone, PartialEq, Eq)]
110110
pub struct LocalState<'tcx, Tag=(), Id=AllocId> {
111-
pub state: LocalValue<Tag, Id>,
111+
pub value: LocalValue<Tag, Id>,
112112
/// Don't modify if `Some`, this is only used to prevent computing the layout twice
113113
pub layout: Cell<Option<TyLayout<'tcx>>>,
114114
}
115115

116-
/// State of a local variable
117-
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
116+
/// Current value of a local variable
117+
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
118118
pub enum LocalValue<Tag=(), Id=AllocId> {
119119
/// This local is not currently alive, and cannot be used at all.
120120
Dead,
@@ -131,27 +131,13 @@ pub enum LocalValue<Tag=(), Id=AllocId> {
131131
Live(Operand<Tag, Id>),
132132
}
133133

134-
impl<Tag: Copy> LocalValue<Tag> {
135-
/// The initial value of a local: ZST get "initialized" because they can be read from without
136-
/// ever having been written to.
137-
fn uninit_local(
138-
layout: TyLayout<'_>
139-
) -> LocalValue<Tag> {
140-
// FIXME: Can we avoid this ZST special case? That would likely require MIR
141-
// generation changes.
142-
if layout.is_zst() {
143-
LocalValue::Live(Operand::Immediate(Immediate::Scalar(Scalar::zst().into())))
144-
} else {
145-
LocalValue::Uninitialized
146-
}
147-
}
148-
}
149-
150-
impl<'tcx, Tag: Copy> LocalState<'tcx, Tag> {
151-
pub fn access(&self) -> EvalResult<'tcx, &Operand<Tag>> {
152-
match self.state {
153-
LocalValue::Dead | LocalValue::Uninitialized => err!(DeadLocal),
154-
LocalValue::Live(ref val) => Ok(val),
134+
impl<'tcx, Tag: Copy + 'static> LocalState<'tcx, Tag> {
135+
pub fn access(&self) -> EvalResult<'tcx, Operand<Tag>> {
136+
match self.value {
137+
LocalValue::Dead => err!(DeadLocal),
138+
LocalValue::Uninitialized =>
139+
bug!("The type checker should prevent reading from a never-written local"),
140+
LocalValue::Live(val) => Ok(val),
155141
}
156142
}
157143

@@ -160,7 +146,7 @@ impl<'tcx, Tag: Copy> LocalState<'tcx, Tag> {
160146
pub fn access_mut(
161147
&mut self,
162148
) -> EvalResult<'tcx, Result<&mut LocalValue<Tag>, MemPlace<Tag>>> {
163-
match self.state {
149+
match self.value {
164150
LocalValue::Dead => err!(DeadLocal),
165151
LocalValue::Live(Operand::Indirect(mplace)) => Ok(Err(mplace)),
166152
ref mut local @ LocalValue::Live(Operand::Immediate(_)) |
@@ -507,13 +493,13 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tc
507493
if mir.local_decls.len() > 1 {
508494
// Locals are initially uninitialized.
509495
let dummy = LocalState {
510-
state: LocalValue::Uninitialized,
496+
value: LocalValue::Uninitialized,
511497
layout: Cell::new(None),
512498
};
513499
let mut locals = IndexVec::from_elem(dummy, &mir.local_decls);
514500
// Return place is handled specially by the `eval_place` functions, and the
515501
// entry in `locals` should never be used. Make it dead, to be sure.
516-
locals[mir::RETURN_PLACE].state = LocalValue::Dead;
502+
locals[mir::RETURN_PLACE].value = LocalValue::Dead;
517503
// Now mark those locals as dead that we do not want to initialize
518504
match self.tcx.describe_def(instance.def_id()) {
519505
// statics and constants don't have `Storage*` statements, no need to look for them
@@ -526,31 +512,14 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tc
526512
match stmt.kind {
527513
StorageLive(local) |
528514
StorageDead(local) => {
529-
locals[local].state = LocalValue::Dead;
515+
locals[local].value = LocalValue::Dead;
530516
}
531517
_ => {}
532518
}
533519
}
534520
}
535521
},
536522
}
537-
// The remaining locals are uninitialized, fill them with `uninit_local`.
538-
// (For ZST this is not a NOP.)
539-
for (idx, local) in locals.iter_enumerated_mut() {
540-
match local.state {
541-
LocalValue::Uninitialized => {
542-
// This needs to be properly initialized.
543-
let ty = self.monomorphize(mir.local_decls[idx].ty)?;
544-
let layout = self.layout_of(ty)?;
545-
local.state = LocalValue::uninit_local(layout);
546-
local.layout = Cell::new(Some(layout));
547-
}
548-
LocalValue::Dead => {
549-
// Nothing to do
550-
}
551-
LocalValue::Live(_) => bug!("Locals cannot be live yet"),
552-
}
553-
}
554523
// done
555524
self.frame_mut().locals = locals;
556525
}
@@ -585,7 +554,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tc
585554
}
586555
// Deallocate all locals that are backed by an allocation.
587556
for local in frame.locals {
588-
self.deallocate_local(local.state)?;
557+
self.deallocate_local(local.value)?;
589558
}
590559
// Validate the return value. Do this after deallocating so that we catch dangling
591560
// references.
@@ -633,10 +602,9 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tc
633602
assert!(local != mir::RETURN_PLACE, "Cannot make return place live");
634603
trace!("{:?} is now live", local);
635604

636-
let layout = self.layout_of_local(self.frame(), local, None)?;
637-
let local_val = LocalValue::uninit_local(layout);
605+
let local_val = LocalValue::Uninitialized;
638606
// StorageLive *always* kills the value that's currently stored
639-
Ok(mem::replace(&mut self.frame_mut().locals[local].state, local_val))
607+
Ok(mem::replace(&mut self.frame_mut().locals[local].value, local_val))
640608
}
641609

642610
/// Returns the old value of the local.
@@ -645,7 +613,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tc
645613
assert!(local != mir::RETURN_PLACE, "Cannot make return place dead");
646614
trace!("{:?} is now dead", local);
647615

648-
mem::replace(&mut self.frame_mut().locals[local].state, LocalValue::Dead)
616+
mem::replace(&mut self.frame_mut().locals[local].value, LocalValue::Dead)
649617
}
650618

651619
pub(super) fn deallocate_local(
@@ -698,7 +666,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tc
698666
}
699667
write!(msg, ":").unwrap();
700668

701-
match self.stack[frame].locals[local].state {
669+
match self.stack[frame].locals[local].value {
702670
LocalValue::Dead => write!(msg, " is dead").unwrap(),
703671
LocalValue::Uninitialized => write!(msg, " is uninitialized").unwrap(),
704672
LocalValue::Live(Operand::Indirect(mplace)) => {

src/librustc_mir/interpret/operand.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -459,8 +459,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
459459
layout: Option<TyLayout<'tcx>>,
460460
) -> EvalResult<'tcx, OpTy<'tcx, M::PointerTag>> {
461461
assert_ne!(local, mir::RETURN_PLACE);
462-
let op = *frame.locals[local].access()?;
463462
let layout = self.layout_of_local(frame, local, layout)?;
463+
let op = if layout.is_zst() {
464+
// Do not read from ZST, they might not be initialized
465+
Operand::Immediate(Immediate::Scalar(Scalar::zst().into()))
466+
} else {
467+
frame.locals[local].access()?
468+
};
464469
Ok(OpTy { op, layout })
465470
}
466471

@@ -475,7 +480,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
475480
Operand::Indirect(mplace)
476481
}
477482
Place::Local { frame, local } =>
478-
*self.stack[frame].locals[local].access()?
483+
*self.access_local(&self.stack[frame], local, None)?
479484
};
480485
Ok(OpTy { op, layout: place.layout })
481486
}

src/librustc_mir/interpret/snapshot.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -363,13 +363,13 @@ impl<'a, 'tcx, Ctx> Snapshot<'a, Ctx> for &'a LocalState<'tcx>
363363
type Item = LocalValue<(), AllocIdSnapshot<'a>>;
364364

365365
fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
366-
let LocalState { state, layout: _ } = self;
367-
state.snapshot(ctx)
366+
let LocalState { value, layout: _ } = self;
367+
value.snapshot(ctx)
368368
}
369369
}
370370

371371
impl_stable_hash_for!(struct LocalState<'tcx> {
372-
state,
372+
value,
373373
layout -> _,
374374
});
375375

0 commit comments

Comments
 (0)