2018-08-22 16:52:01 -03:00
|
|
|
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
2018-08-24 16:39:25 +02:00
|
|
|
// Not in interpret to make sure we do not use private implementation details
|
|
|
|
|
2018-06-08 03:47:26 +01:00
|
|
|
use std::fmt;
|
|
|
|
use std::error::Error;
|
2018-10-05 15:13:59 +02:00
|
|
|
use std::borrow::{Borrow, Cow};
|
|
|
|
use std::hash::Hash;
|
|
|
|
use std::collections::hash_map::Entry;
|
2018-06-08 03:47:26 +01:00
|
|
|
|
2018-08-23 21:22:27 +02:00
|
|
|
use rustc::hir::{self, def_id::DefId};
|
2018-08-13 16:14:22 +02:00
|
|
|
use rustc::mir::interpret::ConstEvalErr;
|
2018-01-02 23:22:09 +00:00
|
|
|
use rustc::mir;
|
2018-10-16 17:00:39 +02:00
|
|
|
use rustc::ty::{self, Ty, TyCtxt, Instance, query::TyCtxtAt};
|
|
|
|
use rustc::ty::layout::{self, Size, LayoutOf, TyLayout};
|
2018-01-02 23:22:09 +00:00
|
|
|
use rustc::ty::subst::Subst;
|
2018-07-25 16:14:04 +03:00
|
|
|
use rustc_data_structures::indexed_vec::IndexVec;
|
2018-10-05 15:13:59 +02:00
|
|
|
use rustc_data_structures::fx::FxHashMap;
|
2017-12-12 17:14:49 +01:00
|
|
|
|
|
|
|
use syntax::ast::Mutability;
|
2018-09-20 10:12:21 +02:00
|
|
|
use syntax::source_map::{Span, DUMMY_SP};
|
2017-12-12 17:14:49 +01:00
|
|
|
|
2018-08-24 16:39:25 +02:00
|
|
|
use interpret::{self,
|
2018-10-16 17:00:39 +02:00
|
|
|
PlaceTy, MemPlace, OpTy, Operand, Value, Pointer, Scalar, ConstValue,
|
|
|
|
EvalResult, EvalError, EvalErrorKind, GlobalId, EvalContext, StackPopCleanup,
|
|
|
|
Allocation, AllocId, MemoryKind,
|
2018-09-20 10:12:21 +02:00
|
|
|
snapshot,
|
2018-04-26 09:18:19 +02:00
|
|
|
};
|
2017-12-12 17:14:49 +01:00
|
|
|
|
2018-09-20 11:57:45 +02:00
|
|
|
/// Number of steps until the detector even starts doing anything.
|
|
|
|
/// Also, a warning is shown to the user when this number is reached.
|
|
|
|
const STEPS_UNTIL_DETECTOR_ENABLED: isize = 1_000_000;
|
|
|
|
/// The number of steps between loop detector snapshots.
|
|
|
|
/// Should be a power of two for performance reasons.
|
|
|
|
const DETECTOR_SNAPSHOT_PERIOD: isize = 256;
|
|
|
|
|
2018-01-16 09:31:48 +01:00
|
|
|
pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>(
|
|
|
|
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|
|
|
instance: Instance<'tcx>,
|
|
|
|
mir: &'mir mir::Mir<'tcx>,
|
2018-01-16 10:16:38 +01:00
|
|
|
span: Span,
|
2018-09-20 10:12:21 +02:00
|
|
|
) -> EvalResult<'tcx, CompileTimeEvalContext<'a, 'mir, 'tcx>> {
|
2018-01-16 09:31:48 +01:00
|
|
|
debug!("mk_borrowck_eval_cx: {:?}", instance);
|
|
|
|
let param_env = tcx.param_env(instance.def_id());
|
2018-10-16 09:15:13 +02:00
|
|
|
let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeInterpreter::new());
|
2018-01-16 09:31:48 +01:00
|
|
|
// insert a stack frame so any queries have the correct substs
|
2018-10-09 21:05:53 +02:00
|
|
|
// cannot use `push_stack_frame`; if we do `const_prop` explodes
|
2018-08-24 16:39:25 +02:00
|
|
|
ecx.stack.push(interpret::Frame {
|
2018-07-24 18:28:53 +02:00
|
|
|
block: mir::START_BLOCK,
|
|
|
|
locals: IndexVec::new(),
|
2018-01-16 09:31:48 +01:00
|
|
|
instance,
|
2018-01-16 10:16:38 +01:00
|
|
|
span,
|
2018-01-16 09:31:48 +01:00
|
|
|
mir,
|
2018-10-09 21:05:53 +02:00
|
|
|
return_place: None,
|
2018-08-23 19:04:33 +02:00
|
|
|
return_to_block: StackPopCleanup::Goto(None), // never pop
|
2018-07-24 18:28:53 +02:00
|
|
|
stmt: 0,
|
|
|
|
});
|
2018-01-16 09:31:48 +01:00
|
|
|
Ok(ecx)
|
|
|
|
}
|
|
|
|
|
2017-12-15 08:55:54 +01:00
|
|
|
pub fn mk_eval_cx<'a, 'tcx>(
|
|
|
|
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|
|
|
instance: Instance<'tcx>,
|
|
|
|
param_env: ty::ParamEnv<'tcx>,
|
2018-09-20 10:12:21 +02:00
|
|
|
) -> EvalResult<'tcx, CompileTimeEvalContext<'a, 'tcx, 'tcx>> {
|
2017-12-15 08:55:54 +01:00
|
|
|
debug!("mk_eval_cx: {:?}, {:?}", instance, param_env);
|
2018-02-06 15:35:43 +01:00
|
|
|
let span = tcx.def_span(instance.def_id());
|
2018-10-16 09:15:13 +02:00
|
|
|
let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeInterpreter::new());
|
2017-12-15 08:55:54 +01:00
|
|
|
let mir = ecx.load_mir(instance.def)?;
|
|
|
|
// insert a stack frame so any queries have the correct substs
|
|
|
|
ecx.push_stack_frame(
|
|
|
|
instance,
|
|
|
|
mir.span,
|
|
|
|
mir,
|
2018-10-09 21:05:53 +02:00
|
|
|
None,
|
2018-08-23 19:04:33 +02:00
|
|
|
StackPopCleanup::Goto(None), // never pop
|
2017-12-15 08:55:54 +01:00
|
|
|
)?;
|
|
|
|
Ok(ecx)
|
|
|
|
}
|
|
|
|
|
2018-09-20 10:22:11 +02:00
|
|
|
pub(crate) fn eval_promoted<'a, 'mir, 'tcx>(
|
2018-09-20 10:12:21 +02:00
|
|
|
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
2018-01-02 23:22:09 +00:00
|
|
|
cid: GlobalId<'tcx>,
|
2018-01-16 09:31:48 +01:00
|
|
|
mir: &'mir mir::Mir<'tcx>,
|
2017-12-12 17:14:49 +01:00
|
|
|
param_env: ty::ParamEnv<'tcx>,
|
2018-08-13 16:14:22 +02:00
|
|
|
) -> EvalResult<'tcx, OpTy<'tcx>> {
|
2018-09-20 10:12:21 +02:00
|
|
|
let mut ecx = mk_borrowck_eval_cx(tcx, cid.instance, mir, DUMMY_SP).unwrap();
|
|
|
|
eval_body_using_ecx(&mut ecx, cid, Some(mir), param_env)
|
2018-01-16 09:28:27 +01:00
|
|
|
}
|
|
|
|
|
2018-08-13 16:14:22 +02:00
|
|
|
pub fn op_to_const<'tcx>(
|
2018-09-20 10:12:21 +02:00
|
|
|
ecx: &CompileTimeEvalContext<'_, '_, 'tcx>,
|
2018-08-13 16:14:22 +02:00
|
|
|
op: OpTy<'tcx>,
|
2018-09-30 12:37:00 +02:00
|
|
|
may_normalize: bool,
|
2018-08-01 15:29:13 +02:00
|
|
|
) -> EvalResult<'tcx, &'tcx ty::Const<'tcx>> {
|
2018-09-30 12:37:00 +02:00
|
|
|
// We do not normalize just any data. Only scalar layout and fat pointers.
|
|
|
|
let normalize = may_normalize
|
|
|
|
&& match op.layout.abi {
|
|
|
|
layout::Abi::Scalar(..) => true,
|
|
|
|
layout::Abi::ScalarPair(..) => {
|
|
|
|
// Must be a fat pointer
|
|
|
|
op.layout.ty.builtin_deref(true).is_some()
|
|
|
|
},
|
|
|
|
_ => false,
|
|
|
|
};
|
2018-08-13 16:14:22 +02:00
|
|
|
let normalized_op = if normalize {
|
|
|
|
ecx.try_read_value(op)?
|
|
|
|
} else {
|
|
|
|
match op.op {
|
|
|
|
Operand::Indirect(mplace) => Err(mplace),
|
|
|
|
Operand::Immediate(val) => Ok(val)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let val = match normalized_op {
|
2018-09-29 21:35:20 +02:00
|
|
|
Err(MemPlace { ptr, align, meta }) => {
|
2018-08-13 16:14:22 +02:00
|
|
|
// extract alloc-offset pair
|
2018-09-29 21:35:20 +02:00
|
|
|
assert!(meta.is_none());
|
2018-08-13 16:14:22 +02:00
|
|
|
let ptr = ptr.to_ptr()?;
|
2018-08-01 15:29:13 +02:00
|
|
|
let alloc = ecx.memory.get(ptr.alloc_id)?;
|
|
|
|
assert!(alloc.align.abi() >= align.abi());
|
2018-08-13 16:14:22 +02:00
|
|
|
assert!(alloc.bytes.len() as u64 - ptr.offset.bytes() >= op.layout.size.bytes());
|
2018-08-01 15:29:13 +02:00
|
|
|
let mut alloc = alloc.clone();
|
|
|
|
alloc.align = align;
|
2018-08-23 19:04:33 +02:00
|
|
|
// FIXME shouldnt it be the case that `mark_static_initialized` has already
|
|
|
|
// interned this? I thought that is the entire point of that `FinishStatic` stuff?
|
2018-08-01 12:55:05 +02:00
|
|
|
let alloc = ecx.tcx.intern_const_alloc(alloc);
|
2018-08-23 19:04:33 +02:00
|
|
|
ConstValue::ByRef(ptr.alloc_id, alloc, ptr.offset)
|
2018-08-13 16:14:22 +02:00
|
|
|
},
|
|
|
|
Ok(Value::Scalar(x)) =>
|
|
|
|
ConstValue::Scalar(x.not_undef()?),
|
|
|
|
Ok(Value::ScalarPair(a, b)) =>
|
2018-09-30 12:37:00 +02:00
|
|
|
ConstValue::ScalarPair(a.not_undef()?, b.not_undef()?),
|
2018-08-01 15:29:13 +02:00
|
|
|
};
|
2018-08-13 16:14:22 +02:00
|
|
|
Ok(ty::Const::from_const_value(ecx.tcx.tcx, val, op.layout.ty))
|
|
|
|
}
|
2018-04-26 09:18:19 +02:00
|
|
|
|
2018-01-16 09:31:48 +01:00
|
|
|
fn eval_body_and_ecx<'a, 'mir, 'tcx>(
|
2018-01-16 09:28:27 +01:00
|
|
|
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
2018-01-02 23:22:09 +00:00
|
|
|
cid: GlobalId<'tcx>,
|
2018-01-16 09:31:48 +01:00
|
|
|
mir: Option<&'mir mir::Mir<'tcx>>,
|
2018-01-16 09:28:27 +01:00
|
|
|
param_env: ty::ParamEnv<'tcx>,
|
2018-09-20 10:12:21 +02:00
|
|
|
) -> (EvalResult<'tcx, OpTy<'tcx>>, CompileTimeEvalContext<'a, 'mir, 'tcx>) {
|
2018-01-31 09:31:24 +01:00
|
|
|
// we start out with the best span we have
|
|
|
|
// and try improving it down the road when more information is available
|
2018-02-06 18:33:59 +01:00
|
|
|
let span = tcx.def_span(cid.instance.def_id());
|
2018-04-26 09:18:19 +02:00
|
|
|
let span = mir.map(|mir| mir.span).unwrap_or(span);
|
2018-10-16 09:15:13 +02:00
|
|
|
let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeInterpreter::new());
|
2018-04-26 09:18:19 +02:00
|
|
|
let r = eval_body_using_ecx(&mut ecx, cid, mir, param_env);
|
|
|
|
(r, ecx)
|
|
|
|
}
|
|
|
|
|
2018-08-13 16:14:22 +02:00
|
|
|
// Returns a pointer to where the result lives
|
2018-09-20 10:12:21 +02:00
|
|
|
fn eval_body_using_ecx<'mir, 'tcx>(
|
|
|
|
ecx: &mut CompileTimeEvalContext<'_, 'mir, 'tcx>,
|
2018-04-26 09:18:19 +02:00
|
|
|
cid: GlobalId<'tcx>,
|
|
|
|
mir: Option<&'mir mir::Mir<'tcx>>,
|
|
|
|
param_env: ty::ParamEnv<'tcx>,
|
2018-08-13 16:14:22 +02:00
|
|
|
) -> EvalResult<'tcx, OpTy<'tcx>> {
|
2018-08-23 19:04:33 +02:00
|
|
|
debug!("eval_body_using_ecx: {:?}, {:?}", cid, param_env);
|
2018-04-26 09:18:19 +02:00
|
|
|
let tcx = ecx.tcx.tcx;
|
2018-05-12 18:47:20 +02:00
|
|
|
let mut mir = match mir {
|
|
|
|
Some(mir) => mir,
|
|
|
|
None => ecx.load_mir(cid.instance.def)?,
|
|
|
|
};
|
|
|
|
if let Some(index) = cid.promoted {
|
|
|
|
mir = &mir.promoted[index];
|
|
|
|
}
|
|
|
|
let layout = ecx.layout_of(mir.return_ty().subst(tcx, cid.instance.substs))?;
|
|
|
|
assert!(!layout.is_unsized());
|
2018-08-13 16:14:22 +02:00
|
|
|
let ret = ecx.allocate(layout, MemoryKind::Stack)?;
|
2018-08-23 19:04:33 +02:00
|
|
|
|
2018-05-12 18:47:20 +02:00
|
|
|
let name = ty::tls::with(|tcx| tcx.item_path_str(cid.instance.def_id()));
|
|
|
|
let prom = cid.promoted.map_or(String::new(), |p| format!("::promoted[{:?}]", p));
|
2018-08-23 19:04:33 +02:00
|
|
|
trace!("eval_body_using_ecx: pushing stack frame for global: {}{}", name, prom);
|
2018-05-12 18:47:20 +02:00
|
|
|
assert!(mir.arg_count == 0);
|
|
|
|
ecx.push_stack_frame(
|
|
|
|
cid.instance,
|
|
|
|
mir.span,
|
|
|
|
mir,
|
2018-10-09 21:05:53 +02:00
|
|
|
Some(ret.into()),
|
2018-08-24 17:36:18 +02:00
|
|
|
StackPopCleanup::None { cleanup: false },
|
2018-05-12 18:47:20 +02:00
|
|
|
)?;
|
2018-04-10 16:25:10 +02:00
|
|
|
|
2018-08-13 16:14:22 +02:00
|
|
|
// The main interpreter loop.
|
2018-08-23 19:04:33 +02:00
|
|
|
ecx.run()?;
|
2018-08-13 16:14:22 +02:00
|
|
|
|
2018-08-24 17:36:18 +02:00
|
|
|
// Intern the result
|
|
|
|
let internally_mutable = !layout.ty.is_freeze(tcx, param_env, mir.span);
|
|
|
|
let is_static = tcx.is_static(cid.instance.def_id());
|
|
|
|
let mutability = if is_static == Some(hir::Mutability::MutMutable) || internally_mutable {
|
|
|
|
Mutability::Mutable
|
|
|
|
} else {
|
|
|
|
Mutability::Immutable
|
|
|
|
};
|
|
|
|
ecx.memory.intern_static(ret.ptr.to_ptr()?.alloc_id, mutability)?;
|
|
|
|
|
2018-08-23 19:04:33 +02:00
|
|
|
debug!("eval_body_using_ecx done: {:?}", *ret);
|
2018-08-13 16:14:22 +02:00
|
|
|
Ok(ret.into())
|
2017-12-12 17:14:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'tcx> Into<EvalError<'tcx>> for ConstEvalError {
|
|
|
|
fn into(self) -> EvalError<'tcx> {
|
2018-01-16 09:24:38 +01:00
|
|
|
EvalErrorKind::MachineError(self.to_string()).into()
|
2017-12-12 17:14:49 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
enum ConstEvalError {
|
|
|
|
NeedsRfc(String),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for ConstEvalError {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
use self::ConstEvalError::*;
|
|
|
|
match *self {
|
|
|
|
NeedsRfc(ref msg) => {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"\"{}\" needs an rfc before being allowed inside constants",
|
|
|
|
msg
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Error for ConstEvalError {
|
|
|
|
fn description(&self) -> &str {
|
|
|
|
use self::ConstEvalError::*;
|
|
|
|
match *self {
|
|
|
|
NeedsRfc(_) => "this feature needs an rfc before being allowed inside constants",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-23 10:15:26 -08:00
|
|
|
fn cause(&self) -> Option<&dyn Error> {
|
2017-12-12 17:14:49 +01:00
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-20 10:12:21 +02:00
|
|
|
// Extra machine state for CTFE, and the Machine instance
|
2018-09-20 10:17:14 +02:00
|
|
|
pub struct CompileTimeInterpreter<'a, 'mir, 'tcx: 'a+'mir> {
|
2018-09-20 10:12:21 +02:00
|
|
|
/// When this value is negative, it indicates the number of interpreter
|
|
|
|
/// steps *until* the loop detector is enabled. When it is positive, it is
|
|
|
|
/// the number of steps after the detector has been enabled modulo the loop
|
|
|
|
/// detector period.
|
|
|
|
pub(super) steps_since_detector_enabled: isize,
|
|
|
|
|
|
|
|
/// Extra state to detect loops.
|
|
|
|
pub(super) loop_detector: snapshot::InfiniteLoopDetector<'a, 'mir, 'tcx>,
|
|
|
|
}
|
|
|
|
|
2018-09-20 10:17:14 +02:00
|
|
|
impl<'a, 'mir, 'tcx> CompileTimeInterpreter<'a, 'mir, 'tcx> {
|
2018-09-20 10:12:21 +02:00
|
|
|
fn new() -> Self {
|
2018-09-20 10:17:14 +02:00
|
|
|
CompileTimeInterpreter {
|
2018-09-20 10:12:21 +02:00
|
|
|
loop_detector: Default::default(),
|
2018-09-20 11:57:45 +02:00
|
|
|
steps_since_detector_enabled: -STEPS_UNTIL_DETECTOR_ENABLED,
|
2018-09-20 10:12:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-05 15:13:59 +02:00
|
|
|
impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxHashMap<K, V> {
|
|
|
|
#[inline(always)]
|
|
|
|
fn contains_key<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> bool
|
|
|
|
where K: Borrow<Q>
|
|
|
|
{
|
|
|
|
FxHashMap::contains_key(self, k)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
fn insert(&mut self, k: K, v: V) -> Option<V>
|
|
|
|
{
|
|
|
|
FxHashMap::insert(self, k, v)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
fn remove<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> Option<V>
|
|
|
|
where K: Borrow<Q>
|
|
|
|
{
|
|
|
|
FxHashMap::remove(self, k)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
fn filter_map_collect<T>(&self, mut f: impl FnMut(&K, &V) -> Option<T>) -> Vec<T> {
|
|
|
|
self.iter()
|
|
|
|
.filter_map(move |(k, v)| f(k, &*v))
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
fn get_or<E>(
|
|
|
|
&self,
|
|
|
|
k: K,
|
|
|
|
vacant: impl FnOnce() -> Result<V, E>
|
|
|
|
) -> Result<&V, E>
|
|
|
|
{
|
|
|
|
match self.get(&k) {
|
|
|
|
Some(v) => Ok(v),
|
|
|
|
None => {
|
|
|
|
vacant()?;
|
|
|
|
bug!("The CTFE machine shouldn't ever need to extend the alloc_map when reading")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
fn get_mut_or<E>(
|
|
|
|
&mut self,
|
|
|
|
k: K,
|
|
|
|
vacant: impl FnOnce() -> Result<V, E>
|
|
|
|
) -> Result<&mut V, E>
|
|
|
|
{
|
|
|
|
match self.entry(k) {
|
|
|
|
Entry::Occupied(e) => Ok(e.into_mut()),
|
|
|
|
Entry::Vacant(e) => {
|
|
|
|
let v = vacant()?;
|
|
|
|
Ok(e.insert(v))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-20 10:12:21 +02:00
|
|
|
type CompileTimeEvalContext<'a, 'mir, 'tcx> =
|
2018-09-20 10:17:14 +02:00
|
|
|
EvalContext<'a, 'mir, 'tcx, CompileTimeInterpreter<'a, 'mir, 'tcx>>;
|
2018-09-20 10:12:21 +02:00
|
|
|
|
2018-10-16 12:45:44 +02:00
|
|
|
impl interpret::MayLeak for ! {
|
|
|
|
#[inline(always)]
|
|
|
|
fn may_leak(self) -> bool {
|
|
|
|
// `self` is uninhabited
|
|
|
|
self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-20 10:12:21 +02:00
|
|
|
impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx>
|
2018-09-20 10:17:14 +02:00
|
|
|
for CompileTimeInterpreter<'a, 'mir, 'tcx>
|
2018-09-20 10:12:21 +02:00
|
|
|
{
|
2017-12-12 17:14:49 +01:00
|
|
|
type MemoryKinds = !;
|
2018-10-16 09:15:13 +02:00
|
|
|
type AllocExtra = ();
|
2018-09-21 23:32:59 +02:00
|
|
|
type PointerTag = ();
|
2018-08-23 19:04:33 +02:00
|
|
|
|
2018-10-16 09:15:13 +02:00
|
|
|
type MemoryMap = FxHashMap<AllocId, (MemoryKind<!>, Allocation)>;
|
2018-10-05 15:13:59 +02:00
|
|
|
|
2018-09-21 23:32:59 +02:00
|
|
|
const STATIC_KIND: Option<!> = None; // no copying of statics allowed
|
2018-10-17 17:36:26 +02:00
|
|
|
const ENABLE_PTR_TRACKING_HOOKS: bool = false; // we don't have no provenance
|
2018-10-11 08:48:15 +02:00
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
fn enforce_validity(_ecx: &EvalContext<'a, 'mir, 'tcx, Self>) -> bool {
|
|
|
|
false // for now, we don't enforce validity
|
|
|
|
}
|
2018-08-26 12:59:59 +02:00
|
|
|
|
2018-09-20 10:12:21 +02:00
|
|
|
fn find_fn(
|
2018-01-16 09:31:48 +01:00
|
|
|
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
2017-12-12 17:14:49 +01:00
|
|
|
instance: ty::Instance<'tcx>,
|
2018-08-13 16:14:22 +02:00
|
|
|
args: &[OpTy<'tcx>],
|
2018-08-23 19:04:33 +02:00
|
|
|
dest: Option<PlaceTy<'tcx>>,
|
|
|
|
ret: Option<mir::BasicBlock>,
|
|
|
|
) -> EvalResult<'tcx, Option<&'mir mir::Mir<'tcx>>> {
|
2017-12-12 17:14:49 +01:00
|
|
|
debug!("eval_fn_call: {:?}", instance);
|
|
|
|
if !ecx.tcx.is_const_fn(instance.def_id()) {
|
2018-08-27 17:57:30 +02:00
|
|
|
// Some functions we support even if they are non-const -- but avoid testing
|
|
|
|
// that for const fn!
|
|
|
|
if ecx.hook_fn(instance, args, dest)? {
|
|
|
|
ecx.goto_block(ret)?; // fully evaluated and done
|
|
|
|
return Ok(None);
|
|
|
|
}
|
2017-12-12 17:14:49 +01:00
|
|
|
}
|
2018-08-23 19:04:33 +02:00
|
|
|
// This is a const fn. Call it.
|
|
|
|
Ok(Some(match ecx.load_mir(instance.def) {
|
2017-12-12 17:14:49 +01:00
|
|
|
Ok(mir) => mir,
|
2018-01-25 14:15:56 +01:00
|
|
|
Err(err) => {
|
2018-01-31 15:06:45 +01:00
|
|
|
if let EvalErrorKind::NoMirFor(ref path) = err.kind {
|
2018-01-25 14:15:56 +01:00
|
|
|
return Err(
|
|
|
|
ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path))
|
|
|
|
.into(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return Err(err);
|
2017-12-12 17:14:49 +01:00
|
|
|
}
|
2018-08-23 19:04:33 +02:00
|
|
|
}))
|
2017-12-12 17:14:49 +01:00
|
|
|
}
|
|
|
|
|
2018-09-20 10:12:21 +02:00
|
|
|
fn call_intrinsic(
|
2018-01-16 09:31:48 +01:00
|
|
|
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
2017-12-12 17:14:49 +01:00
|
|
|
instance: ty::Instance<'tcx>,
|
2018-08-13 16:14:22 +02:00
|
|
|
args: &[OpTy<'tcx>],
|
|
|
|
dest: PlaceTy<'tcx>,
|
2017-12-12 17:14:49 +01:00
|
|
|
) -> EvalResult<'tcx> {
|
2018-08-23 19:04:33 +02:00
|
|
|
if ecx.emulate_intrinsic(instance, args, dest)? {
|
|
|
|
return Ok(());
|
2017-12-12 17:14:49 +01:00
|
|
|
}
|
2018-08-23 19:04:33 +02:00
|
|
|
// An intrinsic that we do not support
|
|
|
|
let intrinsic_name = &ecx.tcx.item_name(instance.def_id()).as_str()[..];
|
|
|
|
Err(
|
|
|
|
ConstEvalError::NeedsRfc(format!("calling intrinsic `{}`", intrinsic_name)).into()
|
|
|
|
)
|
2017-12-12 17:14:49 +01:00
|
|
|
}
|
|
|
|
|
2018-09-20 10:12:21 +02:00
|
|
|
fn ptr_op(
|
2018-01-16 09:31:48 +01:00
|
|
|
_ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
|
2017-12-12 17:14:49 +01:00
|
|
|
_bin_op: mir::BinOp,
|
2018-08-28 01:14:29 +02:00
|
|
|
_left: Scalar,
|
2018-08-13 16:14:22 +02:00
|
|
|
_left_layout: TyLayout<'tcx>,
|
2018-08-28 01:14:29 +02:00
|
|
|
_right: Scalar,
|
2018-08-13 16:14:22 +02:00
|
|
|
_right_layout: TyLayout<'tcx>,
|
2018-08-28 01:14:29 +02:00
|
|
|
) -> EvalResult<'tcx, (Scalar, bool)> {
|
|
|
|
Err(
|
|
|
|
ConstEvalError::NeedsRfc("pointer arithmetic or comparison".to_string()).into(),
|
|
|
|
)
|
2017-12-12 17:14:49 +01:00
|
|
|
}
|
|
|
|
|
2018-09-20 10:12:21 +02:00
|
|
|
fn find_foreign_static(
|
2018-08-23 21:22:27 +02:00
|
|
|
_tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
|
|
|
|
_def_id: DefId,
|
2018-09-21 23:32:59 +02:00
|
|
|
) -> EvalResult<'tcx, Cow<'tcx, Allocation<Self::PointerTag>>> {
|
2018-08-23 21:22:27 +02:00
|
|
|
err!(ReadForeignStatic)
|
|
|
|
}
|
|
|
|
|
2018-09-21 23:32:59 +02:00
|
|
|
#[inline(always)]
|
|
|
|
fn static_with_default_tag(
|
|
|
|
alloc: &'_ Allocation
|
|
|
|
) -> Cow<'_, Allocation<Self::PointerTag>> {
|
2018-10-04 18:53:16 +02:00
|
|
|
// We do not use a tag so we can just cheaply forward the reference
|
2018-09-21 23:32:59 +02:00
|
|
|
Cow::Borrowed(alloc)
|
|
|
|
}
|
|
|
|
|
2018-09-20 10:12:21 +02:00
|
|
|
fn box_alloc(
|
2018-01-16 09:31:48 +01:00
|
|
|
_ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
2018-08-13 16:14:22 +02:00
|
|
|
_dest: PlaceTy<'tcx>,
|
2017-12-12 17:14:49 +01:00
|
|
|
) -> EvalResult<'tcx> {
|
|
|
|
Err(
|
2018-05-29 01:38:18 +01:00
|
|
|
ConstEvalError::NeedsRfc("heap allocations via `box` keyword".to_string()).into(),
|
2017-12-12 17:14:49 +01:00
|
|
|
)
|
|
|
|
}
|
2018-09-20 10:12:21 +02:00
|
|
|
|
|
|
|
fn before_terminator(ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>) -> EvalResult<'tcx> {
|
|
|
|
{
|
|
|
|
let steps = &mut ecx.machine.steps_since_detector_enabled;
|
|
|
|
|
|
|
|
*steps += 1;
|
|
|
|
if *steps < 0 {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
2018-09-20 11:57:45 +02:00
|
|
|
*steps %= DETECTOR_SNAPSHOT_PERIOD;
|
2018-09-20 10:12:21 +02:00
|
|
|
if *steps != 0 {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-20 12:27:50 +02:00
|
|
|
let span = ecx.frame().span;
|
2018-09-20 10:12:21 +02:00
|
|
|
ecx.machine.loop_detector.observe_and_analyze(
|
|
|
|
&ecx.tcx,
|
2018-09-20 12:27:50 +02:00
|
|
|
span,
|
2018-09-20 10:12:21 +02:00
|
|
|
&ecx.memory,
|
|
|
|
&ecx.stack[..],
|
|
|
|
)
|
|
|
|
}
|
2018-10-16 14:50:07 +02:00
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
fn tag_reference(
|
|
|
|
_ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
2018-10-16 17:00:39 +02:00
|
|
|
_ptr: Pointer<Self::PointerTag>,
|
|
|
|
_pointee_ty: Ty<'tcx>,
|
|
|
|
_pointee_size: Size,
|
2018-10-17 14:50:36 +02:00
|
|
|
_borrow_kind: Option<mir::BorrowKind>,
|
2018-10-16 14:50:07 +02:00
|
|
|
) -> EvalResult<'tcx, Self::PointerTag> {
|
|
|
|
Ok(())
|
|
|
|
}
|
2018-10-16 17:00:39 +02:00
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
fn tag_dereference(
|
|
|
|
_ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
|
|
|
|
_ptr: Pointer<Self::PointerTag>,
|
|
|
|
_ptr_ty: Ty<'tcx>,
|
|
|
|
) -> EvalResult<'tcx, Self::PointerTag> {
|
|
|
|
Ok(())
|
|
|
|
}
|
2017-12-12 17:14:49 +01:00
|
|
|
}
|
|
|
|
|
2018-08-13 16:14:22 +02:00
|
|
|
/// Project to a field of a (variant of a) const
|
|
|
|
pub fn const_field<'a, 'tcx>(
|
2018-01-16 09:24:38 +01:00
|
|
|
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
2017-12-27 21:32:01 +01:00
|
|
|
param_env: ty::ParamEnv<'tcx>,
|
|
|
|
instance: ty::Instance<'tcx>,
|
2018-01-16 09:26:37 +01:00
|
|
|
variant: Option<usize>,
|
2017-12-27 21:32:01 +01:00
|
|
|
field: mir::Field,
|
2018-06-25 20:53:02 +02:00
|
|
|
value: &'tcx ty::Const<'tcx>,
|
2018-06-25 18:46:02 +02:00
|
|
|
) -> ::rustc::mir::interpret::ConstEvalResult<'tcx> {
|
2018-08-13 16:14:22 +02:00
|
|
|
trace!("const_field: {:?}, {:?}, {:?}", instance, field, value);
|
2018-08-24 15:27:05 +02:00
|
|
|
let ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
|
2018-01-31 15:06:45 +01:00
|
|
|
let result = (|| {
|
2018-08-13 16:14:22 +02:00
|
|
|
// get the operand again
|
2018-08-24 15:27:05 +02:00
|
|
|
let op = ecx.const_to_op(value)?;
|
2018-08-13 16:14:22 +02:00
|
|
|
// downcast
|
|
|
|
let down = match variant {
|
|
|
|
None => op,
|
|
|
|
Some(variant) => ecx.operand_downcast(op, variant)?
|
|
|
|
};
|
|
|
|
// then project
|
|
|
|
let field = ecx.operand_field(down, field.index() as u64)?;
|
|
|
|
// and finally move back to the const world, always normalizing because
|
|
|
|
// this is not called for statics.
|
|
|
|
op_to_const(&ecx, field, true)
|
2018-01-31 15:06:45 +01:00
|
|
|
})();
|
2018-05-13 15:58:16 +02:00
|
|
|
result.map_err(|err| {
|
|
|
|
let (trace, span) = ecx.generate_stacktrace(None);
|
|
|
|
ConstEvalErr {
|
2018-06-25 17:41:20 +02:00
|
|
|
error: err,
|
|
|
|
stacktrace: trace,
|
2018-05-13 15:58:16 +02:00
|
|
|
span,
|
2018-06-25 17:41:20 +02:00
|
|
|
}.into()
|
2018-05-13 15:58:16 +02:00
|
|
|
})
|
2018-01-16 09:24:38 +01:00
|
|
|
}
|
|
|
|
|
2018-04-13 16:05:54 +02:00
|
|
|
pub fn const_variant_index<'a, 'tcx>(
|
2018-01-16 09:24:38 +01:00
|
|
|
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
2017-12-27 21:32:01 +01:00
|
|
|
param_env: ty::ParamEnv<'tcx>,
|
|
|
|
instance: ty::Instance<'tcx>,
|
2018-06-25 20:53:02 +02:00
|
|
|
val: &'tcx ty::Const<'tcx>,
|
2018-04-13 16:05:54 +02:00
|
|
|
) -> EvalResult<'tcx, usize> {
|
2018-06-25 20:53:02 +02:00
|
|
|
trace!("const_variant_index: {:?}, {:?}", instance, val);
|
2018-08-24 15:27:05 +02:00
|
|
|
let ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
|
|
|
|
let op = ecx.const_to_op(val)?;
|
2018-08-25 11:07:03 +02:00
|
|
|
Ok(ecx.read_discriminant(op)?.1)
|
2018-01-16 09:24:38 +01:00
|
|
|
}
|
|
|
|
|
2018-08-13 16:14:22 +02:00
|
|
|
pub fn const_to_allocation_provider<'a, 'tcx>(
|
2018-08-23 23:38:47 +02:00
|
|
|
_tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
2018-06-25 20:53:02 +02:00
|
|
|
val: &'tcx ty::Const<'tcx>,
|
2018-05-03 18:29:14 +02:00
|
|
|
) -> &'tcx Allocation {
|
2018-08-23 23:38:47 +02:00
|
|
|
// FIXME: This really does not need to be a query. Instead, we should have a query for statics
|
|
|
|
// that returns an allocation directly (or an `AllocId`?), after doing a sanity check of the
|
|
|
|
// value and centralizing error reporting.
|
2018-06-25 20:53:02 +02:00
|
|
|
match val.val {
|
2018-08-23 19:04:33 +02:00
|
|
|
ConstValue::ByRef(_, alloc, offset) => {
|
2018-05-19 16:37:29 +02:00
|
|
|
assert_eq!(offset.bytes(), 0);
|
2018-05-14 18:54:24 +02:00
|
|
|
return alloc;
|
|
|
|
},
|
2018-08-23 23:38:47 +02:00
|
|
|
_ => bug!("const_to_allocation called on non-static"),
|
2018-05-03 18:29:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-12 17:14:49 +01:00
|
|
|
pub fn const_eval_provider<'a, 'tcx>(
|
|
|
|
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
2018-01-02 23:22:09 +00:00
|
|
|
key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
|
2018-06-25 18:46:02 +02:00
|
|
|
) -> ::rustc::mir::interpret::ConstEvalResult<'tcx> {
|
2017-12-12 17:14:49 +01:00
|
|
|
trace!("const eval: {:?}", key);
|
2018-01-02 23:22:09 +00:00
|
|
|
let cid = key.value;
|
|
|
|
let def_id = cid.instance.def.def_id();
|
2017-12-12 17:14:49 +01:00
|
|
|
|
2018-01-02 23:22:09 +00:00
|
|
|
if let Some(id) = tcx.hir.as_local_node_id(def_id) {
|
|
|
|
let tables = tcx.typeck_tables_of(def_id);
|
2018-01-29 19:53:46 +01:00
|
|
|
let span = tcx.def_span(def_id);
|
2017-12-12 17:14:49 +01:00
|
|
|
|
|
|
|
// Do match-check before building MIR
|
|
|
|
if tcx.check_match(def_id).is_err() {
|
|
|
|
return Err(ConstEvalErr {
|
2018-06-25 17:41:20 +02:00
|
|
|
error: EvalErrorKind::CheckMatchError.into(),
|
|
|
|
stacktrace: vec![],
|
2018-01-16 09:31:48 +01:00
|
|
|
span,
|
2018-06-25 17:41:20 +02:00
|
|
|
}.into());
|
2017-12-12 17:14:49 +01:00
|
|
|
}
|
|
|
|
|
2018-01-02 23:22:09 +00:00
|
|
|
if let hir::BodyOwnerKind::Const = tcx.hir.body_owner_kind(id) {
|
|
|
|
tcx.mir_const_qualif(def_id);
|
|
|
|
}
|
2017-12-12 17:14:49 +01:00
|
|
|
|
2018-01-02 23:22:09 +00:00
|
|
|
// Do not continue into miri if typeck errors occurred; it will fail horribly
|
|
|
|
if tables.tainted_by_errors {
|
|
|
|
return Err(ConstEvalErr {
|
2018-06-25 17:41:20 +02:00
|
|
|
error: EvalErrorKind::CheckMatchError.into(),
|
|
|
|
stacktrace: vec![],
|
2018-01-02 23:22:09 +00:00
|
|
|
span,
|
2018-06-25 17:41:20 +02:00
|
|
|
}.into());
|
2018-01-02 23:22:09 +00:00
|
|
|
}
|
|
|
|
};
|
2018-01-16 09:24:38 +01:00
|
|
|
|
2018-02-06 14:04:35 +01:00
|
|
|
let (res, ecx) = eval_body_and_ecx(tcx, cid, None, key.param_env);
|
2018-08-13 16:14:22 +02:00
|
|
|
res.and_then(|op| {
|
|
|
|
let normalize = tcx.is_static(def_id).is_none() && cid.promoted.is_none();
|
|
|
|
if !normalize {
|
|
|
|
// Sanity check: These must always be a MemPlace
|
|
|
|
match op.op {
|
|
|
|
Operand::Indirect(_) => { /* all is good */ },
|
|
|
|
Operand::Immediate(_) => bug!("const eval gave us an Immediate"),
|
|
|
|
}
|
2018-05-17 16:14:30 +02:00
|
|
|
}
|
2018-08-13 16:14:22 +02:00
|
|
|
op_to_const(&ecx, op, normalize)
|
2018-06-03 03:01:06 +02:00
|
|
|
}).map_err(|err| {
|
2018-02-06 14:04:35 +01:00
|
|
|
let (trace, span) = ecx.generate_stacktrace(None);
|
2018-06-02 23:38:57 +02:00
|
|
|
let err = ConstEvalErr {
|
2018-06-25 17:41:20 +02:00
|
|
|
error: err,
|
|
|
|
stacktrace: trace,
|
2018-01-14 13:04:02 +01:00
|
|
|
span,
|
2018-06-02 23:38:57 +02:00
|
|
|
};
|
|
|
|
if tcx.is_static(def_id).is_some() {
|
|
|
|
err.report_as_error(ecx.tcx, "could not evaluate static initializer");
|
2018-07-24 18:28:53 +02:00
|
|
|
if tcx.sess.err_count() == 0 {
|
|
|
|
span_bug!(span, "static eval failure didn't emit an error: {:#?}", err);
|
|
|
|
}
|
2018-01-16 09:31:48 +01:00
|
|
|
}
|
2018-06-25 17:41:20 +02:00
|
|
|
err.into()
|
2018-01-16 09:31:48 +01:00
|
|
|
})
|
2017-12-12 17:14:49 +01:00
|
|
|
}
|