1 //! This module contains the `EvalContext` methods for executing a single step of the interpreter.
3 //! The main entry point is the `step` method.
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;
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() {
26 let block = self.frame().block;
27 let stmt = self.frame().stmt;
29 let basic_block = &mir.basic_blocks()[block];
31 if let Some(ref stmt) = basic_block.statements.get(stmt) {
34 span: stmt.source_info.span,
35 substs: self.substs(),
36 def_id: self.frame().def_id,
39 new_constants: &mut new,
40 }.visit_statement(block, stmt);
42 self.statement(stmt)?;
44 // if ConstantExtractor added new frames, we don't execute anything here
45 // but await the next call to step
49 let terminator = basic_block.terminator();
52 span: terminator.source_info.span,
53 substs: self.substs(),
54 def_id: self.frame().def_id,
57 new_constants: &mut new,
58 }.visit_terminator(block, terminator);
60 self.terminator(terminator)?;
62 // if ConstantExtractor added new frames, we don't execute anything here
63 // but await the next call to step
67 fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx, ()> {
69 let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind;
70 self.eval_assignment(lvalue, rvalue)?;
71 self.frame_mut().stmt += 1;
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);
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> {
93 ecx: &'a mut EvalContext<'b, 'tcx>,
94 mir: &'a mir::Mir<'tcx>,
96 substs: &'tcx subst::Substs<'tcx>,
97 new_constants: &'a mut EvalResult<'tcx, u64>,
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 {
105 kind: ConstantKind::Global,
107 if self.ecx.statics.contains_key(&cid) {
110 let mir = self.ecx.load_mir(def_id);
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))
118 fn try<F: FnOnce(&mut Self) -> EvalResult<'tcx, ()>>(&mut self, f: F) {
119 if let Ok(ref mut n) = *self.new_constants {
124 if let Err(e) = f(self) {
125 *self.new_constants = Err(e);
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`
142 self.global_item(def_id, substs, constant.span);
145 mir::Literal::Promoted { index } => {
146 let cid = ConstantId {
149 kind: ConstantKind::Promoted(index),
151 if self.ecx.statics.contains_key(&cid) {
154 let mir = self.mir.promoted[index].clone();
155 let return_ty = mir.return_ty;
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))
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);