]> git.lizzy.rs Git - rust.git/blob - src/lib.rs
rustup for big refactor; kill most of validation
[rust.git] / src / lib.rs
1 #![feature(
2     rustc_private,
3     catch_expr,
4 )]
5
6 #![cfg_attr(feature = "cargo-clippy", allow(cast_lossless))]
7
8 #[macro_use]
9 extern crate log;
10
11 // From rustc.
12 #[macro_use]
13 extern crate rustc;
14 extern crate rustc_data_structures;
15 extern crate rustc_mir;
16 extern crate rustc_target;
17 extern crate syntax;
18
19 use rustc::ty::{self, TyCtxt};
20 use rustc::ty::layout::{TyLayout, LayoutOf, Size};
21 use rustc::ty::subst::Subst;
22 use rustc::hir::def_id::DefId;
23 use rustc::mir;
24
25 use rustc_data_structures::fx::FxHasher;
26
27 use syntax::ast::Mutability;
28 use syntax::codemap::Span;
29
30 use std::marker::PhantomData;
31 use std::collections::{HashMap, BTreeMap};
32 use std::hash::{Hash, Hasher};
33
34 pub use rustc::mir::interpret::*;
35 pub use rustc_mir::interpret::*;
36
37 mod fn_call;
38 mod operator;
39 mod intrinsic;
40 mod helpers;
41 mod memory;
42 mod tls;
43 mod locks;
44 mod range_map;
45
46 use fn_call::EvalContextExt as MissingFnsEvalContextExt;
47 use operator::EvalContextExt as OperatorEvalContextExt;
48 use intrinsic::EvalContextExt as IntrinsicEvalContextExt;
49 use tls::EvalContextExt as TlsEvalContextExt;
50 use locks::LockInfo;
51 use range_map::RangeMap;
52 use helpers::{ScalarExt, FalibleScalarExt};
53
54 pub fn create_ecx<'a, 'mir: 'a, 'tcx: 'mir>(
55     tcx: TyCtxt<'a, 'tcx, 'tcx>,
56     main_id: DefId,
57     start_wrapper: Option<DefId>,
58 ) -> EvalResult<'tcx, (EvalContext<'a, 'mir, 'tcx, Evaluator<'tcx>>, Option<Pointer>)> {
59     let mut ecx = EvalContext::new(
60         tcx.at(syntax::codemap::DUMMY_SP),
61         ty::ParamEnv::reveal_all(),
62         Default::default(),
63         MemoryData::new()
64     );
65
66     let main_instance = ty::Instance::mono(ecx.tcx.tcx, main_id);
67     let main_mir = ecx.load_mir(main_instance.def)?;
68     let mut cleanup_ptr = None; // Scalar to be deallocated when we are done
69
70     if !main_mir.return_ty().is_nil() || main_mir.arg_count != 0 {
71         return err!(Unimplemented(
72             "miri does not support main functions without `fn()` type signatures"
73                 .to_owned(),
74         ));
75     }
76     let ptr_size = ecx.memory.pointer_size();
77
78     if let Some(start_id) = start_wrapper {
79         let main_ret_ty = ecx.tcx.fn_sig(main_id).output();
80         let main_ret_ty = main_ret_ty.no_late_bound_regions().unwrap();
81         let start_instance = ty::Instance::resolve(
82             ecx.tcx.tcx,
83             ty::ParamEnv::reveal_all(),
84             start_id,
85             ecx.tcx.mk_substs(
86                 ::std::iter::once(ty::subst::Kind::from(main_ret_ty)))
87             ).unwrap();
88         let start_mir = ecx.load_mir(start_instance.def)?;
89
90         if start_mir.arg_count != 3 {
91             return err!(AbiViolation(format!(
92                 "'start' lang item should have three arguments, but has {}",
93                 start_mir.arg_count
94             )));
95         }
96
97         // Return value
98         let size = ecx.tcx.data_layout.pointer_size;
99         let align = ecx.tcx.data_layout.pointer_align;
100         let ret_ptr = ecx.memory_mut().allocate(size, align, MemoryKind::Stack)?;
101         cleanup_ptr = Some(ret_ptr);
102
103         // Push our stack frame
104         ecx.push_stack_frame(
105             start_instance,
106             start_mir.span,
107             start_mir,
108             Place::from_ptr(ret_ptr, align),
109             StackPopCleanup::None,
110         )?;
111
112         let mut args = ecx.frame().mir.args_iter();
113
114         // First argument: pointer to main()
115         let main_ptr = ecx.memory_mut().create_fn_alloc(main_instance);
116         let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
117         ecx.write_scalar(Scalar::Ptr(main_ptr), dest)?;
118
119         // Second argument (argc): 1
120         let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
121         ecx.write_scalar(Scalar::from_int(1, dest.layout.size), dest)?;
122
123         // FIXME: extract main source file path
124         // Third argument (argv): &[b"foo"]
125         let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
126         let foo = ecx.memory.allocate_bytes(b"foo\0");
127         let foo_ty = ecx.tcx.mk_imm_ptr(ecx.tcx.types.u8);
128         let foo_layout = ecx.layout_of(foo_ty)?;
129         let foo_place = ecx.allocate(foo_layout, MemoryKind::Stack)?;
130         ecx.write_scalar(Scalar::Ptr(foo), foo_place.into())?;
131         ecx.memory.mark_static_initialized(foo_place.to_ptr()?.alloc_id, Mutability::Immutable)?;
132         ecx.write_scalar(foo_place.ptr, dest)?;
133
134         assert!(args.next().is_none(), "start lang item has more arguments than expected");
135     } else {
136         ecx.push_stack_frame(
137             main_instance,
138             main_mir.span,
139             main_mir,
140             Place::from_scalar_ptr(Scalar::from_int(1, ptr_size).into(), ty::layout::Align::from_bytes(1, 1).unwrap()),
141             StackPopCleanup::None,
142         )?;
143
144         // No arguments
145         let mut args = ecx.frame().mir.args_iter();
146         assert!(args.next().is_none(), "main function must not have arguments");
147     }
148
149     Ok((ecx, cleanup_ptr))
150 }
151
152 pub fn eval_main<'a, 'tcx: 'a>(
153     tcx: TyCtxt<'a, 'tcx, 'tcx>,
154     main_id: DefId,
155     start_wrapper: Option<DefId>,
156 ) {
157     let (mut ecx, cleanup_ptr) = create_ecx(tcx, main_id, start_wrapper).expect("Couldn't create ecx");
158
159     let res: EvalResult = do catch {
160         while ecx.step()? {}
161         ecx.run_tls_dtors()?;
162         if let Some(cleanup_ptr) = cleanup_ptr {
163             ecx.memory_mut().deallocate(
164                 cleanup_ptr,
165                 None,
166                 MemoryKind::Stack,
167             )?;
168         }
169     };
170
171     match res {
172         Ok(()) => {
173             let leaks = ecx.memory().leak_report();
174             if leaks != 0 {
175                 // TODO: Prevent leaks which aren't supposed to be there
176                 //tcx.sess.err("the evaluated program leaked memory");
177             }
178         }
179         Err(e) => {
180             if let Some(frame) = ecx.stack().last() {
181                 let block = &frame.mir.basic_blocks()[frame.block];
182                 let span = if frame.stmt < block.statements.len() {
183                     block.statements[frame.stmt].source_info.span
184                 } else {
185                     block.terminator().source_info.span
186                 };
187
188                 let e = e.to_string();
189                 let msg = format!("constant evaluation error: {}", e);
190                 let mut err = struct_error(ecx.tcx.tcx.at(span), msg.as_str());
191                 let (frames, span) = ecx.generate_stacktrace(None);
192                 err.span_label(span, e);
193                 for FrameInfo { span, location, .. } in frames {
194                     err.span_note(span, &format!("inside call to `{}`", location));
195                 }
196                 err.emit();
197             } else {
198                 ecx.tcx.sess.err(&e.to_string());
199             }
200
201             for (i, frame) in ecx.stack().iter().enumerate() {
202                 trace!("-------------------");
203                 trace!("Frame {}", i);
204                 trace!("    return: {:#?}", frame.return_place);
205                 for (i, local) in frame.locals.iter().enumerate() {
206                     if let Ok(local) = local.access() {
207                         trace!("    local {}: {:?}", i, local);
208                     }
209                 }
210             }
211         }
212     }
213 }
214
215 #[derive(Clone, Default, PartialEq, Eq)]
216 pub struct Evaluator<'tcx> {
217     /// Environment variables set by `setenv`
218     /// Miri does not expose env vars from the host to the emulated program
219     pub(crate) env_vars: HashMap<Vec<u8>, Pointer>,
220
221     /// Use the lifetime
222     _dummy : PhantomData<&'tcx ()>,
223 }
224
225 impl<'tcx> Hash for Evaluator<'tcx> {
226     fn hash<H: Hasher>(&self, state: &mut H) {
227         let Evaluator {
228             env_vars,
229             _dummy: _,
230         } = self;
231
232         env_vars.iter()
233             .map(|(env, ptr)| {
234                 let mut h = FxHasher::default();
235                 env.hash(&mut h);
236                 ptr.hash(&mut h);
237                 h.finish()
238             })
239             .fold(0u64, |acc, hash| acc.wrapping_add(hash))
240             .hash(state);
241     }
242 }
243
244 pub type TlsKey = u128;
245
246 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
247 pub struct TlsEntry<'tcx> {
248     data: Scalar, // Will eventually become a map from thread IDs to `Scalar`s, if we ever support more than one thread.
249     dtor: Option<ty::Instance<'tcx>>,
250 }
251
252 #[derive(Clone, PartialEq, Eq)]
253 pub struct MemoryData<'tcx> {
254     /// The Key to use for the next thread-local allocation.
255     next_thread_local: TlsKey,
256
257     /// pthreads-style thread-local storage.
258     thread_local: BTreeMap<TlsKey, TlsEntry<'tcx>>,
259
260     /// Memory regions that are locked by some function
261     ///
262     /// Only mutable (static mut, heap, stack) allocations have an entry in this map.
263     /// The entry is created when allocating the memory and deleted after deallocation.
264     locks: HashMap<AllocId, RangeMap<LockInfo<'tcx>>>,
265
266     statics: HashMap<GlobalId<'tcx>, AllocId>,
267 }
268
269 impl<'tcx> MemoryData<'tcx> {
270     fn new() -> Self {
271         MemoryData {
272             next_thread_local: 1, // start with 1 as we must not use 0 on Windows
273             thread_local: BTreeMap::new(),
274             locks: HashMap::new(),
275             statics: HashMap::new(),
276         }
277     }
278 }
279
280 impl<'tcx> Hash for MemoryData<'tcx> {
281     fn hash<H: Hasher>(&self, state: &mut H) {
282         let MemoryData {
283             next_thread_local: _,
284             thread_local,
285             locks: _,
286             statics: _,
287         } = self;
288
289         thread_local.hash(state);
290     }
291 }
292
293 impl<'mir, 'tcx: 'mir> Machine<'mir, 'tcx> for Evaluator<'tcx> {
294     type MemoryData = MemoryData<'tcx>;
295     type MemoryKinds = memory::MemoryKind;
296
297     /// Returns Ok() when the function was handled, fail otherwise
298     fn eval_fn_call<'a>(
299         ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
300         instance: ty::Instance<'tcx>,
301         destination: Option<(PlaceTy<'tcx>, mir::BasicBlock)>,
302         args: &[OpTy<'tcx>],
303         span: Span,
304     ) -> EvalResult<'tcx, bool> {
305         ecx.eval_fn_call(instance, destination, args, span)
306     }
307
308     fn call_intrinsic<'a>(
309         ecx: &mut rustc_mir::interpret::EvalContext<'a, 'mir, 'tcx, Self>,
310         instance: ty::Instance<'tcx>,
311         args: &[OpTy<'tcx>],
312         dest: PlaceTy<'tcx>,
313         target: mir::BasicBlock,
314     ) -> EvalResult<'tcx> {
315         ecx.call_intrinsic(instance, args, dest, target)
316     }
317
318     fn try_ptr_op<'a>(
319         ecx: &rustc_mir::interpret::EvalContext<'a, 'mir, 'tcx, Self>,
320         bin_op: mir::BinOp,
321         left: Scalar,
322         left_layout: TyLayout<'tcx>,
323         right: Scalar,
324         right_layout: TyLayout<'tcx>,
325     ) -> EvalResult<'tcx, Option<(Scalar, bool)>> {
326         ecx.ptr_op(bin_op, left, left_layout, right, right_layout)
327     }
328
329     fn mark_static_initialized<'a>(
330         mem: &mut Memory<'a, 'mir, 'tcx, Self>,
331         id: AllocId,
332         _mutability: Mutability,
333     ) -> EvalResult<'tcx, bool> {
334         use memory::MemoryKind::*;
335         match mem.get_alloc_kind(id) {
336             // FIXME: This could be allowed, but not for env vars set during miri execution
337             Some(MemoryKind::Machine(Env)) => err!(Unimplemented("statics can't refer to env vars".to_owned())),
338             _ => Ok(false), // TODO: What does the bool mean?
339         }
340     }
341
342     fn init_static<'a>(
343         ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
344         cid: GlobalId<'tcx>,
345     ) -> EvalResult<'tcx, AllocId> {
346         // Step 1: If the static has already been evaluated return the cached version
347         if let Some(alloc_id) = ecx.memory.data.statics.get(&cid) {
348             return Ok(*alloc_id);
349         }
350
351         let tcx = ecx.tcx.tcx;
352
353         // Step 2: Load mir
354         let mut mir = ecx.load_mir(cid.instance.def)?;
355         if let Some(index) = cid.promoted {
356             mir = &mir.promoted[index];
357         }
358         assert!(mir.arg_count == 0);
359
360         // Step 3: Allocate storage
361         let layout = ecx.layout_of(mir.return_ty().subst(tcx, cid.instance.substs))?;
362         assert!(!layout.is_unsized());
363         let ptr = ecx.memory.allocate(
364             layout.size,
365             layout.align,
366             MemoryKind::Stack,
367         )?;
368
369         // Step 4: Cache allocation id for recursive statics
370         assert!(ecx.memory.data.statics.insert(cid, ptr.alloc_id).is_none());
371
372         // Step 5: Push stackframe to evaluate static
373         let cleanup = StackPopCleanup::None;
374         ecx.push_stack_frame(
375             cid.instance,
376             mir.span,
377             mir,
378             Place::from_ptr(ptr, layout.align),
379             cleanup,
380         )?;
381
382         // Step 6: Step until static has been initialized
383         let call_stackframe = ecx.stack().len();
384         while ecx.step()? && ecx.stack().len() >= call_stackframe {
385             if ecx.stack().len() == call_stackframe {
386                 let cleanup = {
387                     let frame = ecx.frame();
388                     let bb = &frame.mir.basic_blocks()[frame.block];
389                     bb.statements.len() == frame.stmt && !bb.is_cleanup &&
390                         if let ::rustc::mir::TerminatorKind::Return = bb.terminator().kind { true } else { false }
391                 };
392                 if cleanup {
393                     for (local, _local_decl) in mir.local_decls.iter_enumerated().skip(1) {
394                         // Don't deallocate locals, because the return value might reference them
395                         ecx.storage_dead(local);
396                     }
397                 }
398             }
399         }
400
401         // TODO: Freeze immutable statics without copying them to the global static cache
402
403         // Step 7: Return the alloc
404         Ok(ptr.alloc_id)
405     }
406
407     fn box_alloc<'a>(
408         ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
409         dest: PlaceTy<'tcx>,
410     ) -> EvalResult<'tcx> {
411         trace!("box_alloc for {:?}", dest.layout.ty);
412         // Call the `exchange_malloc` lang item
413         let malloc = ecx.tcx.lang_items().exchange_malloc_fn().unwrap();
414         let malloc = ty::Instance::mono(ecx.tcx.tcx, malloc);
415         let malloc_mir = ecx.load_mir(malloc.def)?;
416         ecx.push_stack_frame(
417             malloc,
418             malloc_mir.span,
419             malloc_mir,
420             *dest,
421             // Don't do anything when we are done.  The statement() function will increment
422             // the old stack frame's stmt counter to the next statement, which means that when
423             // exchange_malloc returns, we go on evaluating exactly where we want to be.
424             StackPopCleanup::None,
425         )?;
426
427         let mut args = ecx.frame().mir.args_iter();
428         let layout = ecx.layout_of(dest.layout.ty.builtin_deref(false).unwrap().ty)?;
429
430         // First argument: size
431         // (0 is allowed here, this is expected to be handled by the lang item)
432         let arg = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
433         let size = layout.size.bytes();
434         ecx.write_scalar(Scalar::from_uint(size, arg.layout.size), arg)?;
435
436         // Second argument: align
437         let arg = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
438         let align = layout.align.abi();
439         ecx.write_scalar(Scalar::from_uint(align, arg.layout.size), arg)?;
440
441         // No more arguments
442         assert!(args.next().is_none(), "exchange_malloc lang item has more arguments than expected");
443         Ok(())
444     }
445
446     fn global_item_with_linkage<'a>(
447         _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
448         _instance: ty::Instance<'tcx>,
449         _mutability: Mutability,
450     ) -> EvalResult<'tcx> {
451         panic!("remove this function from rustc");
452     }
453
454     fn check_locks<'a>(
455         _mem: &Memory<'a, 'mir, 'tcx, Self>,
456         _ptr: Pointer,
457         _size: Size,
458         _access: AccessKind,
459     ) -> EvalResult<'tcx> {
460         Ok(())
461     }
462
463     fn add_lock<'a>(
464         _mem: &mut Memory<'a, 'mir, 'tcx, Self>,
465         _id: AllocId,
466     ) { }
467
468     fn free_lock<'a>(
469         _mem: &mut Memory<'a, 'mir, 'tcx, Self>,
470         _id: AllocId,
471         _len: u64,
472     ) -> EvalResult<'tcx> {
473         Ok(())
474     }
475
476     fn end_region<'a>(
477         _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
478         _reg: Option<::rustc::middle::region::Scope>,
479     ) -> EvalResult<'tcx> {
480         Ok(())
481     }
482
483     fn validation_op<'a>(
484         _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
485         _op: ::rustc::mir::ValidationOp,
486         _operand: &::rustc::mir::ValidationOperand<'tcx, ::rustc::mir::Place<'tcx>>,
487     ) -> EvalResult<'tcx> {
488         // FIXME: prevent this from ICEing
489         //ecx.validation_op(op, operand)
490         Ok(())
491     }
492 }