Skip to content

Commit 3de0106

Browse files
committed
Auto merge of #59780 - RalfJung:miri-unsized, r=oli-obk
Miri: unsized locals and by-value dyn traits r? @oli-obk Cc @eddyb Fixes rust-lang/miri#449
2 parents 8509127 + 4d79d39 commit 3de0106

File tree

6 files changed

+192
-153
lines changed

6 files changed

+192
-153
lines changed

src/librustc_mir/interpret/eval_context.rs

+54-56
Original file line numberDiff line numberDiff line change
@@ -108,34 +108,51 @@ 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> {
119+
/// This local is not currently alive, and cannot be used at all.
119120
Dead,
120-
// Mostly for convenience, we re-use the `Operand` type here.
121-
// This is an optimization over just always having a pointer here;
122-
// we can thus avoid doing an allocation when the local just stores
123-
// immediate values *and* never has its address taken.
121+
/// This local is alive but not yet initialized. It can be written to
122+
/// but not read from or its address taken. Locals get initialized on
123+
/// first write because for unsized locals, we do not know their size
124+
/// before that.
125+
Uninitialized,
126+
/// A normal, live local.
127+
/// Mostly for convenience, we re-use the `Operand` type here.
128+
/// This is an optimization over just always having a pointer here;
129+
/// we can thus avoid doing an allocation when the local just stores
130+
/// immediate values *and* never has its address taken.
124131
Live(Operand<Tag, Id>),
125132
}
126133

127-
impl<'tcx, Tag> LocalState<'tcx, Tag> {
128-
pub fn access(&self) -> EvalResult<'tcx, &Operand<Tag>> {
129-
match self.state {
134+
impl<'tcx, Tag: Copy + 'static> LocalState<'tcx, Tag> {
135+
pub fn access(&self) -> EvalResult<'tcx, Operand<Tag>> {
136+
match self.value {
130137
LocalValue::Dead => err!(DeadLocal),
131-
LocalValue::Live(ref val) => Ok(val),
138+
LocalValue::Uninitialized =>
139+
bug!("The type checker should prevent reading from a never-written local"),
140+
LocalValue::Live(val) => Ok(val),
132141
}
133142
}
134143

135-
pub fn access_mut(&mut self) -> EvalResult<'tcx, &mut Operand<Tag>> {
136-
match self.state {
144+
/// Overwrite the local. If the local can be overwritten in place, return a reference
145+
/// to do so; otherwise return the `MemPlace` to consult instead.
146+
pub fn access_mut(
147+
&mut self,
148+
) -> EvalResult<'tcx, Result<&mut LocalValue<Tag>, MemPlace<Tag>>> {
149+
match self.value {
137150
LocalValue::Dead => err!(DeadLocal),
138-
LocalValue::Live(ref mut val) => Ok(val),
151+
LocalValue::Live(Operand::Indirect(mplace)) => Ok(Err(mplace)),
152+
ref mut local @ LocalValue::Live(Operand::Immediate(_)) |
153+
ref mut local @ LocalValue::Uninitialized => {
154+
Ok(Ok(local))
155+
}
139156
}
140157
}
141158
}
@@ -327,6 +344,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tc
327344
let local_ty = self.monomorphize_with_substs(local_ty, frame.instance.substs);
328345
self.layout_of(local_ty)
329346
})?;
347+
// Layouts of locals are requested a lot, so we cache them.
330348
frame.locals[local].layout.set(Some(layout));
331349
Ok(layout)
332350
}
@@ -473,19 +491,15 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tc
473491

