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