]> git.lizzy.rs Git - rust.git/blob - src/interpreter/stepper.rs
448fa83fa676683ba19f8474c24a5c4f42799416
[rust.git] / src / interpreter / stepper.rs
1 use super::{
2     FnEvalContext,
3     CachedMir,
4     TerminatorTarget,
5     ConstantId,
6 };
7 use error::EvalResult;
8 use rustc::mir::repr as mir;
9 use rustc::ty::subst::{self, Subst};
10 use rustc::hir::def_id::DefId;
11 use rustc::mir::visit::{Visitor, LvalueContext};
12 use syntax::codemap::Span;
13 use memory::Pointer;
14 use std::rc::Rc;
15
16 pub enum Event {
17     Constant,
18     Assignment,
19     Terminator,
20     Done,
21 }
22
23 pub struct Stepper<'fncx, 'a: 'fncx, 'b: 'a + 'mir, 'mir: 'fncx, 'tcx: 'b>{
24     fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>,
25     mir: CachedMir<'mir, 'tcx>,
26     process: fn (&mut Stepper<'fncx, 'a, 'b, 'mir, 'tcx>) -> EvalResult<()>,
27     // a stack of constants
28     constants: Vec<Vec<(ConstantId<'tcx>, Span, Pointer, CachedMir<'mir, 'tcx>)>>,
29 }
30
31 impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx> {
32     pub(super) fn new(fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>) -> Self {
33         Stepper {
34             mir: fncx.mir(),
35             fncx: fncx,
36             process: Self::dummy,
37             constants: vec![Vec::new()],
38         }
39     }
40
41     fn dummy(&mut self) -> EvalResult<()> { Ok(()) }
42
43     fn statement(&mut self) -> EvalResult<()> {
44         let block_data = self.mir.basic_block_data(self.fncx.frame().next_block);
45         let stmt = &block_data.statements[self.fncx.frame().stmt];
46         let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind;
47         let result = self.fncx.eval_assignment(lvalue, rvalue);
48         self.fncx.maybe_report(stmt.span, result)?;
49         self.fncx.frame_mut().stmt += 1;
50         Ok(())
51     }
52
53     fn terminator(&mut self) -> EvalResult<()> {
54         // after a terminator we go to a new block
55         self.fncx.frame_mut().stmt = 0;
56         let term = {
57             let block_data = self.mir.basic_block_data(self.fncx.frame().next_block);
58             let terminator = block_data.terminator();
59             let result = self.fncx.eval_terminator(terminator);
60             self.fncx.maybe_report(terminator.span, result)?
61         };
62         match term {
63             TerminatorTarget::Block => {},
64             TerminatorTarget::Return => {
65                 self.fncx.pop_stack_frame();
66                 assert!(self.constants.last().unwrap().is_empty());
67                 self.constants.pop();
68                 if !self.fncx.stack.is_empty() {
69                     self.mir = self.fncx.mir();
70                 }
71             },
72             TerminatorTarget::Call => {
73                 self.mir = self.fncx.mir();
74                 self.constants.push(Vec::new());
75             },
76         }
77         Ok(())
78     }
79
80     fn constant(&mut self) -> EvalResult<()> {
81         match self.constants.last_mut().unwrap().pop() {
82             Some((ConstantId::Promoted { index }, span, return_ptr, mir)) => {
83                 trace!("adding promoted constant {}, {:?}", index, span);
84                 let substs = self.fncx.substs();
85                 // FIXME: somehow encode that this is a promoted constant's frame
86                 let def_id = self.fncx.frame().def_id;
87                 self.fncx.push_stack_frame(def_id, span, mir, substs, Some(return_ptr));
88                 self.constants.push(Vec::new());
89                 self.mir = self.fncx.mir();
90             },
91             Some((ConstantId::Static { def_id, substs }, span, return_ptr, mir)) => {
92                 trace!("adding static {:?}, {:?}", def_id, span);
93                 self.fncx.gecx.statics.insert(def_id, return_ptr);
94                 self.fncx.push_stack_frame(def_id, span, mir, substs, Some(return_ptr));
95                 self.constants.push(Vec::new());
96                 self.mir = self.fncx.mir();
97             },
98             None => unreachable!(),
99         }
100         Ok(())
101     }
102
103     pub fn step(&mut self) -> EvalResult<Event> {
104         (self.process)(self)?;
105
106         if self.fncx.stack.is_empty() {
107             // fuse the iterator
108             self.process = Self::dummy;
109             return Ok(Event::Done);
110         }
111
112         if !self.constants.last().unwrap().is_empty() {
113             self.process = Self::constant;
114             return Ok(Event::Constant);
115         }
116
117         let block = self.fncx.frame().next_block;
118         let stmt = self.fncx.frame().stmt;
119         let basic_block = self.mir.basic_block_data(block);
120
121         if let Some(ref stmt) = basic_block.statements.get(stmt) {
122             assert!(self.constants.last().unwrap().is_empty());
123             ConstantExtractor {
124                 constants: &mut self.constants.last_mut().unwrap(),
125                 span: stmt.span,
126                 fncx: self.fncx,
127                 mir: &self.mir,
128             }.visit_statement(block, stmt);
129             if self.constants.last().unwrap().is_empty() {
130                 self.process = Self::statement;
131                 return Ok(Event::Assignment);
132             } else {
133                 self.process = Self::constant;
134                 return Ok(Event::Constant);
135             }
136         }
137
138         let terminator = basic_block.terminator();
139         ConstantExtractor {
140             constants: &mut self.constants.last_mut().unwrap(),
141             span: terminator.span,
142             fncx: self.fncx,
143             mir: &self.mir,
144         }.visit_terminator(block, terminator);
145         if self.constants.last().unwrap().is_empty() {
146             self.process = Self::terminator;
147             Ok(Event::Terminator)
148         } else {
149             self.process = Self::constant;
150             return Ok(Event::Constant);
151         }
152     }
153
154     /// returns the statement that will be processed next
155     pub fn stmt(&self) -> &mir::Statement {
156         &self.fncx.basic_block().statements[self.fncx.frame().stmt]
157     }
158
159     /// returns the terminator of the current block
160     pub fn term(&self) -> &mir::Terminator {
161         self.fncx.basic_block().terminator()
162     }
163
164     pub fn block(&self) -> mir::BasicBlock {
165         self.fncx.frame().next_block
166     }
167 }
168
169 struct ConstantExtractor<'a: 'c, 'b: 'a + 'mir + 'c, 'c, 'mir: 'c, 'tcx: 'a + 'b + 'c> {
170     constants: &'c mut Vec<(ConstantId<'tcx>, Span, Pointer, CachedMir<'mir, 'tcx>)>,
171     span: Span,
172     mir: &'c mir::Mir<'tcx>,
173     fncx: &'c mut FnEvalContext<'a, 'b, 'mir, 'tcx>,
174 }
175
176 impl<'a, 'b, 'c, 'mir, 'tcx> ConstantExtractor<'a, 'b, 'c, 'mir, 'tcx> {
177     fn constant(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span) {
178         if self.fncx.gecx.statics.contains_key(&def_id) {
179             return;
180         }
181         let cid = ConstantId::Static {
182             def_id: def_id,
183             substs: substs,
184         };
185         let mir = self.fncx.load_mir(def_id);
186         let ptr = self.fncx.alloc_ret_ptr(mir.return_ty).expect("there's no such thing as an unreachable static");
187         self.constants.push((cid, span, ptr, mir));
188     }
189 }
190
191 impl<'a, 'b, 'c, 'mir, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'c, 'mir, 'tcx> {
192     fn visit_constant(&mut self, constant: &mir::Constant<'tcx>) {
193         self.super_constant(constant);
194         match constant.literal {
195             // already computed by rustc
196             mir::Literal::Value { .. } => {}
197             mir::Literal::Item { def_id, substs } => {
198                 let item_ty = self.fncx.tcx.lookup_item_type(def_id).subst(self.fncx.tcx, substs);
199                 if item_ty.ty.is_fn() {
200                     // unimplemented
201                 } else {
202                     self.constant(def_id, substs, constant.span);
203                 }
204             },
205             mir::Literal::Promoted { index } => {
206                 if self.fncx.frame().promoted.contains_key(&index) {
207                     return;
208                 }
209                 let mir = self.mir.promoted[index].clone();
210                 let return_ty = mir.return_ty;
211                 let return_ptr = self.fncx.alloc_ret_ptr(return_ty).expect("there's no such thing as an unreachable static");
212                 self.fncx.frame_mut().promoted.insert(index, return_ptr);
213                 let mir = CachedMir::Owned(Rc::new(mir));
214                 self.constants.push((ConstantId::Promoted { index: index }, constant.span, return_ptr, mir));
215             }
216         }
217     }
218
219     fn visit_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>, context: LvalueContext) {
220         self.super_lvalue(lvalue, context);
221         if let mir::Lvalue::Static(def_id) = *lvalue {
222             let substs = self.fncx.tcx.mk_substs(subst::Substs::empty());
223             let span = self.span;
224             self.constant(def_id, substs, span);
225         }
226     }
227 }