10 use rustc::mir::repr as mir;
11 use rustc::ty::subst::{self, Subst};
12 use rustc::hir::def_id::DefId;
13 use rustc::mir::visit::{Visitor, LvalueContext};
14 use syntax::codemap::Span;
18 pub enum Event<'a, 'tcx: 'a> {
19 Block(mir::BasicBlock),
23 Assignment(&'a mir::Statement<'tcx>),
24 Terminator(&'a mir::Terminator<'tcx>),
28 pub struct Stepper<'fncx, 'a: 'fncx, 'b: 'a + 'mir, 'mir: 'fncx, 'tcx: 'b>{
29 fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>,
31 // a cache of the constants to be computed before the next statement/terminator
32 // this is an optimization, so we don't have to allocate a new vector for every statement
33 constants: Vec<(ConstantId<'tcx>, Span, Pointer, CachedMir<'mir, 'tcx>)>,
36 impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx> {
37 pub(super) fn new(fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>) -> Self {
40 constants: Vec::new(),
44 fn statement<F: for<'f> FnMut(Event<'f, 'tcx>)>(&mut self, mut f: F) -> EvalResult<()> {
45 let mir = self.fncx.mir();
46 let block_data = mir.basic_block_data(self.fncx.frame().next_block);
47 let stmt = &block_data.statements[self.fncx.frame().stmt];
48 f(Event::Assignment(stmt));
49 let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind;
50 let result = self.fncx.eval_assignment(lvalue, rvalue);
51 self.fncx.maybe_report(result)?;
52 self.fncx.frame_mut().stmt += 1;
56 fn terminator<F: for<'f> FnMut(Event<'f, 'tcx>)>(&mut self, mut f: F) -> EvalResult<()> {
57 // after a terminator we go to a new block
58 self.fncx.frame_mut().stmt = 0;
60 let mir = self.fncx.mir();
61 let block_data = mir.basic_block_data(self.fncx.frame().next_block);
62 let terminator = block_data.terminator();
63 f(Event::Terminator(terminator));
64 let result = self.fncx.eval_terminator(terminator);
65 self.fncx.maybe_report(result)?
68 TerminatorTarget::Block => f(Event::Block(self.fncx.frame().next_block)),
69 TerminatorTarget::Return => {
71 self.fncx.pop_stack_frame();
73 TerminatorTarget::Call => f(Event::Call),
78 // returns true as long as there are more things to do
79 pub fn step<F: for<'f> FnMut(Event<'f, 'tcx>)>(&mut self, mut f: F) -> EvalResult<()> {
80 if self.fncx.stack.is_empty() {
85 let block = self.fncx.frame().next_block;
86 let stmt = self.fncx.frame().stmt;
87 let mir = self.fncx.mir();
88 let basic_block = mir.basic_block_data(block);
90 if let Some(ref stmt) = basic_block.statements.get(stmt) {
91 assert!(self.constants.is_empty());
94 substs: self.fncx.substs(),
95 def_id: self.fncx.frame().def_id,
97 constants: &mut self.constants,
99 }.visit_statement(block, stmt);
100 if self.constants.is_empty() {
101 return self.statement(f);
103 return self.extract_constants(f);
107 let terminator = basic_block.terminator();
108 assert!(self.constants.is_empty());
110 span: terminator.span,
111 substs: self.fncx.substs(),
112 def_id: self.fncx.frame().def_id,
113 gecx: self.fncx.gecx,
114 constants: &mut self.constants,
116 }.visit_terminator(block, terminator);
117 if self.constants.is_empty() {
120 self.extract_constants(f)
124 fn extract_constants<F: for<'f> FnMut(Event<'f, 'tcx>)>(&mut self, mut f: F) -> EvalResult<()> {
125 assert!(!self.constants.is_empty());
126 for (cid, span, return_ptr, mir) in self.constants.drain(..) {
128 self.fncx.push_stack_frame(cid.def_id, span, mir, cid.substs, Some(return_ptr));
134 struct ConstantExtractor<'a, 'b: 'mir, 'mir: 'a, 'tcx: 'b> {
136 constants: &'a mut Vec<(ConstantId<'tcx>, Span, Pointer, CachedMir<'mir, 'tcx>)>,
137 gecx: &'a mut GlobalEvalContext<'b, 'tcx>,
138 mir: &'a mir::Mir<'tcx>,
140 substs: &'tcx subst::Substs<'tcx>,
143 impl<'a, 'b, 'mir, 'tcx> ConstantExtractor<'a, 'b, 'mir, 'tcx> {
144 fn static_item(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span) {
145 let cid = ConstantId {
148 kind: ConstantKind::Global,
150 if self.gecx.statics.contains_key(&cid) {
153 let mir = self.gecx.load_mir(def_id);
154 let ptr = self.gecx.alloc_ret_ptr(mir.return_ty, substs).expect("there's no such thing as an unreachable static");
155 self.gecx.statics.insert(cid.clone(), ptr);
156 self.constants.push((cid, span, ptr, mir));
160 impl<'a, 'b, 'mir, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'mir, 'tcx> {
161 fn visit_constant(&mut self, constant: &mir::Constant<'tcx>) {
162 self.super_constant(constant);
163 match constant.literal {
164 // already computed by rustc
165 mir::Literal::Value { .. } => {}
166 mir::Literal::Item { def_id, substs } => {
167 let item_ty = self.gecx.tcx.lookup_item_type(def_id).subst(self.gecx.tcx, substs);
168 if item_ty.ty.is_fn() {
171 self.static_item(def_id, substs, constant.span);
174 mir::Literal::Promoted { index } => {
175 let cid = ConstantId {
178 kind: ConstantKind::Promoted(index),
180 if self.gecx.statics.contains_key(&cid) {
183 let mir = self.mir.promoted[index].clone();
184 let return_ty = mir.return_ty;
185 let return_ptr = self.gecx.alloc_ret_ptr(return_ty, cid.substs).expect("there's no such thing as an unreachable static");
186 let mir = CachedMir::Owned(Rc::new(mir));
187 self.gecx.statics.insert(cid.clone(), return_ptr);
188 self.constants.push((cid, constant.span, return_ptr, mir));
193 fn visit_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>, context: LvalueContext) {
194 self.super_lvalue(lvalue, context);
195 if let mir::Lvalue::Static(def_id) = *lvalue {
196 let substs = self.gecx.tcx.mk_substs(subst::Substs::empty());
197 let span = self.span;
198 self.static_item(def_id, substs, span);