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