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