2018-01-02 23:22:09 +00:00
|
|
|
use rustc::hir;
|
2018-06-25 18:46:02 +02:00
|
|
|
use rustc::mir::interpret::{ConstEvalErr};
|
2018-01-02 23:22:09 +00:00
|
|
|
use rustc::mir;
|
|
|
|
use rustc::ty::{self, TyCtxt, Ty, Instance};
|
2018-05-29 11:41:10 +02:00
|
|
|
use rustc::ty::layout::{self, LayoutOf, Primitive};
|
2018-01-02 23:22:09 +00:00
|
|
|
use rustc::ty::subst::Subst;
|
2017-12-12 17:14:49 +01:00
|
|
|
|
|
|
|
use syntax::ast::Mutability;
|
|
|
|
use syntax::codemap::Span;
|
2018-05-03 18:29:14 +02:00
|
|
|
use syntax::codemap::DUMMY_SP;
|
2017-12-12 17:14:49 +01:00
|
|
|
|
2018-04-26 09:18:19 +02:00
|
|
|
use rustc::mir::interpret::{
|
|
|
|
EvalResult, EvalError, EvalErrorKind, GlobalId,
|
2018-05-21 00:30:00 +02:00
|
|
|
Value, Scalar, AllocId, Allocation, ConstValue,
|
2018-04-26 09:18:19 +02:00
|
|
|
};
|
2018-05-03 18:29:14 +02:00
|
|
|
use super::{Place, EvalContext, StackPopCleanup, ValTy, PlaceExtra, Memory, MemoryKind};
|
2017-12-12 17:14:49 +01:00
|
|
|
|
|
|
|
use std::fmt;
|
|
|
|
use std::error::Error;
|
|
|
|
|
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-01-16 09:31:48 +01:00
|
|
|
) -> EvalResult<'tcx, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>> {
|
|
|
|
debug!("mk_borrowck_eval_cx: {:?}", instance);
|
|
|
|
let param_env = tcx.param_env(instance.def_id());
|
2018-02-06 18:33:59 +01:00
|
|
|
let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ());
|
2018-01-16 09:31:48 +01:00
|
|
|
// insert a stack frame so any queries have the correct substs
|
|
|
|
ecx.push_stack_frame(
|
|
|
|
instance,
|
2018-01-16 10:16:38 +01:00
|
|
|
span,
|
2018-01-16 09:31:48 +01:00
|
|
|
mir,
|
|
|
|
Place::undef(),
|
|
|
|
StackPopCleanup::None,
|
|
|
|
)?;
|
|
|
|
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-01-16 09:31:48 +01:00
|
|
|
) -> EvalResult<'tcx, EvalContext<'a, 'tcx, 'tcx, CompileTimeEvaluator>> {
|
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-02-06 18:33:59 +01:00
|
|
|
let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ());
|
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,
|
|
|
|
Place::undef(),
|
|
|
|
StackPopCleanup::None,
|
|
|
|
)?;
|
|
|
|
Ok(ecx)
|
|
|
|
}
|
|
|
|
|
2018-04-08 22:26:28 +02:00
|
|
|
pub fn eval_promoted<'a, 'mir, 'tcx>(
|
2018-04-26 09:18:19 +02:00
|
|
|
ecx: &mut EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>,
|
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-06-02 23:38:57 +02:00
|
|
|
) -> EvalResult<'tcx, (Value, Scalar, Ty<'tcx>)> {
|
2018-04-26 09:18:19 +02:00
|
|
|
ecx.with_fresh_body(|ecx| {
|
2018-06-02 23:38:57 +02:00
|
|
|
eval_body_using_ecx(ecx, cid, Some(mir), param_env)
|
2018-04-26 09:18:19 +02:00
|
|
|
})
|
2018-01-16 09:28:27 +01:00
|
|
|
}
|
|
|
|
|
2018-05-13 15:58:16 +02:00
|
|
|
pub fn value_to_const_value<'tcx>(
|
|
|
|
ecx: &EvalContext<'_, '_, 'tcx, CompileTimeEvaluator>,
|
2018-05-17 16:14:30 +02:00
|
|
|
val: Value,
|
2018-04-26 09:18:19 +02:00
|
|
|
ty: Ty<'tcx>,
|
|
|
|
) -> &'tcx ty::Const<'tcx> {
|
2018-06-27 18:24:24 +02:00
|
|
|
let layout = ecx.layout_of(ty).unwrap();
|
2018-05-13 19:52:53 +02:00
|
|
|
match (val, &layout.abi) {
|
2018-05-22 10:28:46 +02:00
|
|
|
(Value::Scalar(Scalar::Bits { defined: 0, ..}), _) if layout.is_zst() => {},
|
2018-05-13 19:52:53 +02:00
|
|
|
(Value::ByRef(..), _) |
|
2018-05-20 23:46:30 +02:00
|
|
|
(Value::Scalar(_), &layout::Abi::Scalar(_)) |
|
|
|
|
(Value::ScalarPair(..), &layout::Abi::ScalarPair(..)) => {},
|
2018-05-13 19:52:53 +02:00
|
|
|
_ => bug!("bad value/layout combo: {:#?}, {:#?}", val, layout),
|
|
|
|
}
|
2018-05-12 18:47:20 +02:00
|
|
|
let val = (|| {
|
|
|
|
match val {
|
2018-05-20 23:46:30 +02:00
|
|
|
Value::Scalar(val) => Ok(ConstValue::Scalar(val)),
|
|
|
|
Value::ScalarPair(a, b) => Ok(ConstValue::ScalarPair(a, b)),
|
2018-05-12 18:47:20 +02:00
|
|
|
Value::ByRef(ptr, align) => {
|
2018-05-21 00:30:00 +02:00
|
|
|
let ptr = ptr.to_ptr().unwrap();
|
2018-05-12 18:47:20 +02:00
|
|
|
let alloc = ecx.memory.get(ptr.alloc_id)?;
|
2018-05-17 16:14:30 +02:00
|
|
|
assert!(alloc.align.abi() >= align.abi());
|
2018-05-19 16:37:29 +02:00
|
|
|
assert!(alloc.bytes.len() as u64 - ptr.offset.bytes() >= layout.size.bytes());
|
2018-05-12 18:47:20 +02:00
|
|
|
let mut alloc = alloc.clone();
|
2018-05-17 16:14:30 +02:00
|
|
|
alloc.align = align;
|
2018-05-12 18:47:20 +02:00
|
|
|
let alloc = ecx.tcx.intern_const_alloc(alloc);
|
2018-05-17 16:14:30 +02:00
|
|
|
Ok(ConstValue::ByRef(alloc, ptr.offset))
|
2018-04-26 09:18:19 +02:00
|
|
|
}
|
2018-05-12 18:47:20 +02:00
|
|
|
}
|
2018-05-13 15:58:16 +02:00
|
|
|
})();
|
2018-05-17 16:14:30 +02:00
|
|
|
match val {
|
|
|
|
Ok(val) => ty::Const::from_const_value(ecx.tcx.tcx, val, ty),
|
2018-06-02 23:38:57 +02:00
|
|
|
Err(err) => {
|
|
|
|
let (frames, span) = ecx.generate_stacktrace(None);
|
|
|
|
let err = ConstEvalErr {
|
|
|
|
span,
|
2018-06-25 17:41:20 +02:00
|
|
|
error: err,
|
|
|
|
stacktrace: frames,
|
2018-06-02 23:38:57 +02:00
|
|
|
};
|
|
|
|
err.report_as_error(
|
|
|
|
ecx.tcx,
|
|
|
|
"failed to convert Value to ConstValue, this is a bug",
|
|
|
|
);
|
|
|
|
span_bug!(span, "miri error occured when converting Value to ConstValue")
|
2018-05-13 15:58:16 +02:00
|
|
|
}
|
|
|
|
}
|
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-05-21 00:30:00 +02:00
|
|
|
) -> (EvalResult<'tcx, (Value, Scalar, Ty<'tcx>)>, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>) {
|
2018-04-26 09:18:19 +02:00
|
|
|
debug!("eval_body_and_ecx: {:?}, {:?}", cid, param_env);
|
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-02-06 18:33:59 +01:00
|
|
|
let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ());
|
2018-04-26 09:18:19 +02:00
|
|
|
let r = eval_body_using_ecx(&mut ecx, cid, mir, param_env);
|
|
|
|
(r, ecx)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn eval_body_using_ecx<'a, 'mir, 'tcx>(
|
|
|
|
ecx: &mut EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>,
|
|
|
|
cid: GlobalId<'tcx>,
|
|
|
|
mir: Option<&'mir mir::Mir<'tcx>>,
|
|
|
|
param_env: ty::ParamEnv<'tcx>,
|
2018-05-21 00:30:00 +02:00
|
|
|
) -> EvalResult<'tcx, (Value, Scalar, Ty<'tcx>)> {
|
2018-04-26 09:18:19 +02:00
|
|
|
debug!("eval_body: {:?}, {:?}", cid, param_env);
|
|
|
|
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());
|
|
|
|
let ptr = ecx.memory.allocate(
|
2018-05-19 16:37:29 +02:00
|
|
|
layout.size,
|
2018-05-12 18:47:20 +02:00
|
|
|
layout.align,
|
|
|
|
None,
|
|
|
|
)?;
|
|
|
|
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
|
|
|
|
};
|
|
|
|
let cleanup = StackPopCleanup::MarkStatic(mutability);
|
|
|
|
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));
|
|
|
|
trace!("const_eval: pushing stack frame for global: {}{}", name, prom);
|
|
|
|
assert!(mir.arg_count == 0);
|
|
|
|
ecx.push_stack_frame(
|
|
|
|
cid.instance,
|
|
|
|
mir.span,
|
|
|
|
mir,
|
|
|
|
Place::from_ptr(ptr, layout.align),
|
|
|
|
cleanup,
|
|
|
|
)?;
|
2018-04-10 16:25:10 +02:00
|
|
|
|
2018-05-12 18:47:20 +02:00
|
|
|
while ecx.step()? {}
|
|
|
|
let ptr = ptr.into();
|
|
|
|
// always try to read the value and report errors
|
|
|
|
let value = match ecx.try_read_value(ptr, layout.align, layout.ty)? {
|
|
|
|
Some(val) if is_static.is_none() => val,
|
|
|
|
// point at the allocation
|
|
|
|
_ => Value::ByRef(ptr, layout.align),
|
|
|
|
};
|
|
|
|
Ok((value, ptr, layout.ty))
|
2017-12-12 17:14:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct CompileTimeEvaluator;
|
|
|
|
|
|
|
|
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),
|
|
|
|
NotConst(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
|
|
|
|
)
|
|
|
|
}
|
2018-01-26 15:19:01 +01:00
|
|
|
NotConst(ref msg) => write!(f, "{}", msg),
|
2017-12-12 17:14:49 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Error for ConstEvalError {
|
|
|
|
fn description(&self) -> &str {
|
|
|
|
use self::ConstEvalError::*;
|
|
|
|
match *self {
|
|
|
|
NeedsRfc(_) => "this feature needs an rfc before being allowed inside constants",
|
|
|
|
NotConst(_) => "this feature is not compatible with constant evaluation",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-23 10:15:26 -08:00
|
|
|
fn cause(&self) -> Option<&dyn Error> {
|
2017-12-12 17:14:49 +01:00
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-16 09:31:48 +01:00
|
|
|
impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
|
2017-12-12 17:14:49 +01:00
|
|
|
type MemoryData = ();
|
|
|
|
type MemoryKinds = !;
|
|
|
|
fn eval_fn_call<'a>(
|
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>,
|
|
|
|
destination: Option<(Place, mir::BasicBlock)>,
|
2018-01-14 13:04:02 +01:00
|
|
|
args: &[ValTy<'tcx>],
|
2017-12-12 17:14:49 +01:00
|
|
|
span: Span,
|
2018-01-14 13:04:02 +01:00
|
|
|
sig: ty::FnSig<'tcx>,
|
2017-12-12 17:14:49 +01:00
|
|
|
) -> EvalResult<'tcx, bool> {
|
|
|
|
debug!("eval_fn_call: {:?}", instance);
|
|
|
|
if !ecx.tcx.is_const_fn(instance.def_id()) {
|
2018-01-14 13:04:02 +01:00
|
|
|
let def_id = instance.def_id();
|
|
|
|
let (op, oflo) = if let Some(op) = ecx.tcx.is_binop_lang_item(def_id) {
|
|
|
|
op
|
|
|
|
} else {
|
|
|
|
return Err(
|
|
|
|
ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into(),
|
|
|
|
);
|
|
|
|
};
|
|
|
|
let (dest, bb) = destination.expect("128 lowerings can't diverge");
|
|
|
|
let dest_ty = sig.output();
|
|
|
|
if oflo {
|
|
|
|
ecx.intrinsic_with_overflow(op, args[0], args[1], dest, dest_ty)?;
|
|
|
|
} else {
|
|
|
|
ecx.intrinsic_overflowing(op, args[0], args[1], dest, dest_ty)?;
|
|
|
|
}
|
|
|
|
ecx.goto_block(bb);
|
|
|
|
return Ok(true);
|
2017-12-12 17:14:49 +01:00
|
|
|
}
|
|
|
|
let mir = match ecx.load_mir(instance.def) {
|
|
|
|
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
|
|
|
}
|
|
|
|
};
|
|
|
|
let (return_place, return_to_block) = match destination {
|
|
|
|
Some((place, block)) => (place, StackPopCleanup::Goto(block)),
|
|
|
|
None => (Place::undef(), StackPopCleanup::None),
|
|
|
|
};
|
|
|
|
|
|
|
|
ecx.push_stack_frame(
|
|
|
|
instance,
|
|
|
|
span,
|
|
|
|
mir,
|
|
|
|
return_place,
|
|
|
|
return_to_block,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
Ok(false)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn call_intrinsic<'a>(
|
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-05-29 11:41:10 +02:00
|
|
|
args: &[ValTy<'tcx>],
|
2017-12-12 17:14:49 +01:00
|
|
|
dest: Place,
|
|
|
|
dest_layout: layout::TyLayout<'tcx>,
|
|
|
|
target: mir::BasicBlock,
|
|
|
|
) -> EvalResult<'tcx> {
|
|
|
|
let substs = instance.substs;
|
|
|
|
|
2018-04-11 23:02:41 +02:00
|
|
|
let intrinsic_name = &ecx.tcx.item_name(instance.def_id()).as_str()[..];
|
2017-12-12 17:14:49 +01:00
|
|
|
match intrinsic_name {
|
|
|
|
"min_align_of" => {
|
|
|
|
let elem_ty = substs.type_at(0);
|
|
|
|
let elem_align = ecx.layout_of(elem_ty)?.align.abi();
|
2018-05-22 10:28:46 +02:00
|
|
|
let align_val = Scalar::Bits {
|
|
|
|
bits: elem_align as u128,
|
|
|
|
defined: dest_layout.size.bits() as u8,
|
|
|
|
};
|
2018-05-22 19:30:16 +02:00
|
|
|
ecx.write_scalar(dest, align_val, dest_layout.ty)?;
|
2017-12-12 17:14:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
"size_of" => {
|
|
|
|
let ty = substs.type_at(0);
|
|
|
|
let size = ecx.layout_of(ty)?.size.bytes() as u128;
|
2018-05-22 10:28:46 +02:00
|
|
|
let size_val = Scalar::Bits {
|
|
|
|
bits: size,
|
|
|
|
defined: dest_layout.size.bits() as u8,
|
|
|
|
};
|
2018-05-22 19:30:16 +02:00
|
|
|
ecx.write_scalar(dest, size_val, dest_layout.ty)?;
|
2017-12-12 17:14:49 +01:00
|
|
|
}
|
|
|
|
|
2018-01-30 21:33:33 +01:00
|
|
|
"type_id" => {
|
|
|
|
let ty = substs.type_at(0);
|
|
|
|
let type_id = ecx.tcx.type_id_hash(ty) as u128;
|
2018-05-22 10:28:46 +02:00
|
|
|
let id_val = Scalar::Bits {
|
|
|
|
bits: type_id,
|
|
|
|
defined: dest_layout.size.bits() as u8,
|
|
|
|
};
|
2018-05-22 19:30:16 +02:00
|
|
|
ecx.write_scalar(dest, id_val, dest_layout.ty)?;
|
2018-01-30 21:33:33 +01:00
|
|
|
}
|
2018-05-29 11:41:10 +02:00
|
|
|
"ctpop" | "cttz" | "cttz_nonzero" | "ctlz" | "ctlz_nonzero" | "bswap" => {
|
|
|
|
let ty = substs.type_at(0);
|
|
|
|
let layout_of = ecx.layout_of(ty)?;
|
2018-05-31 19:43:56 +02:00
|
|
|
let bits = ecx.value_to_scalar(args[0])?.to_bits(layout_of.size)?;
|
2018-05-29 11:41:10 +02:00
|
|
|
let kind = match layout_of.abi {
|
|
|
|
ty::layout::Abi::Scalar(ref scalar) => scalar.value,
|
|
|
|
_ => Err(::rustc::mir::interpret::EvalErrorKind::TypeNotPrimitive(ty))?,
|
|
|
|
};
|
2018-05-31 19:43:56 +02:00
|
|
|
let out_val = if intrinsic_name.ends_with("_nonzero") {
|
|
|
|
if bits == 0 {
|
2018-05-29 11:41:10 +02:00
|
|
|
return err!(Intrinsic(format!("{} called on 0", intrinsic_name)));
|
|
|
|
}
|
2018-05-31 19:43:56 +02:00
|
|
|
numeric_intrinsic(intrinsic_name.trim_right_matches("_nonzero"), bits, kind)?
|
2018-05-29 11:41:10 +02:00
|
|
|
} else {
|
2018-05-31 19:43:56 +02:00
|
|
|
numeric_intrinsic(intrinsic_name, bits, kind)?
|
2018-05-29 11:41:10 +02:00
|
|
|
};
|
2018-05-31 19:43:56 +02:00
|
|
|
ecx.write_scalar(dest, out_val, ty)?;
|
2018-05-29 11:41:10 +02:00
|
|
|
}
|
2018-01-30 21:33:33 +01:00
|
|
|
|
2018-05-29 11:41:10 +02:00
|
|
|
name => return Err(
|
|
|
|
ConstEvalError::NeedsRfc(format!("calling intrinsic `{}`", name)).into()
|
|
|
|
),
|
2017-12-12 17:14:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ecx.goto_block(target);
|
|
|
|
|
|
|
|
// Since we pushed no stack frame, the main loop will act
|
|
|
|
// as if the call just completed and it's returning to the
|
|
|
|
// current frame.
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn try_ptr_op<'a>(
|
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-05-20 23:43:16 +02:00
|
|
|
left: Scalar,
|
2017-12-12 17:14:49 +01:00
|
|
|
_left_ty: Ty<'tcx>,
|
2018-05-20 23:43:16 +02:00
|
|
|
right: Scalar,
|
2017-12-12 17:14:49 +01:00
|
|
|
_right_ty: Ty<'tcx>,
|
2018-05-20 23:43:16 +02:00
|
|
|
) -> EvalResult<'tcx, Option<(Scalar, bool)>> {
|
2018-05-22 10:28:46 +02:00
|
|
|
if left.is_bits() && right.is_bits() {
|
2017-12-12 17:14:49 +01:00
|
|
|
Ok(None)
|
|
|
|
} else {
|
|
|
|
Err(
|
2018-05-29 01:38:18 +01:00
|
|
|
ConstEvalError::NeedsRfc("pointer arithmetic or comparison".to_string()).into(),
|
2017-12-12 17:14:49 +01:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-16 09:31:48 +01:00
|
|
|
fn mark_static_initialized<'a>(
|
|
|
|
_mem: &mut Memory<'a, 'mir, 'tcx, Self>,
|
|
|
|
_id: AllocId,
|
|
|
|
_mutability: Mutability,
|
|
|
|
) -> EvalResult<'tcx, bool> {
|
|
|
|
Ok(false)
|
2017-12-12 17:14:49 +01:00
|
|
|
}
|
|
|
|
|
2018-01-25 08:06:53 +01:00
|
|
|
fn init_static<'a>(
|
|
|
|
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
|
|
|
cid: GlobalId<'tcx>,
|
|
|
|
) -> EvalResult<'tcx, AllocId> {
|
|
|
|
Ok(ecx
|
|
|
|
.tcx
|
2018-05-02 06:03:06 +02:00
|
|
|
.alloc_map
|
|
|
|
.lock()
|
|
|
|
.intern_static(cid.instance.def_id()))
|
2018-01-25 08:06:53 +01:00
|
|
|
}
|
|
|
|
|
2017-12-12 17:14:49 +01:00
|
|
|
fn box_alloc<'a>(
|
2018-01-16 09:31:48 +01:00
|
|
|
_ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
2017-12-12 17:14:49 +01:00
|
|
|
_ty: Ty<'tcx>,
|
|
|
|
_dest: Place,
|
|
|
|
) -> 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
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn global_item_with_linkage<'a>(
|
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>,
|
|
|
|
_mutability: Mutability,
|
|
|
|
) -> EvalResult<'tcx> {
|
|
|
|
Err(
|
|
|
|
ConstEvalError::NotConst("statics with `linkage` attribute".to_string()).into(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-16 09:24:38 +01:00
|
|
|
pub fn const_val_field<'a, 'tcx>(
|
|
|
|
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-06-25 20:53:02 +02:00
|
|
|
trace!("const_val_field: {:?}, {:?}, {:?}", instance, field, value);
|
2018-01-31 15:06:45 +01:00
|
|
|
let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
|
|
|
|
let result = (|| {
|
2018-06-25 20:53:02 +02:00
|
|
|
let ty = value.ty;
|
|
|
|
let value = ecx.const_to_value(value.val)?;
|
2018-05-14 18:54:24 +02:00
|
|
|
let layout = ecx.layout_of(ty)?;
|
|
|
|
let (ptr, align) = match value {
|
|
|
|
Value::ByRef(ptr, align) => (ptr, align),
|
2018-05-20 23:46:30 +02:00
|
|
|
Value::ScalarPair(..) | Value::Scalar(_) => {
|
2018-05-14 18:54:24 +02:00
|
|
|
let ptr = ecx.alloc_ptr(ty)?.into();
|
2018-05-17 16:14:30 +02:00
|
|
|
ecx.write_value_to_ptr(value, ptr, layout.align, ty)?;
|
2018-05-14 18:54:24 +02:00
|
|
|
(ptr, layout.align)
|
|
|
|
},
|
2018-01-31 15:06:45 +01:00
|
|
|
};
|
2018-05-14 18:54:24 +02:00
|
|
|
let place = Place::Ptr {
|
|
|
|
ptr,
|
|
|
|
align,
|
|
|
|
extra: variant.map_or(PlaceExtra::None, PlaceExtra::DowncastVariant),
|
|
|
|
};
|
|
|
|
let (place, layout) = ecx.place_field(place, field, layout)?;
|
|
|
|
let (ptr, align) = place.to_ptr_align();
|
2018-05-17 16:14:30 +02:00
|
|
|
let mut new_value = Value::ByRef(ptr, align);
|
|
|
|
new_value = ecx.try_read_by_ref(new_value, layout.ty)?;
|
|
|
|
use rustc_data_structures::indexed_vec::Idx;
|
|
|
|
match (value, new_value) {
|
2018-05-20 23:46:30 +02:00
|
|
|
(Value::Scalar(_), Value::ByRef(..)) |
|
|
|
|
(Value::ScalarPair(..), Value::ByRef(..)) |
|
|
|
|
(Value::Scalar(_), Value::ScalarPair(..)) => bug!(
|
2018-05-17 16:14:30 +02:00
|
|
|
"field {} of {:?} yielded {:?}",
|
|
|
|
field.index(),
|
|
|
|
value,
|
|
|
|
new_value,
|
|
|
|
),
|
|
|
|
_ => {},
|
|
|
|
}
|
|
|
|
Ok(value_to_const_value(&ecx, new_value, layout.ty))
|
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);
|
2017-12-27 21:32:01 +01:00
|
|
|
let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
|
2018-06-25 20:53:02 +02:00
|
|
|
let value = ecx.const_to_value(val.val)?;
|
2018-01-16 09:24:38 +01:00
|
|
|
let (ptr, align) = match value {
|
2018-05-20 23:46:30 +02:00
|
|
|
Value::ScalarPair(..) | Value::Scalar(_) => {
|
2018-06-25 20:53:02 +02:00
|
|
|
let layout = ecx.layout_of(val.ty)?;
|
2018-05-21 00:30:00 +02:00
|
|
|
let ptr = ecx.memory.allocate(layout.size, layout.align, Some(MemoryKind::Stack))?.into();
|
2018-06-25 20:53:02 +02:00
|
|
|
ecx.write_value_to_ptr(value, ptr, layout.align, val.ty)?;
|
2018-01-16 09:24:38 +01:00
|
|
|
(ptr, layout.align)
|
|
|
|
},
|
|
|
|
Value::ByRef(ptr, align) => (ptr, align),
|
|
|
|
};
|
2018-05-22 19:30:16 +02:00
|
|
|
let place = Place::from_scalar_ptr(ptr, align);
|
2018-06-25 20:53:02 +02:00
|
|
|
ecx.read_discriminant_as_variant_index(place, val.ty)
|
2018-01-16 09:24:38 +01:00
|
|
|
}
|
|
|
|
|
2018-05-03 18:29:14 +02:00
|
|
|
pub fn const_value_to_allocation_provider<'a, 'tcx>(
|
|
|
|
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-06-25 20:53:02 +02:00
|
|
|
match val.val {
|
2018-05-14 18:54:24 +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-05-03 18:29:14 +02:00
|
|
|
_ => ()
|
|
|
|
}
|
|
|
|
let result = || -> EvalResult<'tcx, &'tcx Allocation> {
|
|
|
|
let mut ecx = EvalContext::new(
|
|
|
|
tcx.at(DUMMY_SP),
|
|
|
|
ty::ParamEnv::reveal_all(),
|
|
|
|
CompileTimeEvaluator,
|
|
|
|
());
|
2018-06-25 20:53:02 +02:00
|
|
|
let value = ecx.const_to_value(val.val)?;
|
|
|
|
let layout = ecx.layout_of(val.ty)?;
|
2018-05-19 16:37:29 +02:00
|
|
|
let ptr = ecx.memory.allocate(layout.size, layout.align, Some(MemoryKind::Stack))?;
|
2018-06-25 20:53:02 +02:00
|
|
|
ecx.write_value_to_ptr(value, ptr.into(), layout.align, val.ty)?;
|
2018-05-03 18:29:14 +02:00
|
|
|
let alloc = ecx.memory.get(ptr.alloc_id)?;
|
|
|
|
Ok(tcx.intern_const_alloc(alloc.clone()))
|
|
|
|
};
|
2018-06-25 20:53:02 +02:00
|
|
|
result().expect("unable to convert ConstValue to Allocation")
|
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-05-17 16:14:30 +02:00
|
|
|
res.and_then(|(mut val, _, miri_ty)| {
|
|
|
|
if tcx.is_static(def_id).is_none() {
|
|
|
|
val = ecx.try_read_by_ref(val, miri_ty)?;
|
|
|
|
}
|
|
|
|
Ok(value_to_const_value(&ecx, val, miri_ty))
|
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-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
|
|
|
}
|
2018-05-29 11:41:10 +02:00
|
|
|
|
|
|
|
fn numeric_intrinsic<'tcx>(
|
|
|
|
name: &str,
|
2018-05-31 19:21:34 +02:00
|
|
|
bits: u128,
|
2018-05-29 11:41:10 +02:00
|
|
|
kind: Primitive,
|
|
|
|
) -> EvalResult<'tcx, Scalar> {
|
2018-05-30 23:27:11 +02:00
|
|
|
let defined = match kind {
|
|
|
|
Primitive::Int(integer, _) => integer.size().bits() as u8,
|
2018-05-31 19:21:34 +02:00
|
|
|
_ => bug!("invalid `{}` argument: {:?}", name, bits),
|
2018-05-30 23:27:11 +02:00
|
|
|
};
|
|
|
|
let extra = 128 - defined as u128;
|
2018-05-31 19:21:34 +02:00
|
|
|
let bits_out = match name {
|
|
|
|
"ctpop" => bits.count_ones() as u128,
|
|
|
|
"ctlz" => bits.leading_zeros() as u128 - extra,
|
|
|
|
"cttz" => (bits << extra).trailing_zeros() as u128 - extra,
|
|
|
|
"bswap" => (bits << extra).swap_bytes(),
|
2018-05-29 11:41:10 +02:00
|
|
|
_ => bug!("not a numeric intrinsic: {}", name),
|
|
|
|
};
|
2018-05-31 19:21:34 +02:00
|
|
|
Ok(Scalar::Bits { bits: bits_out, defined })
|
2018-05-29 11:41:10 +02:00
|
|
|
}
|