]> git.lizzy.rs Git - rust.git/blob - src/interpreter/stepper.rs
db7b129eee601db020aadf4eaffd301109a04313
[rust.git] / src / interpreter / stepper.rs
1 use super::{
2     FnEvalContext,
3     CachedMir,
4     TerminatorTarget,
5     ConstantId,
6     GlobalEvalContext,
7     ConstantKind,
8 };
9 use error::EvalResult;
10 use rustc::mir::repr as mir;
11 use rustc::ty::{subst, self};
12 use rustc::hir::def_id::DefId;
13 use rustc::mir::visit::{Visitor, LvalueContext};
14 use syntax::codemap::Span;
15 use std::rc::Rc;
16 use memory::Pointer;
17
18 pub struct Stepper<'fncx, 'a: 'fncx, 'b: 'a + 'mir, 'mir: 'fncx, 'tcx: 'b>{
19     fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>,
20
21     // a cache of the constants to be computed before the next statement/terminator
22     // this is an optimization, so we don't have to allocate a new vector for every statement
23     constants: Vec<(ConstantId<'tcx>, Span, Pointer, CachedMir<'mir, 'tcx>)>,
24 }
25
26 impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx> {
27     pub(super) fn new(fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>) -> Self {
28         Stepper {
29             fncx: fncx,
30             constants: Vec::new(),
31         }
32     }
33
34     fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<()> {
35         trace!("{:?}", stmt);
36         let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind;
37         let result = self.fncx.eval_assignment(lvalue, rvalue);
38         self.fncx.maybe_report(result)?;
39         self.fncx.frame_mut().stmt += 1;
40         Ok(())
41     }
42
43     fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<()> {
44         // after a terminator we go to a new block
45         self.fncx.frame_mut().stmt = 0;
46         let term = {
47             trace!("{:?}", terminator.kind);
48             let result = self.fncx.eval_terminator(terminator);
49             self.fncx.maybe_report(result)?
50         };
51         match term {
52             TerminatorTarget::Return => {
53                 self.fncx.pop_stack_frame();
54             },
55             TerminatorTarget::Block |
56             TerminatorTarget::Call => trace!("// {:?}", self.fncx.frame().next_block),
57         }
58         Ok(())
59     }
60
61     // returns true as long as there are more things to do
62     pub fn step(&mut self) -> EvalResult<bool> {
63         if self.fncx.stack.is_empty() {
64             return Ok(false);
65         }
66
67         let block = self.fncx.frame().next_block;
68         let stmt = self.fncx.frame().stmt;
69         let mir = self.fncx.mir();
70         let basic_block = mir.basic_block_data(block);
71
72         if let Some(ref stmt) = basic_block.statements.get(stmt) {
73             assert!(self.constants.is_empty());
74             ConstantExtractor {
75                 span: stmt.span,
76                 substs: self.fncx.substs(),
77                 def_id: self.fncx.frame().def_id,
78                 gecx: self.fncx.gecx,
79                 constants: &mut self.constants,
80                 mir: &mir,
81             }.visit_statement(block, stmt);
82             if self.constants.is_empty() {
83                 self.statement(stmt)?;
84             } else {
85                 self.extract_constants()?;
86             }
87             return Ok(true);
88         }
89
90         let terminator = basic_block.terminator();
91         assert!(self.constants.is_empty());
92         ConstantExtractor {
93             span: terminator.span,
94             substs: self.fncx.substs(),
95             def_id: self.fncx.frame().def_id,
96             gecx: self.fncx.gecx,
97             constants: &mut self.constants,
98             mir: &mir,
99         }.visit_terminator(block, terminator);
100         if self.constants.is_empty() {
101             self.terminator(terminator)?;
102         } else {
103             self.extract_constants()?;
104         }
105         Ok(true)
106     }
107
108     fn extract_constants(&mut self) -> EvalResult<()> {
109         assert!(!self.constants.is_empty());
110         for (cid, span, return_ptr, mir) in self.constants.drain(..) {
111             trace!("queuing a constant");
112             self.fncx.push_stack_frame(cid.def_id, span, mir, cid.substs, Some(return_ptr));
113         }
114         // self.step() can't be "done", so it can't return false
115         assert!(self.step()?);
116         Ok(())
117     }
118 }
119
120 struct ConstantExtractor<'a, 'b: 'mir, 'mir: 'a, 'tcx: 'b> {
121     span: Span,
122     // FIXME: directly push the new stackframes instead of doing this intermediate caching
123     constants: &'a mut Vec<(ConstantId<'tcx>, Span, Pointer, CachedMir<'mir, 'tcx>)>,
124     gecx: &'a mut GlobalEvalContext<'b, 'tcx>,
125     mir: &'a mir::Mir<'tcx>,
126     def_id: DefId,
127     substs: &'tcx subst::Substs<'tcx>,
128 }
129
130 impl<'a, 'b, 'mir, 'tcx> ConstantExtractor<'a, 'b, 'mir, 'tcx> {
131     fn static_item(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span) {
132         let cid = ConstantId {
133             def_id: def_id,
134             substs: substs,
135             kind: ConstantKind::Global,
136         };
137         if self.gecx.statics.contains_key(&cid) {
138             return;
139         }
140         let mir = self.gecx.load_mir(def_id);
141         let ptr = self.gecx.alloc_ret_ptr(mir.return_ty, substs).expect("there's no such thing as an unreachable static");
142         self.gecx.statics.insert(cid.clone(), ptr);
143         self.constants.push((cid, span, ptr, mir));
144     }
145 }
146
147 impl<'a, 'b, 'mir, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'mir, 'tcx> {
148     fn visit_constant(&mut self, constant: &mir::Constant<'tcx>) {
149         self.super_constant(constant);
150         match constant.literal {
151             // already computed by rustc
152             mir::Literal::Value { .. } => {}
153             mir::Literal::Item { def_id, substs } => {
154                 if let ty::TyFnDef(..) = constant.ty.sty {
155                     // No need to do anything here, even if function pointers are implemented,
156                     // because the type is the actual function, not the signature of the function.
157                     // Thus we can simply create a zero sized allocation in `evaluate_operand`
158                 } else {
159                     self.static_item(def_id, substs, constant.span);
160                 }
161             },
162             mir::Literal::Promoted { index } => {
163                 let cid = ConstantId {
164                     def_id: self.def_id,
165                     substs: self.substs,
166                     kind: ConstantKind::Promoted(index),
167                 };
168                 if self.gecx.statics.contains_key(&cid) {
169                     return;
170                 }
171                 let mir = self.mir.promoted[index].clone();
172                 let return_ty = mir.return_ty;
173                 let return_ptr = self.gecx.alloc_ret_ptr(return_ty, cid.substs).expect("there's no such thing as an unreachable static");
174                 let mir = CachedMir::Owned(Rc::new(mir));
175                 self.gecx.statics.insert(cid.clone(), return_ptr);
176                 self.constants.push((cid, constant.span, return_ptr, mir));
177             }
178         }
179     }
180
181     fn visit_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>, context: LvalueContext) {
182         self.super_lvalue(lvalue, context);
183         if let mir::Lvalue::Static(def_id) = *lvalue {
184             let substs = self.gecx.tcx.mk_substs(subst::Substs::empty());
185             let span = self.span;
186             self.static_item(def_id, substs, span);
187         }
188     }
189 }