10 use rustc::mir::repr as mir;
12 use rustc::hir::def_id::DefId;
13 use rustc::mir::visit::{Visitor, LvalueContext};
14 use syntax::codemap::Span;
18 pub struct Stepper<'fncx, 'a: 'fncx, 'b: 'a + 'mir, 'mir: 'fncx, 'tcx: 'b>{
19 fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>,
21 // a cache of the constants to be computed before the next statement/terminator
22 // this is an optimization, so we don't have to allocate a new vector for every statement
23 constants: Vec<(ConstantId<'tcx>, Span, Pointer, CachedMir<'mir, 'tcx>)>,
26 impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx> {
27 pub(super) fn new(fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>) -> Self {
30 constants: Vec::new(),
34 fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<()> {
36 let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind;
37 let result = self.fncx.eval_assignment(lvalue, rvalue);
38 self.fncx.maybe_report(result)?;
39 self.fncx.frame_mut().stmt += 1;
43 fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<()> {
44 // after a terminator we go to a new block
45 self.fncx.frame_mut().stmt = 0;
47 trace!("{:?}", terminator.kind);
48 let result = self.fncx.eval_terminator(terminator);
49 self.fncx.maybe_report(result)?
52 TerminatorTarget::Return => {
53 self.fncx.pop_stack_frame();
55 TerminatorTarget::Block |
56 TerminatorTarget::Call => trace!("// {:?}", self.fncx.frame().next_block),
61 // returns true as long as there are more things to do
62 pub fn step(&mut self) -> EvalResult<bool> {
63 if self.fncx.stack.is_empty() {
67 let block = self.fncx.frame().next_block;
68 let stmt = self.fncx.frame().stmt;
69 let mir = self.fncx.mir();
70 let basic_block = mir.basic_block_data(block);
72 if let Some(ref stmt) = basic_block.statements.get(stmt) {
73 assert!(self.constants.is_empty());
76 substs: self.fncx.substs(),
77 def_id: self.fncx.frame().def_id,
79 constants: &mut self.constants,
81 }.visit_statement(block, stmt);
82 if self.constants.is_empty() {
83 self.statement(stmt)?;
85 self.extract_constants()?;
90 let terminator = basic_block.terminator();
91 assert!(self.constants.is_empty());
93 span: terminator.span,
94 substs: self.fncx.substs(),
95 def_id: self.fncx.frame().def_id,
97 constants: &mut self.constants,
99 }.visit_terminator(block, terminator);
100 if self.constants.is_empty() {
101 self.terminator(terminator)?;
103 self.extract_constants()?;
108 fn extract_constants(&mut self) -> EvalResult<()> {
109 assert!(!self.constants.is_empty());
110 for (cid, span, return_ptr, mir) in self.constants.drain(..) {
111 trace!("queuing a constant");
112 self.fncx.push_stack_frame(cid.def_id, span, mir, cid.substs, Some(return_ptr));
114 // self.step() can't be "done", so it can't return false
115 assert!(self.step()?);
120 struct ConstantExtractor<'a, 'b: 'mir, 'mir: 'a, 'tcx: 'b> {
122 // FIXME: directly push the new stackframes instead of doing this intermediate caching
123 constants: &'a mut Vec<(ConstantId<'tcx>, Span, Pointer, CachedMir<'mir, 'tcx>)>,
124 gecx: &'a mut GlobalEvalContext<'b, 'tcx>,
125 mir: &'a mir::Mir<'tcx>,
127 substs: &'tcx subst::Substs<'tcx>,
130 impl<'a, 'b, 'mir, 'tcx> ConstantExtractor<'a, 'b, 'mir, 'tcx> {
131 fn static_item(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span) {
132 let cid = ConstantId {
135 kind: ConstantKind::Global,
137 if self.gecx.statics.contains_key(&cid) {
140 let mir = self.gecx.load_mir(def_id);
141 let ptr = self.gecx.alloc_ret_ptr(mir.return_ty, substs).expect("there's no such thing as an unreachable static");
142 self.gecx.statics.insert(cid.clone(), ptr);
143 self.constants.push((cid, span, ptr, mir));
147 impl<'a, 'b, 'mir, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'mir, 'tcx> {
148 fn visit_constant(&mut self, constant: &mir::Constant<'tcx>) {
149 self.super_constant(constant);
150 match constant.literal {
151 // already computed by rustc
152 mir::Literal::Value { .. } => {}
153 mir::Literal::Item { def_id, substs } => {
154 if constant.ty.is_fn() {
155 // No need to do anything here, even if function pointers are implemented,
156 // because the type is the actual function, not the signature of the function.
157 // Thus we can simply create a zero sized allocation in `evaluate_operand`
159 self.static_item(def_id, substs, constant.span);
162 mir::Literal::Promoted { index } => {
163 let cid = ConstantId {
166 kind: ConstantKind::Promoted(index),
168 if self.gecx.statics.contains_key(&cid) {
171 let mir = self.mir.promoted[index].clone();
172 let return_ty = mir.return_ty;
173 let return_ptr = self.gecx.alloc_ret_ptr(return_ty, cid.substs).expect("there's no such thing as an unreachable static");
174 let mir = CachedMir::Owned(Rc::new(mir));
175 self.gecx.statics.insert(cid.clone(), return_ptr);
176 self.constants.push((cid, constant.span, return_ptr, mir));
181 fn visit_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>, context: LvalueContext) {
182 self.super_lvalue(lvalue, context);
183 if let mir::Lvalue::Static(def_id) = *lvalue {
184 let substs = self.gecx.tcx.mk_substs(subst::Substs::empty());
185 let span = self.span;
186 self.static_item(def_id, substs, span);