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;
15 pub(super) struct Stepper<'ecx, 'a: 'ecx, 'tcx: 'a>{
16 ecx: &'ecx mut EvalContext<'a, 'tcx>,
19 impl<'ecx, 'a, 'tcx> Stepper<'ecx, 'a, 'tcx> {
20 pub(super) fn new(ecx: &'ecx mut EvalContext<'a, 'tcx>) -> Self {
26 fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<()> {
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;
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);
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() {
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];
56 if let Some(ref stmt) = basic_block.statements.get(stmt) {
57 let current_stack = self.ecx.stack.len();
59 span: stmt.source_info.span,
60 substs: self.ecx.substs(),
61 def_id: self.ecx.frame().def_id,
64 }.visit_statement(block, stmt);
65 if current_stack == self.ecx.stack.len() {
66 self.statement(stmt)?;
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()?);
75 let terminator = basic_block.terminator();
76 let current_stack = self.ecx.stack.len();
78 span: terminator.source_info.span,
79 substs: self.ecx.substs(),
80 def_id: self.ecx.frame().def_id,
83 }.visit_terminator(block, terminator);
84 if current_stack == self.ecx.stack.len() {
85 self.terminator(terminator)?;
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()?);
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> {
101 ecx: &'a mut EvalContext<'b, 'tcx>,
102 mir: &'a mir::Mir<'tcx>,
104 substs: &'tcx subst::Substs<'tcx>,
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 {
112 kind: ConstantKind::Global,
114 if self.ecx.statics.contains_key(&cid) {
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));
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`
136 self.global_item(def_id, substs, constant.span);
139 mir::Literal::Promoted { index } => {
140 let cid = ConstantId {
143 kind: ConstantKind::Promoted(index),
145 if self.ecx.statics.contains_key(&cid) {
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));
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);