8 use rustc::mir::repr as mir;
9 use rustc::ty::{self, subst};
10 use rustc::mir::visit::Visitor;
11 use syntax::codemap::Span;
21 pub struct Stepper<'fncx, 'a: 'fncx, 'b: 'a + 'mir, 'mir: 'fncx, 'tcx: 'b>{
22 fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>,
23 block: mir::BasicBlock,
24 // a stack of statement positions
26 mir: CachedMir<'mir, 'tcx>,
27 process: fn (&mut Stepper<'fncx, 'a, 'b, 'mir, 'tcx>) -> EvalResult<()>,
28 // a stack of constants
29 constants: Vec<Vec<(ConstantId, Span)>>,
32 impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx> {
33 pub(super) fn new(fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>) -> Self {
34 let mut stepper = Stepper {
35 block: fncx.frame().next_block,
40 constants: Vec::new(),
42 stepper.extract_constants();
46 fn dummy(&mut self) -> EvalResult<()> { Ok(()) }
48 fn statement(&mut self) -> EvalResult<()> {
49 let block_data = self.mir.basic_block_data(self.block);
50 let stmt = &block_data.statements[*self.stmt.last().unwrap()];
51 let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind;
52 let result = self.fncx.eval_assignment(lvalue, rvalue);
53 self.fncx.maybe_report(stmt.span, result)?;
54 *self.stmt.last_mut().unwrap() += 1;
58 fn terminator(&mut self) -> EvalResult<()> {
59 *self.stmt.last_mut().unwrap() = 0;
61 let block_data = self.mir.basic_block_data(self.block);
62 let terminator = block_data.terminator();
63 let result = self.fncx.eval_terminator(terminator);
64 self.fncx.maybe_report(terminator.span, result)?
67 TerminatorTarget::Block(block) => {
70 TerminatorTarget::Return => {
71 self.fncx.pop_stack_frame();
72 self.fncx.name_stack.pop();
74 assert!(self.constants.last().unwrap().is_empty());
76 if !self.fncx.stack.is_empty() {
77 self.block = self.fncx.frame().next_block;
78 self.mir = self.fncx.mir();
81 TerminatorTarget::Call => {
82 self.block = self.fncx.frame().next_block;
83 self.mir = self.fncx.mir();
85 self.extract_constants();
91 fn alloc(&mut self, ty: ty::FnOutput<'tcx>) -> Pointer {
93 ty::FnConverging(ty) => {
94 let size = self.fncx.type_size(ty);
95 self.fncx.memory.allocate(size)
97 ty::FnDiverging => panic!("there's no such thing as an unreachable static"),
101 pub fn step(&mut self) -> EvalResult<Event> {
102 (self.process)(self)?;
104 if self.fncx.stack.is_empty() {
106 self.process = Self::dummy;
107 return Ok(Event::Done);
110 match self.constants.last_mut().unwrap().pop() {
111 Some((ConstantId::Promoted { index }, span)) => {
112 trace!("adding promoted constant {}", index);
113 let mir = self.mir.promoted[index].clone();
114 let return_ptr = self.alloc(mir.return_ty);
115 self.fncx.frame_mut().promoted.insert(index, return_ptr);
116 let substs = self.fncx.substs();
117 // FIXME: somehow encode that this is a promoted constant's frame
118 let def_id = self.fncx.name_stack.last().unwrap().0;
119 self.fncx.name_stack.push((def_id, substs, span));
120 self.fncx.push_stack_frame(CachedMir::Owned(Rc::new(mir)), substs, Some(return_ptr));
122 self.constants.push(Vec::new());
123 self.block = self.fncx.frame().next_block;
124 self.mir = self.fncx.mir();
126 Some((ConstantId::Static { def_id }, span)) => {
127 trace!("adding static {:?}", def_id);
128 let mir = self.fncx.load_mir(def_id);
129 let return_ptr = self.alloc(mir.return_ty);
130 self.fncx.gecx.statics.insert(def_id, return_ptr);
131 let substs = self.fncx.tcx.mk_substs(subst::Substs::empty());
132 self.fncx.name_stack.push((def_id, substs, span));
133 self.fncx.push_stack_frame(mir, substs, Some(return_ptr));
135 self.constants.push(Vec::new());
136 self.block = self.fncx.frame().next_block;
137 self.mir = self.fncx.mir();
142 let basic_block = self.mir.basic_block_data(self.block);
144 if basic_block.statements.len() > *self.stmt.last().unwrap() {
145 self.process = Self::statement;
146 return Ok(Event::Assignment);
149 self.process = Self::terminator;
150 Ok(Event::Terminator)
153 /// returns the basic block index of the currently processed block
154 pub fn block(&self) -> mir::BasicBlock {
158 /// returns the statement that will be processed next
159 pub fn stmt(&self) -> &mir::Statement {
160 let block_data = self.mir.basic_block_data(self.block);
161 &block_data.statements[*self.stmt.last().unwrap()]
164 /// returns the terminator of the current block
165 pub fn term(&self) -> &mir::Terminator {
166 let block_data = self.mir.basic_block_data(self.block);
167 block_data.terminator()
170 fn extract_constants(&mut self) {
171 let mut extractor = ConstantExtractor {
172 constants: Vec::new(),
174 extractor.visit_mir(&self.mir);
175 self.constants.push(extractor.constants);
179 struct ConstantExtractor {
180 constants: Vec<(ConstantId, Span)>,
183 impl<'tcx> Visitor<'tcx> for ConstantExtractor {
184 fn visit_constant(&mut self, constant: &mir::Constant<'tcx>) {
185 self.super_constant(constant);
186 match constant.literal {
187 // already computed by rustc
188 mir::Literal::Value { .. } => {}
189 mir::Literal::Item { .. } => {}, // FIXME: unimplemented
190 mir::Literal::Promoted { index } => {
191 self.constants.push((ConstantId::Promoted { index: index }, constant.span));
196 fn visit_statement(&mut self, block: mir::BasicBlock, stmt: &mir::Statement<'tcx>) {
197 self.super_statement(block, stmt);
198 if let mir::StatementKind::Assign(mir::Lvalue::Static(def_id), _) = stmt.kind {
199 self.constants.push((ConstantId::Static { def_id: def_id }, stmt.span));