mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-14 02:49:40 +00:00
also step through promoteds, constants and statics
This commit is contained in:
parent
5211178377
commit
6ac64f19af
@ -20,6 +20,8 @@ use error::{EvalError, EvalResult};
|
||||
use memory::{Memory, Pointer};
|
||||
use primval::{self, PrimVal};
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
mod stepper;
|
||||
|
||||
struct GlobalEvalContext<'a, 'tcx: 'a> {
|
||||
@ -45,6 +47,9 @@ struct GlobalEvalContext<'a, 'tcx: 'a> {
|
||||
/// * Function DefIds and Substs to print proper substituted function names.
|
||||
/// * Spans pointing to specific function calls in the source.
|
||||
name_stack: Vec<(DefId, &'tcx Substs<'tcx>, codemap::Span)>,
|
||||
|
||||
/// Precomputed statics and constants
|
||||
statics: DefIdMap<Pointer>,
|
||||
}
|
||||
|
||||
struct FnEvalContext<'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> {
|
||||
@ -88,6 +93,9 @@ struct Frame<'a, 'tcx: 'a> {
|
||||
|
||||
/// The offset of the first temporary in `self.locals`.
|
||||
temp_offset: usize,
|
||||
|
||||
/// List of precomputed promoted constants
|
||||
promoted: HashMap<usize, Pointer>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
@ -122,6 +130,13 @@ enum TerminatorTarget {
|
||||
Return,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
||||
enum ConstantId {
|
||||
Promoted { index: usize },
|
||||
Static { def_id: DefId },
|
||||
}
|
||||
|
||||
|
||||
impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> {
|
||||
fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>) -> Self {
|
||||
GlobalEvalContext {
|
||||
@ -135,10 +150,11 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> {
|
||||
.expect("Session::target::uint_type was usize")/8),
|
||||
substs_stack: Vec::new(),
|
||||
name_stack: Vec::new(),
|
||||
statics: DefIdMap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn call(&mut self, mir: &mir::Mir<'tcx>) -> EvalResult<Option<Pointer>> {
|
||||
fn call(&mut self, mir: &mir::Mir<'tcx>, def_id: DefId) -> EvalResult<Option<Pointer>> {
|
||||
let mut nested_fecx = FnEvalContext::new(self);
|
||||
|
||||
let return_ptr = match mir.return_ty {
|
||||
@ -150,6 +166,7 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> {
|
||||
};
|
||||
|
||||
let substs = nested_fecx.substs();
|
||||
nested_fecx.name_stack.push((def_id, substs, mir.span));
|
||||
nested_fecx.push_stack_frame(CachedMir::Ref(mir), substs, return_ptr);
|
||||
nested_fecx.run()?;
|
||||
Ok(return_ptr)
|
||||
@ -193,9 +210,9 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
|
||||
|
||||
loop {
|
||||
match stepper.step()? {
|
||||
Assignment(statement) => trace!("{:?}", statement),
|
||||
Terminator(terminator) => {
|
||||
trace!("{:?}", terminator.kind);
|
||||
Assignment => trace!("{:?}", stepper.stmt()),
|
||||
Terminator => {
|
||||
trace!("{:?}", stepper.term().kind);
|
||||
continue 'outer;
|
||||
},
|
||||
Done => return Ok(()),
|
||||
@ -230,6 +247,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
|
||||
locals: locals,
|
||||
var_offset: num_args,
|
||||
temp_offset: num_args + num_vars,
|
||||
promoted: HashMap::new(),
|
||||
});
|
||||
}
|
||||
|
||||
@ -983,13 +1001,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
|
||||
match *literal {
|
||||
Value { ref value } => Ok(self.const_to_ptr(value)?),
|
||||
Item { .. } => Err(EvalError::Unimplemented(format!("literal items (e.g. mentions of function items) are unimplemented"))),
|
||||
Promoted { index } => {
|
||||
// TODO(solson): Mark constants and statics as read-only and cache their
|
||||
// values.
|
||||
let current_mir = self.mir();
|
||||
let mir = ¤t_mir.promoted[index];
|
||||
self.gecx.call(mir).map(Option::unwrap)
|
||||
}
|
||||
Promoted { index } => Ok(*self.frame().promoted.get(&index).expect("a promoted constant hasn't been precomputed")),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1004,11 +1016,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
|
||||
Var(i) => self.frame().locals[self.frame().var_offset + i as usize],
|
||||
Temp(i) => self.frame().locals[self.frame().temp_offset + i as usize],
|
||||
|
||||
Static(def_id) => {
|
||||
// TODO(solson): Mark constants and statics as read-only and cache their values.
|
||||
let mir = self.load_mir(def_id);
|
||||
self.gecx.call(&mir)?.unwrap()
|
||||
}
|
||||
Static(def_id) => *self.gecx.statics.get(&def_id).expect("static should have been cached"),
|
||||
|
||||
Projection(ref proj) => {
|
||||
let base = self.eval_lvalue(&proj.base)?;
|
||||
@ -1412,7 +1420,7 @@ pub fn interpret_start_points<'a, 'tcx>(
|
||||
debug!("Interpreting: {}", item.name);
|
||||
|
||||
let mut gecx = GlobalEvalContext::new(tcx, mir_map);
|
||||
match gecx.call(mir) {
|
||||
match gecx.call(mir, tcx.map.local_def_id(id)) {
|
||||
Ok(Some(return_ptr)) => if log_enabled!(::log::LogLevel::Debug) {
|
||||
gecx.memory.dump(return_ptr.alloc_id);
|
||||
},
|
||||
|
@ -2,49 +2,61 @@ use super::{
|
||||
FnEvalContext,
|
||||
CachedMir,
|
||||
TerminatorTarget,
|
||||
ConstantId,
|
||||
};
|
||||
use error::EvalResult;
|
||||
use rustc::mir::repr as mir;
|
||||
use rustc::ty::{self, subst};
|
||||
use rustc::mir::visit::Visitor;
|
||||
use syntax::codemap::Span;
|
||||
use memory::Pointer;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub enum Event<'a, 'tcx: 'a> {
|
||||
Assignment(&'a mir::Statement<'tcx>),
|
||||
Terminator(&'a mir::Terminator<'tcx>),
|
||||
pub enum Event {
|
||||
Assignment,
|
||||
Terminator,
|
||||
Done,
|
||||
}
|
||||
|
||||
pub struct Stepper<'fncx, 'a: 'fncx, 'b: 'a + 'mir, 'mir: 'fncx, 'tcx: 'b>{
|
||||
fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>,
|
||||
block: mir::BasicBlock,
|
||||
stmt: usize,
|
||||
// a stack of statement positions
|
||||
stmt: Vec<usize>,
|
||||
mir: CachedMir<'mir, 'tcx>,
|
||||
process: fn (&mut Stepper<'fncx, 'a, 'b, 'mir, 'tcx>) -> EvalResult<()>,
|
||||
// a stack of constants
|
||||
constants: Vec<Vec<(ConstantId, Span)>>,
|
||||
}
|
||||
|
||||
impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx> {
|
||||
pub(super) fn new(fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>) -> Self {
|
||||
Stepper {
|
||||
let mut stepper = Stepper {
|
||||
block: fncx.frame().next_block,
|
||||
mir: fncx.mir(),
|
||||
fncx: fncx,
|
||||
stmt: 0,
|
||||
stmt: vec![0],
|
||||
process: Self::dummy,
|
||||
}
|
||||
constants: Vec::new(),
|
||||
};
|
||||
stepper.extract_constants();
|
||||
stepper
|
||||
}
|
||||
|
||||
fn dummy(&mut self) -> EvalResult<()> { Ok(()) }
|
||||
|
||||
fn statement(&mut self) -> EvalResult<()> {
|
||||
let block_data = self.mir.basic_block_data(self.block);
|
||||
let stmt = &block_data.statements[self.stmt];
|
||||
let stmt = &block_data.statements[*self.stmt.last().unwrap()];
|
||||
let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind;
|
||||
let result = self.fncx.eval_assignment(lvalue, rvalue);
|
||||
self.fncx.maybe_report(stmt.span, result)?;
|
||||
self.stmt += 1;
|
||||
*self.stmt.last_mut().unwrap() += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn terminator(&mut self) -> EvalResult<()> {
|
||||
self.stmt = 0;
|
||||
*self.stmt.last_mut().unwrap() = 0;
|
||||
let term = {
|
||||
let block_data = self.mir.basic_block_data(self.block);
|
||||
let terminator = block_data.terminator();
|
||||
@ -58,6 +70,9 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx
|
||||
TerminatorTarget::Return => {
|
||||
self.fncx.pop_stack_frame();
|
||||
self.fncx.name_stack.pop();
|
||||
self.stmt.pop();
|
||||
assert!(self.constants.last().unwrap().is_empty());
|
||||
self.constants.pop();
|
||||
if !self.fncx.stack.is_empty() {
|
||||
self.block = self.fncx.frame().next_block;
|
||||
self.mir = self.fncx.mir();
|
||||
@ -66,12 +81,24 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx
|
||||
TerminatorTarget::Call => {
|
||||
self.block = self.fncx.frame().next_block;
|
||||
self.mir = self.fncx.mir();
|
||||
self.stmt.push(0);
|
||||
self.extract_constants();
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn step<'step>(&'step mut self) -> EvalResult<Event<'step, 'tcx>> {
|
||||
fn alloc(&mut self, ty: ty::FnOutput<'tcx>) -> Pointer {
|
||||
match ty {
|
||||
ty::FnConverging(ty) => {
|
||||
let size = self.fncx.type_size(ty);
|
||||
self.fncx.memory.allocate(size)
|
||||
}
|
||||
ty::FnDiverging => panic!("there's no such thing as an unreachable static"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn step(&mut self) -> EvalResult<Event> {
|
||||
(self.process)(self)?;
|
||||
|
||||
if self.fncx.stack.is_empty() {
|
||||
@ -80,18 +107,97 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx
|
||||
return Ok(Event::Done);
|
||||
}
|
||||
|
||||
match self.constants.last_mut().unwrap().pop() {
|
||||
Some((ConstantId::Promoted { index }, span)) => {
|
||||
trace!("adding promoted constant {}", index);
|
||||
let mir = self.mir.promoted[index].clone();
|
||||
let return_ptr = self.alloc(mir.return_ty);
|
||||
self.fncx.frame_mut().promoted.insert(index, return_ptr);
|
||||
let substs = self.fncx.substs();
|
||||
// FIXME: somehow encode that this is a promoted constant's frame
|
||||
println!("{}, {}, {}", self.fncx.stack.len(), self.fncx.name_stack.len(), self.fncx.substs_stack.len());
|
||||
let def_id = self.fncx.name_stack.last().unwrap().0;
|
||||
self.fncx.name_stack.push((def_id, substs, span));
|
||||
self.fncx.push_stack_frame(CachedMir::Owned(Rc::new(mir)), substs, Some(return_ptr));
|
||||
self.stmt.push(0);
|
||||
self.constants.push(Vec::new());
|
||||
self.block = self.fncx.frame().next_block;
|
||||
self.mir = self.fncx.mir();
|
||||
},
|
||||
Some((ConstantId::Static { def_id }, span)) => {
|
||||
trace!("adding static {:?}", def_id);
|
||||
let mir = self.fncx.load_mir(def_id);
|
||||
let return_ptr = self.alloc(mir.return_ty);
|
||||
self.fncx.gecx.statics.insert(def_id, return_ptr);
|
||||
let substs = self.fncx.tcx.mk_substs(subst::Substs::empty());
|
||||
self.fncx.name_stack.push((def_id, substs, span));
|
||||
self.fncx.push_stack_frame(mir, substs, Some(return_ptr));
|
||||
self.stmt.push(0);
|
||||
self.constants.push(Vec::new());
|
||||
self.block = self.fncx.frame().next_block;
|
||||
self.mir = self.fncx.mir();
|
||||
},
|
||||
None => {},
|
||||
}
|
||||
|
||||
let basic_block = self.mir.basic_block_data(self.block);
|
||||
|
||||
if let Some(stmt) = basic_block.statements.get(self.stmt) {
|
||||
if basic_block.statements.len() > *self.stmt.last().unwrap() {
|
||||
self.process = Self::statement;
|
||||
return Ok(Event::Assignment(&stmt));
|
||||
return Ok(Event::Assignment);
|
||||
}
|
||||
|
||||
self.process = Self::terminator;
|
||||
Ok(Event::Terminator(basic_block.terminator()))
|
||||
Ok(Event::Terminator)
|
||||
}
|
||||
|
||||
|
||||
/// returns the basic block index of the currently processed block
|
||||
pub fn block(&self) -> mir::BasicBlock {
|
||||
self.block
|
||||
}
|
||||
|
||||
/// returns the statement that will be processed next
|
||||
pub fn stmt(&self) -> &mir::Statement {
|
||||
let block_data = self.mir.basic_block_data(self.block);
|
||||
&block_data.statements[*self.stmt.last().unwrap()]
|
||||
}
|
||||
|
||||
/// returns the terminator of the current block
|
||||
pub fn term(&self) -> &mir::Terminator {
|
||||
let block_data = self.mir.basic_block_data(self.block);
|
||||
block_data.terminator()
|
||||
}
|
||||
|
||||
fn extract_constants(&mut self) {
|
||||
let mut extractor = ConstantExtractor {
|
||||
constants: Vec::new(),
|
||||
};
|
||||
extractor.visit_mir(&self.mir);
|
||||
self.constants.push(extractor.constants);
|
||||
}
|
||||
}
|
||||
|
||||
struct ConstantExtractor {
|
||||
constants: Vec<(ConstantId, Span)>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for ConstantExtractor {
|
||||
fn visit_constant(&mut self, constant: &mir::Constant<'tcx>) {
|
||||
self.super_constant(constant);
|
||||
match constant.literal {
|
||||
// already computed by rustc
|
||||
mir::Literal::Value { .. } => {}
|
||||
mir::Literal::Item { .. } => {}, // FIXME: unimplemented
|
||||
mir::Literal::Promoted { index } => {
|
||||
self.constants.push((ConstantId::Promoted { index: index }, constant.span));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_statement(&mut self, block: mir::BasicBlock, stmt: &mir::Statement<'tcx>) {
|
||||
self.super_statement(block, stmt);
|
||||
if let mir::StatementKind::Assign(mir::Lvalue::Static(def_id), _) = stmt.kind {
|
||||
self.constants.push((ConstantId::Static { def_id: def_id }, stmt.span));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,12 @@
|
||||
#![feature(custom_attribute)]
|
||||
#![allow(dead_code, unused_attributes)]
|
||||
|
||||
//error-pattern:literal items (e.g. mentions of function items) are unimplemented
|
||||
//error-pattern:static should have been cached
|
||||
|
||||
static mut X: usize = 5;
|
||||
|
||||
#[miri_run]
|
||||
fn static_mut() {
|
||||
unsafe {
|
||||
X = 6;
|
||||
assert_eq!(X, 6);
|
||||
}
|
||||
fn failed_assertions() {
|
||||
assert_eq!(5, 6);
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
14
tests/run-pass/bug.rs
Normal file
14
tests/run-pass/bug.rs
Normal file
@ -0,0 +1,14 @@
|
||||
#![feature(custom_attribute)]
|
||||
#![allow(dead_code, unused_attributes)]
|
||||
|
||||
static mut X: usize = 5;
|
||||
|
||||
#[miri_run]
|
||||
fn static_mut() {
|
||||
unsafe {
|
||||
X = 6;
|
||||
assert_eq!(X, 6);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
Loading…
Reference in New Issue
Block a user