2017-12-12 16:14:49 +00:00
|
|
|
use rustc::ty::{self, TyCtxt, Ty, Instance};
|
|
|
|
use rustc::ty::layout::{self, LayoutOf};
|
|
|
|
use rustc::ty::subst::Substs;
|
|
|
|
use rustc::hir::def_id::DefId;
|
|
|
|
use rustc::mir;
|
|
|
|
use rustc::middle::const_val::ErrKind::{CheckMatchError, TypeckError};
|
|
|
|
use rustc::middle::const_val::{ConstEvalErr, ConstVal};
|
|
|
|
use rustc_const_eval::{lookup_const_by_id, ConstContext};
|
|
|
|
use rustc::mir::Field;
|
|
|
|
use rustc_data_structures::indexed_vec::Idx;
|
|
|
|
|
|
|
|
use syntax::ast::Mutability;
|
|
|
|
use syntax::codemap::Span;
|
|
|
|
|
2018-01-05 03:12:38 +00:00
|
|
|
use rustc::mir::interpret::{EvalResult, EvalError, EvalErrorKind, GlobalId, Value, MemoryPointer, Pointer, PrimVal};
|
2017-12-16 21:34:43 +00:00
|
|
|
use super::{Place, EvalContext, StackPopCleanup, ValTy};
|
2017-12-12 16:14:49 +00:00
|
|
|
|
|
|
|
use rustc_const_math::ConstInt;
|
|
|
|
|
|
|
|
use std::fmt;
|
|
|
|
use std::error::Error;
|
|
|
|
|
2017-12-15 07:55:54 +00:00
|
|
|
|
|
|
|
pub fn mk_eval_cx<'a, 'tcx>(
|
|
|
|
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|
|
|
instance: Instance<'tcx>,
|
|
|
|
param_env: ty::ParamEnv<'tcx>,
|
|
|
|
) -> EvalResult<'tcx, EvalContext<'a, 'tcx, CompileTimeEvaluator>> {
|
|
|
|
debug!("mk_eval_cx: {:?}, {:?}", instance, param_env);
|
|
|
|
let limits = super::ResourceLimits::default();
|
|
|
|
let mut ecx = EvalContext::new(tcx, param_env, limits, CompileTimeEvaluator, ());
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2017-12-12 16:14:49 +00:00
|
|
|
pub fn eval_body<'a, 'tcx>(
|
|
|
|
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|
|
|
instance: Instance<'tcx>,
|
|
|
|
param_env: ty::ParamEnv<'tcx>,
|
2017-12-01 16:29:35 +00:00
|
|
|
) -> EvalResult<'tcx, (Pointer, Ty<'tcx>)> {
|
2017-12-12 16:14:49 +00:00
|
|
|
debug!("eval_body: {:?}, {:?}", instance, param_env);
|
|
|
|
let limits = super::ResourceLimits::default();
|
|
|
|
let mut ecx = EvalContext::new(tcx, param_env, limits, CompileTimeEvaluator, ());
|
|
|
|
let cid = GlobalId {
|
|
|
|
instance,
|
|
|
|
promoted: None,
|
|
|
|
};
|
|
|
|
|
2017-12-15 07:55:54 +00:00
|
|
|
if ecx.tcx.has_attr(instance.def_id(), "linkage") {
|
|
|
|
return Err(ConstEvalError::NotConst("extern global".to_string()).into());
|
|
|
|
}
|
2017-12-17 01:48:39 +00:00
|
|
|
let instance_ty = instance.ty(tcx);
|
2017-12-15 07:55:54 +00:00
|
|
|
if tcx.interpret_interner.borrow().get_cached(cid).is_none() {
|
|
|
|
let mir = ecx.load_mir(instance.def)?;
|
|
|
|
let layout = ecx.layout_of(instance_ty)?;
|
|
|
|
assert!(!layout.is_unsized());
|
|
|
|
let ptr = ecx.memory.allocate(
|
|
|
|
layout.size.bytes(),
|
2017-12-17 06:47:22 +00:00
|
|
|
layout.align,
|
2017-12-15 07:55:54 +00:00
|
|
|
None,
|
|
|
|
)?;
|
2018-01-05 03:12:38 +00:00
|
|
|
tcx.interpret_interner.borrow_mut().cache(cid, ptr.alloc_id);
|
2017-12-15 07:55:54 +00:00
|
|
|
let cleanup = StackPopCleanup::MarkStatic(Mutability::Immutable);
|
|
|
|
let name = ty::tls::with(|tcx| tcx.item_path_str(instance.def_id()));
|
|
|
|
trace!("const_eval: pushing stack frame for global: {}", name);
|
|
|
|
ecx.push_stack_frame(
|
|
|
|
instance,
|
|
|
|
mir.span,
|
|
|
|
mir,
|
2017-12-16 12:07:04 +00:00
|
|
|
Place::from_ptr(ptr, layout.align),
|
2017-12-15 07:55:54 +00:00
|
|
|
cleanup.clone(),
|
|
|
|
)?;
|
|
|
|
|
|
|
|
while ecx.step()? {}
|
|
|
|
}
|
2018-01-05 03:12:38 +00:00
|
|
|
let alloc = tcx.interpret_interner.borrow().get_cached(cid).expect("global not cached");
|
|
|
|
Ok((MemoryPointer::new(alloc, 0).into(), instance_ty))
|
2017-12-12 16:14:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn eval_body_as_integer<'a, 'tcx>(
|
|
|
|
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|
|
|
param_env: ty::ParamEnv<'tcx>,
|
|
|
|
instance: Instance<'tcx>,
|
|
|
|
) -> EvalResult<'tcx, ConstInt> {
|
2017-12-15 07:55:54 +00:00
|
|
|
let ptr_ty = eval_body(tcx, instance, param_env);
|
2017-12-12 16:14:49 +00:00
|
|
|
let (ptr, ty) = ptr_ty?;
|
2017-12-15 07:55:54 +00:00
|
|
|
let ecx = mk_eval_cx(tcx, instance, param_env)?;
|
2017-12-17 06:47:22 +00:00
|
|
|
let prim = match ecx.try_read_value(ptr, ecx.layout_of(ty)?.align, ty)? {
|
2017-12-12 16:14:49 +00:00
|
|
|
Some(Value::ByVal(prim)) => prim.to_bytes()?,
|
|
|
|
_ => return err!(TypeNotPrimitive(ty)),
|
|
|
|
};
|
|
|
|
use syntax::ast::{IntTy, UintTy};
|
|
|
|
use rustc::ty::TypeVariants::*;
|
|
|
|
use rustc_const_math::{ConstIsize, ConstUsize};
|
|
|
|
Ok(match ty.sty {
|
|
|
|
TyInt(IntTy::I8) => ConstInt::I8(prim as i128 as i8),
|
|
|
|
TyInt(IntTy::I16) => ConstInt::I16(prim as i128 as i16),
|
|
|
|
TyInt(IntTy::I32) => ConstInt::I32(prim as i128 as i32),
|
|
|
|
TyInt(IntTy::I64) => ConstInt::I64(prim as i128 as i64),
|
|
|
|
TyInt(IntTy::I128) => ConstInt::I128(prim as i128),
|
2018-01-04 01:12:04 +00:00
|
|
|
TyInt(IntTy::Isize) => ConstInt::Isize(
|
2017-12-12 16:14:49 +00:00
|
|
|
ConstIsize::new(prim as i128 as i64, tcx.sess.target.isize_ty)
|
|
|
|
.expect("miri should already have errored"),
|
|
|
|
),
|
|
|
|
TyUint(UintTy::U8) => ConstInt::U8(prim as u8),
|
|
|
|
TyUint(UintTy::U16) => ConstInt::U16(prim as u16),
|
|
|
|
TyUint(UintTy::U32) => ConstInt::U32(prim as u32),
|
|
|
|
TyUint(UintTy::U64) => ConstInt::U64(prim as u64),
|
|
|
|
TyUint(UintTy::U128) => ConstInt::U128(prim),
|
2018-01-04 01:12:04 +00:00
|
|
|
TyUint(UintTy::Usize) => ConstInt::Usize(
|
2017-12-12 16:14:49 +00:00
|
|
|
ConstUsize::new(prim as u64, tcx.sess.target.usize_ty)
|
|
|
|
.expect("miri should already have errored"),
|
|
|
|
),
|
|
|
|
_ => {
|
|
|
|
return Err(
|
|
|
|
ConstEvalError::NeedsRfc(
|
|
|
|
"evaluating anything other than isize/usize during typeck".to_string(),
|
|
|
|
).into(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct CompileTimeEvaluator;
|
|
|
|
|
|
|
|
impl<'tcx> Into<EvalError<'tcx>> for ConstEvalError {
|
|
|
|
fn into(self) -> EvalError<'tcx> {
|
|
|
|
EvalErrorKind::MachineError(Box::new(self)).into()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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
|
|
|
|
)
|
|
|
|
}
|
|
|
|
NotConst(ref msg) => write!(f, "Cannot evaluate within 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",
|
|
|
|
NotConst(_) => "this feature is not compatible with constant evaluation",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-23 18:15:26 +00:00
|
|
|
fn cause(&self) -> Option<&dyn Error> {
|
2017-12-12 16:14:49 +00:00
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'tcx> super::Machine<'tcx> for CompileTimeEvaluator {
|
|
|
|
type MemoryData = ();
|
|
|
|
type MemoryKinds = !;
|
|
|
|
fn eval_fn_call<'a>(
|
|
|
|
ecx: &mut EvalContext<'a, 'tcx, Self>,
|
|
|
|
instance: ty::Instance<'tcx>,
|
|
|
|
destination: Option<(Place, mir::BasicBlock)>,
|
|
|
|
_args: &[ValTy<'tcx>],
|
|
|
|
span: Span,
|
|
|
|
_sig: ty::FnSig<'tcx>,
|
|
|
|
) -> EvalResult<'tcx, bool> {
|
|
|
|
debug!("eval_fn_call: {:?}", instance);
|
|
|
|
if !ecx.tcx.is_const_fn(instance.def_id()) {
|
|
|
|
return Err(
|
|
|
|
ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
let mir = match ecx.load_mir(instance.def) {
|
|
|
|
Ok(mir) => mir,
|
|
|
|
Err(EvalError { kind: EvalErrorKind::NoMirFor(path), .. }) => {
|
|
|
|
// some simple things like `malloc` might get accepted in the future
|
|
|
|
return Err(
|
|
|
|
ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path))
|
|
|
|
.into(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
Err(other) => return Err(other),
|
|
|
|
};
|
|
|
|
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>(
|
|
|
|
ecx: &mut EvalContext<'a, 'tcx, Self>,
|
|
|
|
instance: ty::Instance<'tcx>,
|
|
|
|
_args: &[ValTy<'tcx>],
|
|
|
|
dest: Place,
|
|
|
|
dest_layout: layout::TyLayout<'tcx>,
|
|
|
|
target: mir::BasicBlock,
|
|
|
|
) -> EvalResult<'tcx> {
|
|
|
|
let substs = instance.substs;
|
|
|
|
|
|
|
|
let intrinsic_name = &ecx.tcx.item_name(instance.def_id())[..];
|
|
|
|
match intrinsic_name {
|
|
|
|
"min_align_of" => {
|
|
|
|
let elem_ty = substs.type_at(0);
|
|
|
|
let elem_align = ecx.layout_of(elem_ty)?.align.abi();
|
|
|
|
let align_val = PrimVal::from_u128(elem_align as u128);
|
|
|
|
ecx.write_primval(dest, align_val, dest_layout.ty)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
"size_of" => {
|
|
|
|
let ty = substs.type_at(0);
|
|
|
|
let size = ecx.layout_of(ty)?.size.bytes() as u128;
|
|
|
|
ecx.write_primval(dest, PrimVal::from_u128(size), dest_layout.ty)?;
|
|
|
|
}
|
|
|
|
|
2018-01-30 20:33:33 +00:00
|
|
|
"type_id" => {
|
|
|
|
let ty = substs.type_at(0);
|
|
|
|
let type_id = ecx.tcx.type_id_hash(ty) as u128;
|
|
|
|
ecx.write_primval(dest, PrimVal::from_u128(type_id), dest_layout.ty)?;
|
|
|
|
}
|
|
|
|
|
2017-12-12 16:14:49 +00:00
|
|
|
name => return Err(ConstEvalError::NeedsRfc(format!("calling intrinsic `{}`", name)).into()),
|
|
|
|
}
|
|
|
|
|
|
|
|
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>(
|
|
|
|
_ecx: &EvalContext<'a, 'tcx, Self>,
|
|
|
|
_bin_op: mir::BinOp,
|
|
|
|
left: PrimVal,
|
|
|
|
_left_ty: Ty<'tcx>,
|
|
|
|
right: PrimVal,
|
|
|
|
_right_ty: Ty<'tcx>,
|
|
|
|
) -> EvalResult<'tcx, Option<(PrimVal, bool)>> {
|
|
|
|
if left.is_bytes() && right.is_bytes() {
|
|
|
|
Ok(None)
|
|
|
|
} else {
|
|
|
|
Err(
|
|
|
|
ConstEvalError::NeedsRfc("Pointer arithmetic or comparison".to_string()).into(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn mark_static_initialized(m: !) -> EvalResult<'tcx> {
|
|
|
|
m
|
|
|
|
}
|
|
|
|
|
|
|
|
fn box_alloc<'a>(
|
|
|
|
_ecx: &mut EvalContext<'a, 'tcx, Self>,
|
|
|
|
_ty: Ty<'tcx>,
|
|
|
|
_dest: Place,
|
|
|
|
) -> EvalResult<'tcx> {
|
|
|
|
Err(
|
|
|
|
ConstEvalError::NeedsRfc("Heap allocations via `box` keyword".to_string()).into(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn global_item_with_linkage<'a>(
|
|
|
|
_ecx: &mut EvalContext<'a, 'tcx, Self>,
|
|
|
|
_instance: ty::Instance<'tcx>,
|
|
|
|
_mutability: Mutability,
|
|
|
|
) -> EvalResult<'tcx> {
|
|
|
|
Err(
|
|
|
|
ConstEvalError::NotConst("statics with `linkage` attribute".to_string()).into(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn const_eval_provider<'a, 'tcx>(
|
|
|
|
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|
|
|
key: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>,
|
|
|
|
) -> ::rustc::middle::const_val::EvalResult<'tcx> {
|
|
|
|
trace!("const eval: {:?}", key);
|
|
|
|
let (def_id, substs) = if let Some(resolved) = lookup_const_by_id(tcx, key) {
|
|
|
|
resolved
|
|
|
|
} else {
|
|
|
|
return Err(ConstEvalErr {
|
|
|
|
span: tcx.def_span(key.value.0),
|
|
|
|
kind: TypeckError
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
let tables = tcx.typeck_tables_of(def_id);
|
|
|
|
let body = if let Some(id) = tcx.hir.as_local_node_id(def_id) {
|
|
|
|
let body_id = tcx.hir.body_owned_by(id);
|
|
|
|
|
|
|
|
// Do match-check before building MIR
|
|
|
|
if tcx.check_match(def_id).is_err() {
|
|
|
|
return Err(ConstEvalErr {
|
|
|
|
span: tcx.def_span(key.value.0),
|
|
|
|
kind: CheckMatchError,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
tcx.mir_const_qualif(def_id);
|
|
|
|
tcx.hir.body(body_id)
|
|
|
|
} else {
|
|
|
|
tcx.extern_const_body(def_id).body
|
|
|
|
};
|
|
|
|
|
|
|
|
// do not continue into miri if typeck errors occurred
|
|
|
|
// it will fail horribly
|
|
|
|
if tables.tainted_by_errors {
|
|
|
|
return Err(ConstEvalErr { span: body.value.span, kind: TypeckError })
|
|
|
|
}
|
|
|
|
|
|
|
|
trace!("running old const eval");
|
|
|
|
let old_result = ConstContext::new(tcx, key.param_env.and(substs), tables).eval(&body.value);
|
|
|
|
trace!("old const eval produced {:?}", old_result);
|
|
|
|
if tcx.sess.opts.debugging_opts.miri {
|
|
|
|
let instance = ty::Instance::new(def_id, substs);
|
|
|
|
trace!("const eval instance: {:?}, {:?}", instance, key.param_env);
|
|
|
|
let miri_result = ::interpret::eval_body(tcx, instance, key.param_env);
|
|
|
|
match (miri_result, old_result) {
|
2017-12-15 07:55:54 +00:00
|
|
|
(Err(err), Ok(ok)) => {
|
2017-12-12 16:14:49 +00:00
|
|
|
trace!("miri failed, ctfe returned {:?}", ok);
|
|
|
|
tcx.sess.span_warn(
|
|
|
|
tcx.def_span(key.value.0),
|
|
|
|
"miri failed to eval, while ctfe succeeded",
|
|
|
|
);
|
2017-12-15 07:55:54 +00:00
|
|
|
let ecx = mk_eval_cx(tcx, instance, key.param_env).unwrap();
|
2017-12-12 16:14:49 +00:00
|
|
|
let () = unwrap_miri(&ecx, Err(err));
|
|
|
|
Ok(ok)
|
|
|
|
},
|
2017-12-15 07:55:54 +00:00
|
|
|
(_, Err(err)) => Err(err),
|
|
|
|
(Ok((miri_val, miri_ty)), Ok(ctfe)) => {
|
|
|
|
let mut ecx = mk_eval_cx(tcx, instance, key.param_env).unwrap();
|
2017-12-16 21:34:43 +00:00
|
|
|
let layout = ecx.layout_of(miri_ty).unwrap();
|
|
|
|
let miri_place = Place::from_primval_ptr(miri_val, layout.align);
|
|
|
|
check_ctfe_against_miri(&mut ecx, miri_place, miri_ty, ctfe.val);
|
2017-12-12 16:14:49 +00:00
|
|
|
Ok(ctfe)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
old_result
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check_ctfe_against_miri<'a, 'tcx>(
|
|
|
|
ecx: &mut EvalContext<'a, 'tcx, CompileTimeEvaluator>,
|
2017-12-16 21:34:43 +00:00
|
|
|
miri_place: Place,
|
2017-12-12 16:14:49 +00:00
|
|
|
miri_ty: Ty<'tcx>,
|
|
|
|
ctfe: ConstVal<'tcx>,
|
|
|
|
) {
|
|
|
|
use rustc::middle::const_val::ConstAggregate::*;
|
|
|
|
use rustc_const_math::ConstFloat;
|
|
|
|
use rustc::ty::TypeVariants::*;
|
2017-12-16 21:34:43 +00:00
|
|
|
let miri_val = ValTy {
|
|
|
|
value: ecx.read_place(miri_place).unwrap(),
|
|
|
|
ty: miri_ty
|
|
|
|
};
|
2017-12-12 16:14:49 +00:00
|
|
|
match miri_ty.sty {
|
|
|
|
TyInt(int_ty) => {
|
2017-12-16 21:34:43 +00:00
|
|
|
let prim = get_prim(ecx, miri_val);
|
2017-12-12 16:14:49 +00:00
|
|
|
let c = ConstInt::new_signed_truncating(prim as i128,
|
|
|
|
int_ty,
|
|
|
|
ecx.tcx.sess.target.isize_ty);
|
|
|
|
let c = ConstVal::Integral(c);
|
|
|
|
assert_eq!(c, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", c, ctfe);
|
|
|
|
},
|
|
|
|
TyUint(uint_ty) => {
|
2017-12-16 21:34:43 +00:00
|
|
|
let prim = get_prim(ecx, miri_val);
|
2017-12-12 16:14:49 +00:00
|
|
|
let c = ConstInt::new_unsigned_truncating(prim,
|
|
|
|
uint_ty,
|
|
|
|
ecx.tcx.sess.target.usize_ty);
|
|
|
|
let c = ConstVal::Integral(c);
|
|
|
|
assert_eq!(c, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", c, ctfe);
|
|
|
|
},
|
|
|
|
TyFloat(ty) => {
|
2017-12-16 21:34:43 +00:00
|
|
|
let prim = get_prim(ecx, miri_val);
|
2017-12-12 16:14:49 +00:00
|
|
|
let f = ConstVal::Float(ConstFloat { bits: prim, ty });
|
|
|
|
assert_eq!(f, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", f, ctfe);
|
|
|
|
},
|
|
|
|
TyBool => {
|
2017-12-16 21:34:43 +00:00
|
|
|
let bits = get_prim(ecx, miri_val);
|
2017-12-12 16:14:49 +00:00
|
|
|
if bits > 1 {
|
|
|
|
bug!("miri evaluated to {}, but expected a bool {:?}", bits, ctfe);
|
|
|
|
}
|
|
|
|
let b = ConstVal::Bool(bits == 1);
|
|
|
|
assert_eq!(b, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", b, ctfe);
|
|
|
|
},
|
|
|
|
TyChar => {
|
2017-12-16 21:34:43 +00:00
|
|
|
let bits = get_prim(ecx, miri_val);
|
2017-12-12 16:14:49 +00:00
|
|
|
if let Some(cm) = ::std::char::from_u32(bits as u32) {
|
|
|
|
assert_eq!(
|
|
|
|
ConstVal::Char(cm), ctfe,
|
|
|
|
"miri evaluated to {:?}, but expected {:?}", cm, ctfe,
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
bug!("miri evaluated to {}, but expected a char {:?}", bits, ctfe);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
TyStr => {
|
2017-12-16 21:34:43 +00:00
|
|
|
let value = ecx.follow_by_ref_value(miri_val.value, miri_val.ty);
|
|
|
|
if let Ok(Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(len))) = value {
|
2017-12-12 16:14:49 +00:00
|
|
|
let bytes = ecx
|
|
|
|
.memory
|
|
|
|
.read_bytes(ptr.into(), len as u64)
|
|
|
|
.expect("bad miri memory for str");
|
|
|
|
if let Ok(s) = ::std::str::from_utf8(bytes) {
|
|
|
|
if let ConstVal::Str(s2) = ctfe {
|
|
|
|
assert_eq!(s, s2, "miri produced {:?}, but expected {:?}", s, s2);
|
|
|
|
} else {
|
|
|
|
bug!("miri produced {:?}, but expected {:?}", s, ctfe);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
bug!(
|
|
|
|
"miri failed to produce valid utf8 {:?}, while ctfe produced {:?}",
|
|
|
|
bytes,
|
|
|
|
ctfe,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
bug!("miri evaluated to {:?}, but expected a str {:?}", value, ctfe);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
TyArray(elem_ty, n) => {
|
|
|
|
let n = n.val.to_const_int().unwrap().to_u64().unwrap();
|
|
|
|
let vec: Vec<(ConstVal, Ty<'tcx>)> = match ctfe {
|
|
|
|
ConstVal::ByteStr(arr) => arr.data.iter().map(|&b| {
|
|
|
|
(ConstVal::Integral(ConstInt::U8(b)), ecx.tcx.types.u8)
|
|
|
|
}).collect(),
|
|
|
|
ConstVal::Aggregate(Array(v)) => {
|
|
|
|
v.iter().map(|c| (c.val, c.ty)).collect()
|
|
|
|
},
|
|
|
|
ConstVal::Aggregate(Repeat(v, n)) => {
|
|
|
|
vec![(v.val, v.ty); n as usize]
|
|
|
|
},
|
|
|
|
_ => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe),
|
|
|
|
};
|
2017-12-16 21:34:43 +00:00
|
|
|
let layout = ecx.layout_of(miri_ty).unwrap();
|
2017-12-12 16:14:49 +00:00
|
|
|
for (i, elem) in vec.into_iter().enumerate() {
|
|
|
|
assert!((i as u64) < n);
|
2017-12-16 21:34:43 +00:00
|
|
|
let (field_place, _) =
|
|
|
|
ecx.place_field(miri_place, Field::new(i), layout).unwrap();
|
|
|
|
check_ctfe_against_miri(ecx, field_place, elem_ty, elem.0);
|
2017-12-12 16:14:49 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
TyTuple(..) => {
|
|
|
|
let vec = match ctfe {
|
|
|
|
ConstVal::Aggregate(Tuple(v)) => v,
|
|
|
|
_ => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe),
|
|
|
|
};
|
|
|
|
let layout = ecx.layout_of(miri_ty).unwrap();
|
|
|
|
for (i, elem) in vec.into_iter().enumerate() {
|
2017-12-16 21:34:43 +00:00
|
|
|
let (field_place, _) =
|
|
|
|
ecx.place_field(miri_place, Field::new(i), layout).unwrap();
|
|
|
|
check_ctfe_against_miri(ecx, field_place, elem.ty, elem.val);
|
2017-12-12 16:14:49 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
TyAdt(def, _) => {
|
2017-12-16 21:34:43 +00:00
|
|
|
let mut miri_place = miri_place;
|
|
|
|
let struct_variant = if def.is_enum() {
|
|
|
|
let discr = ecx.read_discriminant_value(miri_place, miri_ty).unwrap();
|
2017-12-12 16:14:49 +00:00
|
|
|
let variant = def.discriminants(ecx.tcx).position(|variant_discr| {
|
|
|
|
variant_discr.to_u128_unchecked() == discr
|
|
|
|
}).expect("miri produced invalid enum discriminant");
|
2017-12-16 21:34:43 +00:00
|
|
|
miri_place = ecx.place_downcast(miri_place, variant).unwrap();
|
|
|
|
&def.variants[variant]
|
2017-12-12 16:14:49 +00:00
|
|
|
} else {
|
2018-01-07 21:41:41 +00:00
|
|
|
def.non_enum_variant()
|
2017-12-12 16:14:49 +00:00
|
|
|
};
|
|
|
|
let vec = match ctfe {
|
|
|
|
ConstVal::Aggregate(Struct(v)) => v,
|
|
|
|
ConstVal::Variant(did) => {
|
|
|
|
assert_eq!(struct_variant.fields.len(), 0);
|
|
|
|
assert_eq!(did, struct_variant.did);
|
|
|
|
return;
|
|
|
|
},
|
|
|
|
ctfe => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe),
|
|
|
|
};
|
|
|
|
let layout = ecx.layout_of(miri_ty).unwrap();
|
|
|
|
for &(name, elem) in vec.into_iter() {
|
|
|
|
let field = struct_variant.fields.iter().position(|f| f.name == name).unwrap();
|
2017-12-16 21:34:43 +00:00
|
|
|
let (field_place, _) =
|
|
|
|
ecx.place_field(miri_place, Field::new(field), layout).unwrap();
|
|
|
|
check_ctfe_against_miri(ecx, field_place, elem.ty, elem.val);
|
2017-12-12 16:14:49 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
TySlice(_) => bug!("miri produced a slice?"),
|
|
|
|
// not supported by ctfe
|
|
|
|
TyRawPtr(_) |
|
|
|
|
TyRef(..) => {}
|
|
|
|
TyDynamic(..) => bug!("miri produced a trait object"),
|
|
|
|
TyClosure(..) => bug!("miri produced a closure"),
|
|
|
|
TyGenerator(..) => bug!("miri produced a generator"),
|
2017-10-07 14:36:28 +00:00
|
|
|
TyGeneratorWitness(..) => bug!("miri produced a generator witness"),
|
2017-12-12 16:14:49 +00:00
|
|
|
TyNever => bug!("miri produced a value of the never type"),
|
|
|
|
TyProjection(_) => bug!("miri produced a projection"),
|
|
|
|
TyAnon(..) => bug!("miri produced an impl Trait type"),
|
|
|
|
TyParam(_) => bug!("miri produced an unmonomorphized type"),
|
|
|
|
TyInfer(_) => bug!("miri produced an uninferred type"),
|
|
|
|
TyError => bug!("miri produced a type error"),
|
|
|
|
TyForeign(_) => bug!("miri produced an extern type"),
|
|
|
|
// should be fine
|
|
|
|
TyFnDef(..) => {}
|
|
|
|
TyFnPtr(_) => {
|
2017-12-16 21:34:43 +00:00
|
|
|
let value = ecx.value_to_primval(miri_val);
|
2017-12-12 16:14:49 +00:00
|
|
|
let ptr = match value {
|
2017-12-16 21:34:43 +00:00
|
|
|
Ok(PrimVal::Ptr(ptr)) => ptr,
|
2017-12-12 16:14:49 +00:00
|
|
|
value => bug!("expected fn ptr, got {:?}", value),
|
|
|
|
};
|
|
|
|
let inst = ecx.memory.get_fn(ptr).unwrap();
|
|
|
|
match ctfe {
|
|
|
|
ConstVal::Function(did, substs) => {
|
|
|
|
let ctfe = ty::Instance::resolve(
|
|
|
|
ecx.tcx,
|
|
|
|
ecx.param_env,
|
|
|
|
did,
|
|
|
|
substs,
|
|
|
|
).unwrap();
|
|
|
|
assert_eq!(inst, ctfe, "expected fn ptr {:?}, but got {:?}", ctfe, inst);
|
|
|
|
},
|
|
|
|
_ => bug!("ctfe produced {:?}, but miri produced function {:?}", ctfe, inst),
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_prim<'a, 'tcx>(
|
|
|
|
ecx: &mut EvalContext<'a, 'tcx, CompileTimeEvaluator>,
|
2017-12-16 21:34:43 +00:00
|
|
|
val: ValTy<'tcx>,
|
2017-12-12 16:14:49 +00:00
|
|
|
) -> u128 {
|
2017-12-16 21:34:43 +00:00
|
|
|
let res = ecx.value_to_primval(val).and_then(|prim| prim.to_bytes());
|
|
|
|
unwrap_miri(ecx, res)
|
2017-12-12 16:14:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn unwrap_miri<'a, 'tcx, T>(
|
|
|
|
ecx: &EvalContext<'a, 'tcx, CompileTimeEvaluator>,
|
|
|
|
res: Result<T, EvalError<'tcx>>,
|
|
|
|
) -> T {
|
|
|
|
match res {
|
|
|
|
Ok(val) => val,
|
|
|
|
Err(mut err) => {
|
|
|
|
ecx.report(&mut err);
|
|
|
|
ecx.tcx.sess.abort_if_errors();
|
|
|
|
bug!("{:#?}", err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|