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