474492
// don't allocate at all for trivial constants
475493
if mir.local_decls.len() > 1 {
476-
// We put some marker immediate into the locals that we later want to initialize.
477-
// This can be anything except for LocalValue::Dead -- because *that* is the
478-
// value we use for things that we know are initially dead.
494+
// Locals are initially uninitialized.
479495
let dummy = LocalState {
480-
state: LocalValue::Live(Operand::Immediate(Immediate::Scalar(
481-
ScalarMaybeUndef::Undef,
482-
))),
496+
value: LocalValue::Uninitialized,
483497
layout: Cell::new(None),
484498
};
485499
let mut locals = IndexVec::from_elem(dummy, &mir.local_decls);
486500
// Return place is handled specially by the `eval_place` functions, and the
487501
// entry in `locals` should never be used. Make it dead, to be sure.
488-
locals[mir::RETURN_PLACE].state = LocalValue::Dead;
502+
locals[mir::RETURN_PLACE].value = LocalValue::Dead;
489503
// Now mark those locals as dead that we do not want to initialize
490504
match self.tcx.describe_def(instance.def_id()) {
491505
// statics and constants don't have `Storage*` statements, no need to look for them
@@ -498,29 +512,14 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tc
498512
match stmt.kind {
499513
StorageLive(local) |
500514
StorageDead(local) => {
501-
locals[local].state = LocalValue::Dead;
515+
locals[local].value = LocalValue::Dead;
502516
}
503517
_ => {}
504518
}
505519
}
506520
}
507521
},
508522
}
509-
// Finally, properly initialize all those that still have the dummy value
510-
for (idx, local) in locals.iter_enumerated_mut() {
511-
match local.state {
512-
LocalValue::Live(_) => {
513-
// This needs to be properly initialized.
514-
let ty = self.monomorphize(mir.local_decls[idx].ty)?;
515-
let layout = self.layout_of(ty)?;
516-
local.state = LocalValue::Live(self.uninit_operand(layout)?);
517-
local.layout = Cell::new(Some(layout));
518-
}
519-
LocalValue::Dead => {
520-
// Nothing to do
521-
}
522-
}
523-
}
524523
// done
525524
self.frame_mut().locals = locals;
526525
}
@@ -555,7 +554,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tc
555554
}
556555
// Deallocate all locals that are backed by an allocation.
557556
for local in frame.locals {
558-
self.deallocate_local(local.state)?;
557+
self.deallocate_local(local.value)?;
559558
}
560559
// Validate the return value. Do this after deallocating so that we catch dangling
561560
// references.
@@ -603,10 +602,9 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tc
603602
assert!(local != mir::RETURN_PLACE, "Cannot make return place live");
604603
trace!("{:?} is now live", local);
605604

606-
let layout = self.layout_of_local(self.frame(), local, None)?;
607-
let init = LocalValue::Live(self.uninit_operand(layout)?);
605+
let local_val = LocalValue::Uninitialized;
608606
// StorageLive *always* kills the value that's currently stored
609-
Ok(mem::replace(&mut self.frame_mut().locals[local].state, init))
607+
Ok(mem::replace(&mut self.frame_mut().locals[local].value, local_val))
610608
}
611609

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

618-
mem::replace(&mut self.frame_mut().locals[local].state, LocalValue::Dead)
616+
mem::replace(&mut self.frame_mut().locals[local].value, LocalValue::Dead)
619617
}
620618

621619
pub(super) fn deallocate_local(
@@ -668,31 +666,31 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tc
668666
}
669667
write!(msg, ":").unwrap();
670668

