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.
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.
11 // Not in interpret to make sure we do not use private implementation details
14 use std::error::Error;
15 use std::borrow::{Borrow, Cow};
17 use std::collections::hash_map::Entry;
19 use rustc::hir::{self, def_id::DefId};
20 use rustc::mir::interpret::ConstEvalErr;
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;
28 use syntax::ast::Mutability;
29 use syntax::source_map::{Span, DUMMY_SP};
32 PlaceTy, MemPlace, OpTy, Operand, Value, Pointer, Scalar, ConstValue,
33 EvalResult, EvalError, EvalErrorKind, GlobalId, EvalContext, StackPopCleanup,
34 Allocation, AllocId, MemoryKind,
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;
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>,
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(),
63 return_to_block: StackPopCleanup::Goto(None), // never pop
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
84 StackPopCleanup::Goto(None), // never pop
89 pub(crate) fn eval_promoted<'a, 'mir, 'tcx>(
90 tcx: TyCtxt<'a, 'tcx, '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)
99 pub fn op_to_const<'tcx>(
100 ecx: &CompileTimeEvalContext<'_, '_, 'tcx>,
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()
114 let normalized_op = if normalize {
115 ecx.try_read_value(op)?
118 Operand::Indirect(mplace) => Err(mplace),
119 Operand::Immediate(val) => Ok(val)
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();
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)
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()?),
142 Ok(ty::Const::from_const_value(ecx.tcx.tcx, val, op.layout.ty))
145 fn eval_body_and_ecx<'a, 'mir, 'tcx>(
146 tcx: TyCtxt<'a, 'tcx, '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);
160 // Returns a pointer to where the result lives
161 fn eval_body_using_ecx<'mir, 'tcx>(
162 ecx: &mut CompileTimeEvalContext<'_, 'mir, '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 {
171 None => ecx.load_mir(cid.instance.def)?,
173 if let Some(index) = cid.promoted {
174 mir = &mir.promoted[index];
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)?;
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(
189 StackPopCleanup::None { cleanup: false },
192 // The main interpreter loop.
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 {
201 Mutability::Immutable
203 ecx.memory.intern_static(ret.ptr.to_ptr()?.alloc_id, mutability)?;
205 debug!("eval_body_using_ecx done: {:?}", *ret);
209 impl<'tcx> Into<EvalError<'tcx>> for ConstEvalError {
210 fn into(self) -> EvalError<'tcx> {
211 EvalErrorKind::MachineError(self.to_string()).into()
215 #[derive(Clone, Debug)]
216 enum ConstEvalError {
220 impl fmt::Display for ConstEvalError {
221 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
222 use self::ConstEvalError::*;
224 NeedsRfc(ref msg) => {
227 "\"{}\" needs an rfc before being allowed inside constants",
235 impl Error for ConstEvalError {
236 fn description(&self) -> &str {
237 use self::ConstEvalError::*;
239 NeedsRfc(_) => "this feature needs an rfc before being allowed inside constants",
243 fn cause(&self) -> Option<&dyn Error> {
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
254 pub(super) steps_since_detector_enabled: isize,
256 /// Extra state to detect loops.
257 pub(super) loop_detector: snapshot::InfiniteLoopDetector<'a, 'mir, 'tcx>,
260 impl<'a, 'mir, 'tcx> CompileTimeInterpreter<'a, 'mir, 'tcx> {
262 CompileTimeInterpreter {
263 loop_detector: Default::default(),
264 steps_since_detector_enabled: -STEPS_UNTIL_DETECTOR_ENABLED,
269 impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxHashMap<K, V> {
271 fn contains_key<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> bool
274 FxHashMap::contains_key(self, k)
278 fn insert(&mut self, k: K, v: V) -> Option<V>
280 FxHashMap::insert(self, k, v)
284 fn remove<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> Option<V>
287 FxHashMap::remove(self, k)
291 fn filter_map_collect<T>(&self, mut f: impl FnMut(&K, &V) -> Option<T>) -> Vec<T> {
293 .filter_map(move |(k, v)| f(k, &*v))
301 vacant: impl FnOnce() -> Result<V, E>
308 bug!("The CTFE machine shouldn't ever need to extend the alloc_map when reading")
317 vacant: impl FnOnce() -> Result<V, E>
318 ) -> Result<&mut V, E>
320 match self.entry(k) {
321 Entry::Occupied(e) => Ok(e.into_mut()),
322 Entry::Vacant(e) => {
330 type CompileTimeEvalContext<'a, 'mir, 'tcx> =
331 EvalContext<'a, 'mir, 'tcx, CompileTimeInterpreter<'a, 'mir, 'tcx>>;
333 impl interpret::MayLeak for ! {
335 fn may_leak(self) -> bool {
336 // `self` is uninhabited
341 impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx>
342 for CompileTimeInterpreter<'a, 'mir, 'tcx>
344 type MemoryKinds = !;
345 type AllocExtra = ();
346 type PointerTag = ();
348 type MemoryMap = FxHashMap<AllocId, (MemoryKind<!>, Allocation)>;
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
354 fn enforce_validity(_ecx: &EvalContext<'a, 'mir, 'tcx, Self>) -> bool {
355 false // for now, we don't enforce validity
359 ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
360 instance: ty::Instance<'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
374 // This is a const fn. Call it.
375 Ok(Some(match ecx.load_mir(instance.def) {
378 if let EvalErrorKind::NoMirFor(ref path) = err.kind {
380 ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path))
390 ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
391 instance: ty::Instance<'tcx>,
394 ) -> EvalResult<'tcx> {
395 if ecx.emulate_intrinsic(instance, args, dest)? {
398 // An intrinsic that we do not support
399 let intrinsic_name = &ecx.tcx.item_name(instance.def_id()).as_str()[..];
401 ConstEvalError::NeedsRfc(format!("calling intrinsic `{}`", intrinsic_name)).into()
406 _ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
409 _left_layout: TyLayout<'tcx>,
411 _right_layout: TyLayout<'tcx>,
412 ) -> EvalResult<'tcx, (Scalar, bool)> {
414 ConstEvalError::NeedsRfc("pointer arithmetic or comparison".to_string()).into(),
418 fn find_foreign_static(
419 _tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
421 ) -> EvalResult<'tcx, Cow<'tcx, Allocation<Self::PointerTag>>> {
422 err!(ReadForeignStatic)
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
434 _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
435 _dest: PlaceTy<'tcx>,
436 ) -> EvalResult<'tcx> {
438 ConstEvalError::NeedsRfc("heap allocations via `box` keyword".to_string()).into(),
442 fn before_terminator(ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>) -> EvalResult<'tcx> {
444 let steps = &mut ecx.machine.steps_since_detector_enabled;
451 *steps %= DETECTOR_SNAPSHOT_PERIOD;
457 let span = ecx.frame().span;
458 ecx.machine.loop_detector.observe_and_analyze(
468 _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
469 _ptr: Pointer<Self::PointerTag>,
470 _pointee_ty: Ty<'tcx>,
472 _borrow_kind: Option<mir::BorrowKind>,
473 ) -> EvalResult<'tcx, Self::PointerTag> {
479 _ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
480 _ptr: Pointer<Self::PointerTag>,
482 ) -> EvalResult<'tcx, Self::PointerTag> {
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>,
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();
499 // get the operand again
500 let op = ecx.const_to_op(value)?;
502 let down = match variant {
504 Some(variant) => ecx.operand_downcast(op, variant)?
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)
512 result.map_err(|err| {
513 let (trace, span) = ecx.generate_stacktrace(None);
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)
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.
542 ConstValue::ByRef(_, alloc, offset) => {
543 assert_eq!(offset.bytes(), 0);
546 _ => bug!("const_to_allocation called on non-static"),
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);
556 let def_id = cid.instance.def.def_id();
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);
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(),
571 if let hir::BodyOwnerKind::Const = tcx.hir.body_owner_kind(id) {
572 tcx.mir_const_qualif(def_id);
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(),
585 let (res, ecx) = eval_body_and_ecx(tcx, cid, None, key.param_env);
587 let normalize = tcx.is_static(def_id).is_none() && cid.promoted.is_none();
589 // Sanity check: These must always be a MemPlace
591 Operand::Indirect(_) => { /* all is good */ },
592 Operand::Immediate(_) => bug!("const eval gave us an Immediate"),
595 op_to_const(&ecx, op, normalize)
597 let (trace, span) = ecx.generate_stacktrace(None);
598 let err = ConstEvalErr {
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);