]> git.lizzy.rs Git - rust.git/blob - src/interpreter/step.rs
5bfe85fff92fc9811244e99a9f9f1fd0f7e8f5f5
[rust.git] / src / interpreter / step.rs
1 //! This module contains the `EvalContext` methods for executing a single step of the interpreter.
2 //!
3 //! The main entry point is the `step` method.
4
5 use super::{
6     CachedMir,
7     ConstantId,
8     EvalContext,
9     ConstantKind,
10 };
11 use error::EvalResult;
12 use rustc::mir::repr as mir;
13 use rustc::ty::{subst, self};
14 use rustc::hir::def_id::DefId;
15 use rustc::mir::visit::{Visitor, LvalueContext};
16 use syntax::codemap::Span;
17 use std::rc::Rc;
18
19 impl<'a, 'tcx> EvalContext<'a, 'tcx> {
20     /// Returns true as long as there are more things to do.
21     pub fn step(&mut self) -> EvalResult<'tcx, bool> {
22         if self.stack.is_empty() {
23             return Ok(false);
24         }
25
26         let block = self.frame().block;
27         let stmt = self.frame().stmt;
28         let mir = self.mir();
29         let basic_block = &mir.basic_blocks()[block];
30
31         if let Some(ref stmt) = basic_block.statements.get(stmt) {
32             let mut new = Ok(0);
33             ConstantExtractor {
34                 span: stmt.source_info.span,
35                 substs: self.substs(),
36                 def_id: self.frame().def_id,
37                 ecx: self,
38                 mir: &mir,
39                 new_constants: &mut new,
40             }.visit_statement(block, stmt);
41             if new? == 0 {
42                 self.statement(stmt)?;
43             }
44             // if ConstantExtractor added new frames, we don't execute anything here
45             // but await the next call to step
46             return Ok(true);
47         }
48
49         let terminator = basic_block.terminator();
50         let mut new = Ok(0);
51         ConstantExtractor {
52             span: terminator.source_info.span,
53             substs: self.substs(),
54             def_id: self.frame().def_id,
55             ecx: self,
56             mir: &mir,
57             new_constants: &mut new,
58         }.visit_terminator(block, terminator);
59         if new? == 0 {
60             self.terminator(terminator)?;
61         }
62         // if ConstantExtractor added new frames, we don't execute anything here
63         // but await the next call to step
64         Ok(true)
65     }
66
67     fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx, ()> {
68         trace!("{:?}", stmt);
69         let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind;
70         self.eval_assignment(lvalue, rvalue)?;
71         self.frame_mut().stmt += 1;
72         Ok(())
73     }
74
75     fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<'tcx, ()> {
76         // after a terminator we go to a new block
77         self.frame_mut().stmt = 0;
78         trace!("{:?}", terminator.kind);
79         self.eval_terminator(terminator)?;
80         if !self.stack.is_empty() {
81             trace!("// {:?}", self.frame().block);
82         }
83         Ok(())
84     }
85 }
86
87 // WARNING: make sure that any methods implemented on this type don't ever access ecx.stack
88 // this includes any method that might access the stack
89 // basically don't call anything other than `load_mir`, `alloc_ret_ptr`, `push_stack_frame`
90 // The reason for this is, that `push_stack_frame` modifies the stack out of obvious reasons
91 struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b> {
92     span: Span,
93     ecx: &'a mut EvalContext<'b, 'tcx>,
94     mir: &'a mir::Mir<'tcx>,
95     def_id: DefId,
96     substs: &'tcx subst::Substs<'tcx>,
97     new_constants: &'a mut EvalResult<'tcx, u64>,
98 }
99
100 impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> {
101     fn global_item(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span) {
102         let cid = ConstantId {
103             def_id: def_id,
104             substs: substs,
105             kind: ConstantKind::Global,
106         };
107         if self.ecx.statics.contains_key(&cid) {
108             return;
109         }
110         let mir = self.ecx.load_mir(def_id);
111         self.try(|this| {
112             let ptr = this.ecx.alloc_ret_ptr(mir.return_ty, substs)?;
113             let ptr = ptr.expect("there's no such thing as an unreachable static");
114             this.ecx.statics.insert(cid.clone(), ptr);
115             this.ecx.push_stack_frame(def_id, span, mir, substs, Some(ptr))
116         });
117     }
118     fn try<F: FnOnce(&mut Self) -> EvalResult<'tcx, ()>>(&mut self, f: F) {
119         if let Ok(ref mut n) = *self.new_constants {
120             *n += 1;
121         } else {
122             return;
123         }
124         if let Err(e) = f(self) {
125             *self.new_constants = Err(e);
126         }
127     }
128 }
129
130 impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> {
131     fn visit_constant(&mut self, constant: &mir::Constant<'tcx>) {
132         self.super_constant(constant);
133         match constant.literal {
134             // already computed by rustc
135             mir::Literal::Value { .. } => {}
136             mir::Literal::Item { def_id, substs } => {
137                 if let ty::TyFnDef(..) = constant.ty.sty {
138                     // No need to do anything here,
139                     // because the type is the actual function, not the signature of the function.
140                     // Thus we can simply create a zero sized allocation in `evaluate_operand`
141                 } else {
142                     self.global_item(def_id, substs, constant.span);
143                 }
144             },
145             mir::Literal::Promoted { index } => {
146                 let cid = ConstantId {
147                     def_id: self.def_id,
148                     substs: self.substs,
149                     kind: ConstantKind::Promoted(index),
150                 };
151                 if self.ecx.statics.contains_key(&cid) {
152                     return;
153                 }
154                 let mir = self.mir.promoted[index].clone();
155                 let return_ty = mir.return_ty;
156                 self.try(|this| {
157                     let return_ptr = this.ecx.alloc_ret_ptr(return_ty, cid.substs)?;
158                     let return_ptr = return_ptr.expect("there's no such thing as an unreachable static");
159                     let mir = CachedMir::Owned(Rc::new(mir));
160                     this.ecx.statics.insert(cid.clone(), return_ptr);
161                     this.ecx.push_stack_frame(this.def_id, constant.span, mir, this.substs, Some(return_ptr))
162                 });
163             }
164         }
165     }
166
167     fn visit_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>, context: LvalueContext) {
168         self.super_lvalue(lvalue, context);
169         if let mir::Lvalue::Static(def_id) = *lvalue {
170             let substs = self.ecx.tcx.mk_substs(subst::Substs::empty());
171             let span = self.span;
172             self.global_item(def_id, substs, span);
173         }
174     }
175 }