]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/step.rs
7c8f3764f2b7971b689cc078835015d9c0e29bba
[rust.git] / src / librustc_mir / interpret / step.rs
1 //! This module contains the `EvalContext` methods for executing a single step of the interpreter.
2 //!
3 //! The main entry point is the `step` method.
4
5 use rustc::hir::def_id::DefId;
6 use rustc::hir;
7 use rustc::mir::visit::{Visitor, LvalueContext};
8 use rustc::mir;
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;
14
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;
22
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 {
27             Ok(())
28         } else {
29             Err(EvalError::ExecutionTimeLimitReached)
30         }
31     }
32
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() {
37             return Ok(false);
38         }
39
40         let cur_frame = self.cur_frame();
41         self.memory.set_cur_frame(cur_frame);
42
43         let block = self.frame().block;
44         let stmt_id = self.frame().stmt;
45         let mir = self.mir();
46         let basic_block = &mir.basic_blocks()[block];
47
48         if let Some(stmt) = basic_block.statements.get(stmt_id) {
49             let mut new = Ok(0);
50             ConstantExtractor {
51                 span: stmt.source_info.span,
52                 instance: self.frame().instance,
53                 ecx: self,
54                 mir,
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
59             if new? == 0 {
60                 self.statement(stmt)?;
61             }
62             return Ok(true);
63         }
64
65         let terminator = basic_block.terminator();
66         let mut new = Ok(0);
67         ConstantExtractor {
68             span: terminator.source_info.span,
69             instance: self.frame().instance,
70             ecx: self,
71             mir,
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
76         if new? == 0 {
77             self.terminator(terminator)?;
78         }
79         Ok(true)
80     }
81
82     fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx> {
83         trace!("{:?}", stmt);
84
85         use rustc::mir::StatementKind::*;
86         match stmt.kind {
87             Assign(ref lvalue, ref rvalue) => self.eval_rvalue_into_lvalue(rvalue, lvalue)?,
88
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)?;
93
94                 match *dest_layout {
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)?
99                     }
100
101                     Layout::RawNullablePointer { nndiscr, .. } => {
102                         if variant_index as u64 != nndiscr {
103                             self.write_null(dest, dest_ty)?;
104                         }
105                     }
106
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)?;
115                         }
116                     },
117
118                     _ => bug!("SetDiscriminant on {} represented as {:#?}", dest_ty, dest_layout),
119                 }
120             }
121
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
127                 };
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")
132                 };
133                 self.deallocate_local(old_val)?;
134             }
135
136             // Validity checks.
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)
146                         })
147                     }
148
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))?;
152                 }
153             }
154             EndRegion(ce) => {
155                 self.memory.locks_lifetime_ended(Some(ce));
156             }
157
158             // Defined to do nothing. These are added by optimization passes, to avoid changing the
159             // size of MIR constantly.
160             Nop => {}
161
162             InlineAsm { .. } => return Err(EvalError::InlineAsm),
163         }
164
165         self.frame_mut().stmt += 1;
166         Ok(())
167     }
168
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);
174         }
175         Ok(())
176     }
177 }
178
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> {
184     span: Span,
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>,
189 }
190
191 impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> {
192     fn global_item(
193         &mut self,
194         def_id: DefId,
195         substs: &'tcx Substs<'tcx>,
196         span: Span,
197         mutability: Mutability,
198     ) {
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) {
202             return;
203         }
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));
207             return;
208         }
209         self.try(|this| {
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(
213                     this.ecx.tcx,
214                     ty::ParamEnv::empty(Reveal::All),
215                     span);
216             let mutability = if mutability == Mutability::Mutable || internally_mutable {
217                 Mutability::Mutable
218             } else {
219                 Mutability::Immutable
220             };
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(
225                 instance,
226                 span,
227                 mir,
228                 Lvalue::Global(cid),
229                 cleanup,
230             )
231         });
232     }
233
234     fn try<F: FnOnce(&mut Self) -> EvalResult<'tcx>>(&mut self, f: F) {
235         if let Ok(ref mut n) = *self.new_constants {
236             *n += 1;
237         } else {
238             return;
239         }
240         if let Err(e) = f(self) {
241             *self.new_constants = Err(e);
242         }
243     }
244 }
245
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);
254             },
255             mir::Literal::Promoted { index } => {
256                 let cid = GlobalId {
257                     instance: self.instance,
258                     promoted: Some(index),
259                 };
260                 if self.ecx.globals.contains_key(&cid) {
261                     return;
262                 }
263                 let mir = &self.mir.promoted[index];
264                 self.try(|this| {
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,
269                                               constant.span,
270                                               mir,
271                                               Lvalue::Global(cid),
272                                               StackPopCleanup::MarkStatic(Mutability::Immutable),
273                     )
274                 });
275             }
276         }
277     }
278
279     fn visit_lvalue(
280         &mut self,
281         lvalue: &mir::Lvalue<'tcx>,
282         context: LvalueContext<'tcx>,
283         location: mir::Location
284     ) {
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 });
294                         return;
295                     } else {
296                         bug!("static def id doesn't point to static");
297                     }
298                 } else {
299                     bug!("static def id doesn't point to item");
300                 }
301             } else {
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 });
305                 } else {
306                     bug!("static found but isn't a static: {:?}", def);
307                 }
308             }
309         }
310     }
311 }