671-
match self.stack[frame].locals[local].access() {
672-
Err(err) => {
673-
if let InterpError::DeadLocal = err.kind {
674-
write!(msg, " is dead").unwrap();
675-
} else {
676-
panic!("Failed to access local: {:?}", err);
677-
}
678-
}
679-
Ok(Operand::Indirect(mplace)) => {
680-
let (ptr, align) = mplace.to_scalar_ptr_align();
681-
match ptr {
669+
match self.stack[frame].locals[local].value {
670+
LocalValue::Dead => write!(msg, " is dead").unwrap(),
671+
LocalValue::Uninitialized => write!(msg, " is uninitialized").unwrap(),
672+
LocalValue::Live(Operand::Indirect(mplace)) => {
673+
match mplace.ptr {
682674
Scalar::Ptr(ptr) => {
683-
write!(msg, " by align({}) ref:", align.bytes()).unwrap();
675+
write!(msg, " by align({}){} ref:",
676+
mplace.align.bytes(),
677+
match mplace.meta {
678+
Some(meta) => format!(" meta({:?})", meta),
679+
None => String::new()
680+
}
681+
).unwrap();
684682
allocs.push(ptr.alloc_id);
685683
}
686684
ptr => write!(msg, " by integral ref: {:?}", ptr).unwrap(),
687685
}
688686
}
689-
Ok(Operand::Immediate(Immediate::Scalar(val))) => {
687+
LocalValue::Live(Operand::Immediate(Immediate::Scalar(val))) => {
690688
write!(msg, " {:?}", val).unwrap();
691689
if let ScalarMaybeUndef::Scalar(Scalar::Ptr(ptr)) = val {
692690
allocs.push(ptr.alloc_id);
693691
}
694692
}
695-
Ok(Operand::Immediate(Immediate::ScalarPair(val1, val2))) => {
693+
LocalValue::Live(Operand::Immediate(Immediate::ScalarPair(val1, val2))) => {
696694
write!(msg, " ({:?}, {:?})", val1, val2).unwrap();
697695
if let ScalarMaybeUndef::Scalar(Scalar::Ptr(ptr)) = val1 {
698696
allocs.push(ptr.alloc_id);

src/librustc_mir/interpret/operand.rs

+8-30
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use rustc::mir::interpret::{
1414
};
1515
use super::{
1616
InterpretCx, Machine,
17-
MemPlace, MPlaceTy, PlaceTy, Place, MemoryKind,
17+
MemPlace, MPlaceTy, PlaceTy, Place,
1818
};
1919
pub use rustc::mir::interpret::ScalarMaybeUndef;
2020

@@ -373,33 +373,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
373373
Ok(str)
374374
}
375375

376-
pub fn uninit_operand(
377-
&mut self,
378-
layout: TyLayout<'tcx>
379-
) -> EvalResult<'tcx, Operand<M::PointerTag>> {
380-
// This decides which types we will use the Immediate optimization for, and hence should
381-
// match what `try_read_immediate` and `eval_place_to_op` support.
382-
if layout.is_zst() {
383-
return Ok(Operand::Immediate(Immediate::Scalar(Scalar::zst().into())));
384-
}
385-
386-
Ok(match layout.abi {
387-
layout::Abi::Scalar(..) =>
388-
Operand::Immediate(Immediate::Scalar(ScalarMaybeUndef::Undef)),
389-
layout::Abi::ScalarPair(..) =>
390-
Operand::Immediate(Immediate::ScalarPair(
391-
ScalarMaybeUndef::Undef,
392-
ScalarMaybeUndef::Undef,
393-
)),
394-
_ => {
395-
trace!("Forcing allocation for local of type {:?}", layout.ty);
396-
Operand::Indirect(
397-
*self.allocate(layout, MemoryKind::Stack)
398-
)
399-
}
400-
})
401-
}
402-
403376
/// Projection functions
404377
pub fn operand_field(
405378
&self,
@@ -486,8 +459,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
486459
layout: Option<TyLayout<'tcx>>,
487460
) -> EvalResult<'tcx, OpTy<'tcx, M::PointerTag>> {
488461
assert_ne!(local, mir::RETURN_PLACE);
489-
let op = *frame.locals[local].access()?;
490462
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+
};
491469
Ok(OpTy { op, layout })
492470
}
493471

@@ -502,7 +480,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
502480
Operand::Indirect(mplace)
503481
}
504482
Place::Local { frame, local } =>
505-
*self.stack[frame].locals[local].access()?
483+
*self.access_local(&self.stack[frame], local, None)?
506484
};
507485
Ok(OpTy { op, layout: place.layout })
508486
}

0 commit comments

Comments
 (0)