//! This module contains the `EvalContext` methods for executing a single step of the interpreter. //! //! The main entry point is the `step` method. use rustc::mir; use rustc::mir::interpret::EvalResult; use super::{EvalContext, Machine}; impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { pub fn inc_step_counter_and_check_limit(&mut self, n: usize) { self.terminators_remaining = self.terminators_remaining.saturating_sub(n); if self.terminators_remaining == 0 { // FIXME(#49980): make this warning a lint self.tcx.sess.span_warn(self.frame().span, "Constant evaluating a complex constant, this might take some time"); self.terminators_remaining = 1_000_000; } } /// Returns true as long as there are more things to do. pub fn step(&mut self) -> EvalResult<'tcx, bool> { if self.stack.is_empty() { return Ok(false); } let block = self.frame().block; let stmt_id = self.frame().stmt; let mir = self.mir(); let basic_block = &mir.basic_blocks()[block]; let old_frames = self.cur_frame(); if let Some(stmt) = basic_block.statements.get(stmt_id) { assert_eq!(old_frames, self.cur_frame()); self.statement(stmt)?; return Ok(true); } self.inc_step_counter_and_check_limit(1); let terminator = basic_block.terminator(); assert_eq!(old_frames, self.cur_frame()); self.terminator(terminator)?; Ok(true) } fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx> { trace!("{:?}", stmt); use rustc::mir::StatementKind::*; // Some statements (e.g. box) push new stack frames. We have to record the stack frame number // *before* executing the statement. let frame_idx = self.cur_frame(); self.tcx.span = stmt.source_info.span; self.memory.tcx.span = stmt.source_info.span; match stmt.kind { Assign(ref place, ref rvalue) => self.eval_rvalue_into_place(rvalue, place)?, SetDiscriminant { ref place, variant_index, } => { let dest = self.eval_place(place)?; let dest_ty = self.place_ty(place); self.write_discriminant_value(dest_ty, dest, variant_index)?; } // Mark locals as alive StorageLive(local) => { let old_val = self.frame_mut().storage_live(local); self.deallocate_local(old_val)?; } // Mark locals as dead StorageDead(local) => { let old_val = self.frame_mut().storage_dead(local); self.deallocate_local(old_val)?; } // FIXME: is there some dynamic semantics we should attach to // these? Or am I correct in thinking that the inerpreter // is solely intended for borrowck'ed code? ReadForMatch(..) => {} // Validity checks. Validate(op, ref places) => { for operand in places { M::validation_op(self, op, operand)?; } } EndRegion(ce) => { M::end_region(self, Some(ce))?; } UserAssertTy(..) => {} // Defined to do nothing. These are added by optimization passes, to avoid changing the // size of MIR constantly. Nop => {} InlineAsm { .. } => return err!(InlineAsm), } self.stack[frame_idx].stmt += 1; Ok(()) } fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<'tcx> { trace!("{:?}", terminator.kind); self.tcx.span = terminator.source_info.span; self.memory.tcx.span = terminator.source_info.span; self.eval_terminator(terminator)?; if !self.stack.is_empty() { trace!("// {:?}", self.frame().block); } Ok(()) } }