1 //! This module contains the `EvalContext` methods for executing a single step of the interpreter.
3 //! The main entry point is the `step` method.
5 use rustc::hir::def_id::DefId;
7 use rustc::mir::visit::{Visitor, LvalueContext};
9 use rustc::traits::Reveal;
10 use rustc::ty::{self, TypeFoldable};
11 use rustc::ty::layout::Layout;
12 use rustc::ty::subst::{Subst, Substs};
13 use rustc::infer::TransNormalize;
15 use error::{EvalResult, EvalError};
16 use eval_context::{EvalContext, StackPopCleanup};
17 use lvalue::{Global, GlobalId, Lvalue};
18 use validation::ValidationCtx;
19 use value::{Value, PrimVal};
20 use syntax::codemap::Span;
21 use syntax::ast::Mutability;
23 impl<'a, 'tcx> EvalContext<'a, 'tcx> {
24 pub fn inc_step_counter_and_check_limit(&mut self, n: u64) -> EvalResult<'tcx> {
25 self.steps_remaining = self.steps_remaining.saturating_sub(n);
26 if self.steps_remaining > 0 {
29 Err(EvalError::ExecutionTimeLimitReached)
33 /// Returns true as long as there are more things to do.
34 pub fn step(&mut self) -> EvalResult<'tcx, bool> {
35 self.inc_step_counter_and_check_limit(1)?;
36 if self.stack.is_empty() {
40 let cur_frame = self.cur_frame();
41 self.memory.set_cur_frame(cur_frame);
43 let block = self.frame().block;
44 let stmt_id = self.frame().stmt;
46 let basic_block = &mir.basic_blocks()[block];
48 if let Some(stmt) = basic_block.statements.get(stmt_id) {
51 span: stmt.source_info.span,
52 instance: self.frame().instance,
55 new_constants: &mut new,
56 }.visit_statement(block, stmt, mir::Location { block, statement_index: stmt_id });
57 // if ConstantExtractor added new frames, we don't execute anything here
58 // but await the next call to step
60 self.statement(stmt)?;
65 let terminator = basic_block.terminator();
68 span: terminator.source_info.span,
69 instance: self.frame().instance,
72 new_constants: &mut new,
73 }.visit_terminator(block, terminator, mir::Location { block, statement_index: stmt_id });
74 // if ConstantExtractor added new frames, we don't execute anything here
75 // but await the next call to step
77 self.terminator(terminator)?;
82 fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx> {
85 use rustc::mir::StatementKind::*;
87 Assign(ref lvalue, ref rvalue) => self.eval_rvalue_into_lvalue(rvalue, lvalue)?,
89 SetDiscriminant { ref lvalue, variant_index } => {
90 let dest = self.eval_lvalue(lvalue)?;
91 let dest_ty = self.lvalue_ty(lvalue);
92 let dest_layout = self.type_layout(dest_ty)?;
95 Layout::General { discr, .. } => {
96 let discr_size = discr.size().bytes();
97 let dest_ptr = self.force_allocation(dest)?.to_ptr()?;
98 self.memory.write_uint(dest_ptr, variant_index as u128, discr_size)?
101 Layout::RawNullablePointer { nndiscr, .. } => {
102 if variant_index as u64 != nndiscr {
103 self.write_null(dest, dest_ty)?;
107 Layout::StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => {
108 if variant_index as u64 != nndiscr {
109 let (offset, ty) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?;
110 let nonnull = self.force_allocation(dest)?.to_ptr()?.offset(offset.bytes(), &self)?;
111 trace!("struct wrapped nullable pointer type: {}", ty);
112 // only the pointer part of a fat pointer is used for this space optimization
113 let discr_size = self.type_size(ty)?.expect("bad StructWrappedNullablePointer discrfield");
114 self.memory.write_uint(nonnull, 0, discr_size)?;
118 _ => bug!("SetDiscriminant on {} represented as {:#?}", dest_ty, dest_layout),
122 // Mark locals as dead or alive.
123 StorageLive(ref lvalue) | StorageDead(ref lvalue)=> {
124 let (frame, local) = match self.eval_lvalue(lvalue)? {
125 Lvalue::Local{ frame, local } if self.cur_frame() == frame => (frame, local),
126 _ => return Err(EvalError::Unimplemented("Storage annotations must refer to locals of the topmost stack frame.".to_owned())) // FIXME maybe this should get its own error type
128 let old_val = match stmt.kind {
129 StorageLive(_) => self.stack[frame].storage_live(local)?,
130 StorageDead(_) => self.stack[frame].storage_dead(local)?,
131 _ => bug!("We already checked that we are a storage stmt")
133 self.deallocate_local(old_val)?;
137 Validate(op, ref lvalues) => {
138 for operand in lvalues {
139 // We need to monomorphize ty *without* erasing lifetimes
140 let mut ty = operand.ty.subst(self.tcx, self.substs());
141 // This is essentially a copy of normalize_associated_type, but without erasure
142 if ty.has_projection_types() {
143 let param_env = ty::ParamEnv::empty(Reveal::All);
144 ty = self.tcx.infer_ctxt().enter(move |infcx| {
145 ty.trans_normalize(&infcx, param_env)
149 // Now we can do validation at this type
150 let lvalue = self.eval_lvalue(&operand.lval)?;
151 self.validate(lvalue, ty, ValidationCtx::new(op))?;
155 self.memory.locks_lifetime_ended(Some(ce));
158 // Defined to do nothing. These are added by optimization passes, to avoid changing the
159 // size of MIR constantly.
162 InlineAsm { .. } => return Err(EvalError::InlineAsm),
165 self.frame_mut().stmt += 1;
169 fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<'tcx> {
170 trace!("{:?}", terminator.kind);
171 self.eval_terminator(terminator)?;
172 if !self.stack.is_empty() {
173 trace!("// {:?}", self.frame().block);
179 // WARNING: make sure that any methods implemented on this type don't ever access ecx.stack
180 // this includes any method that might access the stack
181 // basically don't call anything other than `load_mir`, `alloc_ptr`, `push_stack_frame`
182 // The reason for this is, that `push_stack_frame` modifies the stack out of obvious reasons
183 struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b> {
185 ecx: &'a mut EvalContext<'b, 'tcx>,
186 mir: &'tcx mir::Mir<'tcx>,
187 instance: ty::Instance<'tcx>,
188 new_constants: &'a mut EvalResult<'tcx, u64>,
191 impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> {
195 substs: &'tcx Substs<'tcx>,
197 mutability: Mutability,
199 let instance = self.ecx.resolve_associated_const(def_id, substs);
200 let cid = GlobalId { instance, promoted: None };
201 if self.ecx.globals.contains_key(&cid) {
204 if self.ecx.tcx.has_attr(def_id, "linkage") {
205 trace!("Initializing an extern global with NULL");
206 self.ecx.globals.insert(cid, Global::initialized(self.ecx.tcx.type_of(def_id), Value::ByVal(PrimVal::Bytes(0)), mutability));
210 let mir = this.ecx.load_mir(instance.def)?;
211 this.ecx.globals.insert(cid, Global::uninitialized(mir.return_ty));
212 let internally_mutable = !mir.return_ty.is_freeze(
214 ty::ParamEnv::empty(Reveal::All),
216 let mutability = if mutability == Mutability::Mutable || internally_mutable {
219 Mutability::Immutable
221 let cleanup = StackPopCleanup::MarkStatic(mutability);
222 let name = ty::tls::with(|tcx| tcx.item_path_str(def_id));
223 trace!("pushing stack frame for global: {}", name);
224 this.ecx.push_stack_frame(
234 fn try<F: FnOnce(&mut Self) -> EvalResult<'tcx>>(&mut self, f: F) {
235 if let Ok(ref mut n) = *self.new_constants {
240 if let Err(e) = f(self) {
241 *self.new_constants = Err(e);
246 impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> {
247 fn visit_constant(&mut self, constant: &mir::Constant<'tcx>, location: mir::Location) {
248 self.super_constant(constant, location);
249 match constant.literal {
250 // already computed by rustc
251 mir::Literal::Value { .. } => {}
252 mir::Literal::Item { def_id, substs } => {
253 self.global_item(def_id, substs, constant.span, Mutability::Immutable);
255 mir::Literal::Promoted { index } => {
257 instance: self.instance,
258 promoted: Some(index),
260 if self.ecx.globals.contains_key(&cid) {
263 let mir = &self.mir.promoted[index];
265 let ty = this.ecx.monomorphize(mir.return_ty, this.instance.substs);
266 this.ecx.globals.insert(cid, Global::uninitialized(ty));
267 trace!("pushing stack frame for {:?}", index);
268 this.ecx.push_stack_frame(this.instance,
272 StackPopCleanup::MarkStatic(Mutability::Immutable),
281 lvalue: &mir::Lvalue<'tcx>,
282 context: LvalueContext<'tcx>,
283 location: mir::Location
285 self.super_lvalue(lvalue, context, location);
286 if let mir::Lvalue::Static(ref static_) = *lvalue {
287 let def_id = static_.def_id;
288 let substs = self.ecx.tcx.intern_substs(&[]);
289 let span = self.span;
290 if let Some(node_item) = self.ecx.tcx.hir.get_if_local(def_id) {
291 if let hir::map::Node::NodeItem(&hir::Item { ref node, .. }) = node_item {
292 if let hir::ItemStatic(_, m, _) = *node {
293 self.global_item(def_id, substs, span, if m == hir::MutMutable { Mutability::Mutable } else { Mutability::Immutable });
296 bug!("static def id doesn't point to static");
299 bug!("static def id doesn't point to item");
302 let def = self.ecx.tcx.describe_def(def_id).expect("static not found");
303 if let hir::def::Def::Static(_, mutable) = def {
304 self.global_item(def_id, substs, span, if mutable { Mutability::Mutable } else { Mutability::Immutable });
306 bug!("static found but isn't a static: {:?}", def);