]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/const_eval.rs
rustbuild: fix remap-debuginfo when building a release
[rust.git] / src / librustc_mir / const_eval.rs
1 // Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 // Not in interpret to make sure we do not use private implementation details
12
13 use std::fmt;
14 use std::error::Error;
15 use std::borrow::{Borrow, Cow};
16 use std::hash::Hash;
17 use std::collections::hash_map::Entry;
18
19 use rustc::hir::{self, def_id::DefId};
20 use rustc::mir::interpret::ConstEvalErr;
21 use rustc::mir;
22 use rustc::ty::{self, Ty, TyCtxt, Instance, query::TyCtxtAt};
23 use rustc::ty::layout::{self, Size, LayoutOf, TyLayout};
24 use rustc::ty::subst::Subst;
25 use rustc_data_structures::indexed_vec::IndexVec;
26 use rustc_data_structures::fx::FxHashMap;
27
28 use syntax::ast::Mutability;
29 use syntax::source_map::{Span, DUMMY_SP};
30
31 use interpret::{self,
32     PlaceTy, MemPlace, OpTy, Operand, Value, Pointer, Scalar, ConstValue,
33     EvalResult, EvalError, EvalErrorKind, GlobalId, EvalContext, StackPopCleanup,
34     Allocation, AllocId, MemoryKind,
35     snapshot,
36 };
37
38 /// Number of steps until the detector even starts doing anything.
39 /// Also, a warning is shown to the user when this number is reached.
40 const STEPS_UNTIL_DETECTOR_ENABLED: isize = 1_000_000;
41 /// The number of steps between loop detector snapshots.
42 /// Should be a power of two for performance reasons.
43 const DETECTOR_SNAPSHOT_PERIOD: isize = 256;
44
45 pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>(
46     tcx: TyCtxt<'a, 'tcx, 'tcx>,
47     instance: Instance<'tcx>,
48     mir: &'mir mir::Mir<'tcx>,
49     span: Span,
50 ) -> EvalResult<'tcx, CompileTimeEvalContext<'a, 'mir, 'tcx>> {
51     debug!("mk_borrowck_eval_cx: {:?}", instance);
52     let param_env = tcx.param_env(instance.def_id());
53     let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeInterpreter::new());
54     // insert a stack frame so any queries have the correct substs
55     // cannot use `push_stack_frame`; if we do `const_prop` explodes
56     ecx.stack.push(interpret::Frame {
57         block: mir::START_BLOCK,
58         locals: IndexVec::new(),
59         instance,
60         span,
61         mir,
62         return_place: None,
63         return_to_block: StackPopCleanup::Goto(None), // never pop
64         stmt: 0,
65     });
66     Ok(ecx)
67 }
68
69 pub fn mk_eval_cx<'a, 'tcx>(
70     tcx: TyCtxt<'a, 'tcx, 'tcx>,
71     instance: Instance<'tcx>,
72     param_env: ty::ParamEnv<'tcx>,
73 ) -> EvalResult<'tcx, CompileTimeEvalContext<'a, 'tcx, 'tcx>> {
74     debug!("mk_eval_cx: {:?}, {:?}", instance, param_env);
75     let span = tcx.def_span(instance.def_id());
76     let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeInterpreter::new());
77     let mir = ecx.load_mir(instance.def)?;
78     // insert a stack frame so any queries have the correct substs
79     ecx.push_stack_frame(
80         instance,
81         mir.span,
82         mir,
83         None,
84         StackPopCleanup::Goto(None), // never pop
85     )?;
86     Ok(ecx)
87 }
88
89 pub(crate) fn eval_promoted<'a, 'mir, 'tcx>(
90     tcx: TyCtxt<'a, 'tcx, 'tcx>,
91     cid: GlobalId<'tcx>,
92     mir: &'mir mir::Mir<'tcx>,
93     param_env: ty::ParamEnv<'tcx>,
94 ) -> EvalResult<'tcx, OpTy<'tcx>> {
95     let mut ecx = mk_borrowck_eval_cx(tcx, cid.instance, mir, DUMMY_SP).unwrap();
96     eval_body_using_ecx(&mut ecx, cid, Some(mir), param_env)
97 }
98
99 pub fn op_to_const<'tcx>(
100     ecx: &CompileTimeEvalContext<'_, '_, 'tcx>,
101     op: OpTy<'tcx>,
102     may_normalize: bool,
103 ) -> EvalResult<'tcx, &'tcx ty::Const<'tcx>> {
104     // We do not normalize just any data.  Only scalar layout and fat pointers.
105     let normalize = may_normalize
106         && match op.layout.abi {
107             layout::Abi::Scalar(..) => true,
108             layout::Abi::ScalarPair(..) => {
109                 // Must be a fat pointer
110                 op.layout.ty.builtin_deref(true).is_some()
111             },
112             _ => false,
113         };
114     let normalized_op = if normalize {
115         ecx.try_read_value(op)?
116     } else {
117         match op.op {
118             Operand::Indirect(mplace) => Err(mplace),
119             Operand::Immediate(val) => Ok(val)
120         }
121     };
122     let val = match normalized_op {
123         Err(MemPlace { ptr, align, meta }) => {
124             // extract alloc-offset pair
125             assert!(meta.is_none());
126             let ptr = ptr.to_ptr()?;
127             let alloc = ecx.memory.get(ptr.alloc_id)?;
128             assert!(alloc.align.abi() >= align.abi());
129             assert!(alloc.bytes.len() as u64 - ptr.offset.bytes() >= op.layout.size.bytes());
130             let mut alloc = alloc.clone();
131             alloc.align = align;
132             // FIXME shouldnt it be the case that `mark_static_initialized` has already
133             // interned this?  I thought that is the entire point of that `FinishStatic` stuff?
134             let alloc = ecx.tcx.intern_const_alloc(alloc);
135             ConstValue::ByRef(ptr.alloc_id, alloc, ptr.offset)
136         },
137         Ok(Value::Scalar(x)) =>
138             ConstValue::Scalar(x.not_undef()?),
139         Ok(Value::ScalarPair(a, b)) =>
140             ConstValue::ScalarPair(a.not_undef()?, b.not_undef()?),
141     };
142     Ok(ty::Const::from_const_value(ecx.tcx.tcx, val, op.layout.ty))
143 }
144
145 fn eval_body_and_ecx<'a, 'mir, 'tcx>(
146     tcx: TyCtxt<'a, 'tcx, 'tcx>,
147     cid: GlobalId<'tcx>,
148     mir: Option<&'mir mir::Mir<'tcx>>,
149     param_env: ty::ParamEnv<'tcx>,
150 ) -> (EvalResult<'tcx, OpTy<'tcx>>, CompileTimeEvalContext<'a, 'mir, 'tcx>) {
151     // we start out with the best span we have
152     // and try improving it down the road when more information is available
153     let span = tcx.def_span(cid.instance.def_id());
154     let span = mir.map(|mir| mir.span).unwrap_or(span);
155     let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeInterpreter::new());
156     let r = eval_body_using_ecx(&mut ecx, cid, mir, param_env);
157     (r, ecx)
158 }
159
160 // Returns a pointer to where the result lives
161 fn eval_body_using_ecx<'mir, 'tcx>(
162     ecx: &mut CompileTimeEvalContext<'_, 'mir, 'tcx>,
163     cid: GlobalId<'tcx>,
164     mir: Option<&'mir mir::Mir<'tcx>>,
165     param_env: ty::ParamEnv<'tcx>,
166 ) -> EvalResult<'tcx, OpTy<'tcx>> {
167     debug!("eval_body_using_ecx: {:?}, {:?}", cid, param_env);
168     let tcx = ecx.tcx.tcx;
169     let mut mir = match mir {
170         Some(mir) => mir,
171         None => ecx.load_mir(cid.instance.def)?,
172     };
173     if let Some(index) = cid.promoted {
174         mir = &mir.promoted[index];
175     }
176     let layout = ecx.layout_of(mir.return_ty().subst(tcx, cid.instance.substs))?;
177     assert!(!layout.is_unsized());
178     let ret = ecx.allocate(layout, MemoryKind::Stack)?;
179
180     let name = ty::tls::with(|tcx| tcx.item_path_str(cid.instance.def_id()));
181     let prom = cid.promoted.map_or(String::new(), |p| format!("::promoted[{:?}]", p));
182     trace!("eval_body_using_ecx: pushing stack frame for global: {}{}", name, prom);
183     assert!(mir.arg_count == 0);
184     ecx.push_stack_frame(
185         cid.instance,
186         mir.span,
187         mir,
188         Some(ret.into()),
189         StackPopCleanup::None { cleanup: false },
190     )?;
191
192     // The main interpreter loop.
193     ecx.run()?;
194
195     // Intern the result
196     let internally_mutable = !layout.ty.is_freeze(tcx, param_env, mir.span);
197     let is_static = tcx.is_static(cid.instance.def_id());
198     let mutability = if is_static == Some(hir::Mutability::MutMutable) || internally_mutable {
199         Mutability::Mutable
200     } else {
201         Mutability::Immutable
202     };
203     ecx.memory.intern_static(ret.ptr.to_ptr()?.alloc_id, mutability)?;
204
205     debug!("eval_body_using_ecx done: {:?}", *ret);
206     Ok(ret.into())
207 }
208
209 impl<'tcx> Into<EvalError<'tcx>> for ConstEvalError {
210     fn into(self) -> EvalError<'tcx> {
211         EvalErrorKind::MachineError(self.to_string()).into()
212     }
213 }
214
215 #[derive(Clone, Debug)]
216 enum ConstEvalError {
217     NeedsRfc(String),
218 }
219
220 impl fmt::Display for ConstEvalError {
221     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
222         use self::ConstEvalError::*;
223         match *self {
224             NeedsRfc(ref msg) => {
225                 write!(
226                     f,
227                     "\"{}\" needs an rfc before being allowed inside constants",
228                     msg
229                 )
230             }
231         }
232     }
233 }
234
235 impl Error for ConstEvalError {
236     fn description(&self) -> &str {
237         use self::ConstEvalError::*;
238         match *self {
239             NeedsRfc(_) => "this feature needs an rfc before being allowed inside constants",
240         }
241     }
242
243     fn cause(&self) -> Option<&dyn Error> {
244         None
245     }
246 }
247
248 // Extra machine state for CTFE, and the Machine instance
249 pub struct CompileTimeInterpreter<'a, 'mir, 'tcx: 'a+'mir> {
250     /// When this value is negative, it indicates the number of interpreter
251     /// steps *until* the loop detector is enabled. When it is positive, it is
252     /// the number of steps after the detector has been enabled modulo the loop
253     /// detector period.
254     pub(super) steps_since_detector_enabled: isize,
255
256     /// Extra state to detect loops.
257     pub(super) loop_detector: snapshot::InfiniteLoopDetector<'a, 'mir, 'tcx>,
258 }
259
260 impl<'a, 'mir, 'tcx> CompileTimeInterpreter<'a, 'mir, 'tcx> {
261     fn new() -> Self {
262         CompileTimeInterpreter {
263             loop_detector: Default::default(),
264             steps_since_detector_enabled: -STEPS_UNTIL_DETECTOR_ENABLED,
265         }
266     }
267 }
268
269 impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxHashMap<K, V> {
270     #[inline(always)]
271     fn contains_key<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> bool
272         where K: Borrow<Q>
273     {
274         FxHashMap::contains_key(self, k)
275     }
276
277     #[inline(always)]
278     fn insert(&mut self, k: K, v: V) -> Option<V>
279     {
280         FxHashMap::insert(self, k, v)
281     }
282
283     #[inline(always)]
284     fn remove<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> Option<V>
285         where K: Borrow<Q>
286     {
287         FxHashMap::remove(self, k)
288     }
289
290     #[inline(always)]
291     fn filter_map_collect<T>(&self, mut f: impl FnMut(&K, &V) -> Option<T>) -> Vec<T> {
292         self.iter()
293             .filter_map(move |(k, v)| f(k, &*v))
294             .collect()
295     }
296
297     #[inline(always)]
298     fn get_or<E>(
299         &self,
300         k: K,
301         vacant: impl FnOnce() -> Result<V, E>
302     ) -> Result<&V, E>
303     {
304         match self.get(&k) {
305             Some(v) => Ok(v),
306             None => {
307                 vacant()?;
308                 bug!("The CTFE machine shouldn't ever need to extend the alloc_map when reading")
309             }
310         }
311     }
312
313     #[inline(always)]
314     fn get_mut_or<E>(
315         &mut self,
316         k: K,
317         vacant: impl FnOnce() -> Result<V, E>
318     ) -> Result<&mut V, E>
319     {
320         match self.entry(k) {
321             Entry::Occupied(e) => Ok(e.into_mut()),
322             Entry::Vacant(e) => {
323                 let v = vacant()?;
324                 Ok(e.insert(v))
325             }
326         }
327     }
328 }
329
330 type CompileTimeEvalContext<'a, 'mir, 'tcx> =
331     EvalContext<'a, 'mir, 'tcx, CompileTimeInterpreter<'a, 'mir, 'tcx>>;
332
333 impl interpret::MayLeak for ! {
334     #[inline(always)]
335     fn may_leak(self) -> bool {
336         // `self` is uninhabited
337         self
338     }
339 }
340
341 impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx>
342     for CompileTimeInterpreter<'a, 'mir, 'tcx>
343 {
344     type MemoryKinds = !;
345     type AllocExtra = ();
346     type PointerTag = ();
347
348     type MemoryMap = FxHashMap<AllocId, (MemoryKind<!>, Allocation)>;
349
350     const STATIC_KIND: Option<!> = None; // no copying of statics allowed
351     const ENABLE_PTR_TRACKING_HOOKS: bool = false; // we don't have no provenance
352
353     #[inline(always)]
354     fn enforce_validity(_ecx: &EvalContext<'a, 'mir, 'tcx, Self>) -> bool {
355         false // for now, we don't enforce validity
356     }
357
358     fn find_fn(
359         ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
360         instance: ty::Instance<'tcx>,
361         args: &[OpTy<'tcx>],
362         dest: Option<PlaceTy<'tcx>>,
363         ret: Option<mir::BasicBlock>,
364     ) -> EvalResult<'tcx, Option<&'mir mir::Mir<'tcx>>> {
365         debug!("eval_fn_call: {:?}", instance);
366         if !ecx.tcx.is_const_fn(instance.def_id()) {
367             // Some functions we support even if they are non-const -- but avoid testing
368             // that for const fn!
369             if ecx.hook_fn(instance, args, dest)? {
370                 ecx.goto_block(ret)?; // fully evaluated and done
371                 return Ok(None);
372             }
373         }
374         // This is a const fn. Call it.
375         Ok(Some(match ecx.load_mir(instance.def) {
376             Ok(mir) => mir,
377             Err(err) => {
378                 if let EvalErrorKind::NoMirFor(ref path) = err.kind {
379                     return Err(
380                         ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path))
381                             .into(),
382                     );
383                 }
384                 return Err(err);
385             }
386         }))
387     }
388
389     fn call_intrinsic(
390         ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
391         instance: ty::Instance<'tcx>,
392         args: &[OpTy<'tcx>],
393         dest: PlaceTy<'tcx>,
394     ) -> EvalResult<'tcx> {
395         if ecx.emulate_intrinsic(instance, args, dest)? {
396             return Ok(());
397         }
398         // An intrinsic that we do not support
399         let intrinsic_name = &ecx.tcx.item_name(instance.def_id()).as_str()[..];
400         Err(
401             ConstEvalError::NeedsRfc(format!("calling intrinsic `{}`", intrinsic_name)).into()
402         )
403     }
404
405     fn ptr_op(
406         _ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
407         _bin_op: mir::BinOp,
408         _left: Scalar,
409         _left_layout: TyLayout<'tcx>,
410         _right: Scalar,
411         _right_layout: TyLayout<'tcx>,
412     ) -> EvalResult<'tcx, (Scalar, bool)> {
413         Err(
414             ConstEvalError::NeedsRfc("pointer arithmetic or comparison".to_string()).into(),
415         )
416     }
417
418     fn find_foreign_static(
419         _tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
420         _def_id: DefId,
421     ) -> EvalResult<'tcx, Cow<'tcx, Allocation<Self::PointerTag>>> {
422         err!(ReadForeignStatic)
423     }
424
425     #[inline(always)]
426     fn static_with_default_tag(
427         alloc: &'_ Allocation
428     ) -> Cow<'_, Allocation<Self::PointerTag>> {
429         // We do not use a tag so we can just cheaply forward the reference
430         Cow::Borrowed(alloc)
431     }
432
433     fn box_alloc(
434         _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
435         _dest: PlaceTy<'tcx>,
436     ) -> EvalResult<'tcx> {
437         Err(
438             ConstEvalError::NeedsRfc("heap allocations via `box` keyword".to_string()).into(),
439         )
440     }
441
442     fn before_terminator(ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>) -> EvalResult<'tcx> {
443         {
444             let steps = &mut ecx.machine.steps_since_detector_enabled;
445
446             *steps += 1;
447             if *steps < 0 {
448                 return Ok(());
449             }
450
451             *steps %= DETECTOR_SNAPSHOT_PERIOD;
452             if *steps != 0 {
453                 return Ok(());
454             }
455         }
456
457         let span = ecx.frame().span;
458         ecx.machine.loop_detector.observe_and_analyze(
459             &ecx.tcx,
460             span,
461             &ecx.memory,
462             &ecx.stack[..],
463         )
464     }
465
466     #[inline(always)]
467     fn tag_reference(
468         _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
469         _ptr: Pointer<Self::PointerTag>,
470         _pointee_ty: Ty<'tcx>,
471         _pointee_size: Size,
472         _borrow_kind: Option<mir::BorrowKind>,
473     ) -> EvalResult<'tcx, Self::PointerTag> {
474         Ok(())
475     }
476
477     #[inline(always)]
478     fn tag_dereference(
479         _ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
480         _ptr: Pointer<Self::PointerTag>,
481         _ptr_ty: Ty<'tcx>,
482     ) -> EvalResult<'tcx, Self::PointerTag> {
483         Ok(())
484     }
485 }
486
487 /// Project to a field of a (variant of a) const
488 pub fn const_field<'a, 'tcx>(
489     tcx: TyCtxt<'a, 'tcx, 'tcx>,
490     param_env: ty::ParamEnv<'tcx>,
491     instance: ty::Instance<'tcx>,
492     variant: Option<usize>,
493     field: mir::Field,
494     value: &'tcx ty::Const<'tcx>,
495 ) -> ::rustc::mir::interpret::ConstEvalResult<'tcx> {
496     trace!("const_field: {:?}, {:?}, {:?}", instance, field, value);
497     let ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
498     let result = (|| {
499         // get the operand again
500         let op = ecx.const_to_op(value)?;
501         // downcast
502         let down = match variant {
503             None => op,
504             Some(variant) => ecx.operand_downcast(op, variant)?
505         };
506         // then project
507         let field = ecx.operand_field(down, field.index() as u64)?;
508         // and finally move back to the const world, always normalizing because
509         // this is not called for statics.
510         op_to_const(&ecx, field, true)
511     })();
512     result.map_err(|err| {
513         let (trace, span) = ecx.generate_stacktrace(None);
514         ConstEvalErr {
515             error: err,
516             stacktrace: trace,
517             span,
518         }.into()
519     })
520 }
521
522 pub fn const_variant_index<'a, 'tcx>(
523     tcx: TyCtxt<'a, 'tcx, 'tcx>,
524     param_env: ty::ParamEnv<'tcx>,
525     instance: ty::Instance<'tcx>,
526     val: &'tcx ty::Const<'tcx>,
527 ) -> EvalResult<'tcx, usize> {
528     trace!("const_variant_index: {:?}, {:?}", instance, val);
529     let ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
530     let op = ecx.const_to_op(val)?;
531     Ok(ecx.read_discriminant(op)?.1)
532 }
533
534 pub fn const_to_allocation_provider<'a, 'tcx>(
535     _tcx: TyCtxt<'a, 'tcx, 'tcx>,
536     val: &'tcx ty::Const<'tcx>,
537 ) -> &'tcx Allocation {
538     // FIXME: This really does not need to be a query.  Instead, we should have a query for statics
539     // that returns an allocation directly (or an `AllocId`?), after doing a sanity check of the
540     // value and centralizing error reporting.
541     match val.val {
542         ConstValue::ByRef(_, alloc, offset) => {
543             assert_eq!(offset.bytes(), 0);
544             return alloc;
545         },
546         _ => bug!("const_to_allocation called on non-static"),
547     }
548 }
549
550 pub fn const_eval_provider<'a, 'tcx>(
551     tcx: TyCtxt<'a, 'tcx, 'tcx>,
552     key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
553 ) -> ::rustc::mir::interpret::ConstEvalResult<'tcx> {
554     trace!("const eval: {:?}", key);
555     let cid = key.value;
556     let def_id = cid.instance.def.def_id();
557
558     if let Some(id) = tcx.hir.as_local_node_id(def_id) {
559         let tables = tcx.typeck_tables_of(def_id);
560         let span = tcx.def_span(def_id);
561
562         // Do match-check before building MIR
563         if tcx.check_match(def_id).is_err() {
564             return Err(ConstEvalErr {
565                 error: EvalErrorKind::CheckMatchError.into(),
566                 stacktrace: vec![],
567                 span,
568             }.into());
569         }
570
571         if let hir::BodyOwnerKind::Const = tcx.hir.body_owner_kind(id) {
572             tcx.mir_const_qualif(def_id);
573         }
574
575         // Do not continue into miri if typeck errors occurred; it will fail horribly
576         if tables.tainted_by_errors {
577             return Err(ConstEvalErr {
578                 error: EvalErrorKind::CheckMatchError.into(),
579                 stacktrace: vec![],
580                 span,
581             }.into());
582         }
583     };
584
585     let (res, ecx) = eval_body_and_ecx(tcx, cid, None, key.param_env);
586     res.and_then(|op| {
587         let normalize = tcx.is_static(def_id).is_none() && cid.promoted.is_none();
588         if !normalize {
589             // Sanity check: These must always be a MemPlace
590             match op.op {
591                 Operand::Indirect(_) => { /* all is good */ },
592                 Operand::Immediate(_) => bug!("const eval gave us an Immediate"),
593             }
594         }
595         op_to_const(&ecx, op, normalize)
596     }).map_err(|err| {
597         let (trace, span) = ecx.generate_stacktrace(None);
598         let err = ConstEvalErr {
599             error: err,
600             stacktrace: trace,
601             span,
602         };
603         if tcx.is_static(def_id).is_some() {
604             err.report_as_error(ecx.tcx, "could not evaluate static initializer");
605             if tcx.sess.err_count() == 0 {
606                 span_bug!(span, "static eval failure didn't emit an error: {:#?}", err);
607             }
608         }
609         err.into()
610     })
611 }