use rustc::hir::def::Def;
use rustc::mir::interpret::{ConstEvalErr, ErrorHandled};
use rustc::mir;
-use rustc::ty::{self, TyCtxt, Instance, query::TyCtxtAt};
-use rustc::ty::layout::{self, LayoutOf, TyLayout, VariantIdx};
+use rustc::ty::{self, TyCtxt, query::TyCtxtAt};
+use rustc::ty::layout::{self, LayoutOf, VariantIdx};
use rustc::ty::subst::Subst;
use rustc::traits::Reveal;
-use rustc_data_structures::indexed_vec::IndexVec;
use rustc_data_structures::fx::FxHashMap;
use rustc::util::common::ErrorReported;
use syntax::source_map::{Span, DUMMY_SP};
use crate::interpret::{self,
- PlaceTy, MPlaceTy, MemPlace, OpTy, Operand, Immediate, Scalar, RawConst, ConstValue, Pointer,
+ PlaceTy, MPlaceTy, MemPlace, OpTy, ImmTy, Immediate, Scalar, Pointer,
+ RawConst, ConstValue,
EvalResult, EvalError, EvalErrorKind, GlobalId, EvalContext, StackPopCleanup,
Allocation, AllocId, MemoryKind,
snapshot, RefTracking,
/// Should be a power of two for performance reasons.
const DETECTOR_SNAPSHOT_PERIOD: isize = 256;
-/// Warning: do not use this function if you expect to start interpreting the given `Mir`.
-/// The `EvalContext` is only meant to be used to query values from constants and statics.
-///
-/// This function is used during const propagation. We cannot use `mk_eval_cx`, because copy
-/// propagation happens *during* the computation of the MIR of the current function. So if we
-/// tried to call the `optimized_mir` query, we'd get a cycle error because we are (transitively)
-/// inside the `optimized_mir` query of the `Instance` given.
-///
-/// Since we are looking at the MIR of the function in an abstract manner, we don't have a
-/// `ParamEnv` available to us. This function creates a `ParamEnv` for the given instance.
-pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>(
- tcx: TyCtxt<'a, 'tcx, 'tcx>,
- instance: Instance<'tcx>,
- mir: &'mir mir::Mir<'tcx>,
- span: Span,
-) -> EvalResult<'tcx, CompileTimeEvalContext<'a, 'mir, 'tcx>> {
- debug!("mk_borrowck_eval_cx: {:?}", instance);
- let param_env = tcx.param_env(instance.def_id());
- mk_eval_cx_inner(tcx, instance, mir, span, param_env)
-}
-
-/// This is just a helper function to reduce code duplication between `mk_borrowck_eval_cx` and
-/// `mk_eval_cx`. Do not call this function directly.
-fn mk_eval_cx_inner<'a, 'mir, 'tcx>(
- tcx: TyCtxt<'a, 'tcx, 'tcx>,
- instance: Instance<'tcx>,
- mir: &'mir mir::Mir<'tcx>,
- span: Span,
- param_env: ty::ParamEnv<'tcx>,
-) -> EvalResult<'tcx, CompileTimeEvalContext<'a, 'mir, 'tcx>> {
- let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeInterpreter::new());
- // Insert a stack frame so any queries have the correct substs.
- // We also avoid all the extra work performed by push_stack_frame,
- // like initializing local variables
- ecx.stack.push(interpret::Frame {
- block: mir::START_BLOCK,
- locals: IndexVec::new(),
- instance,
- span,
- mir,
- return_place: None,
- return_to_block: StackPopCleanup::Goto(None), // never pop
- stmt: 0,
- extra: (),
- });
- Ok(ecx)
-}
-
-/// Warning: do not use this function if you expect to start interpreting the given `Mir`.
/// The `EvalContext` is only meant to be used to do field and index projections into constants for
/// `simd_shuffle` and const patterns in match arms.
///
/// The function containing the `match` that is currently being analyzed may have generic bounds
-/// that inform us about the generic bounds of the constant. E.g. using an associated constant
+/// that inform us about the generic bounds of the constant. E.g., using an associated constant
/// of a function's generic parameter will require knowledge about the bounds on the generic
/// parameter. These bounds are passed to `mk_eval_cx` via the `ParamEnv` argument.
-fn mk_eval_cx<'a, 'tcx>(
+pub(crate) fn mk_eval_cx<'a, 'mir, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
- instance: Instance<'tcx>,
+ span: Span,
param_env: ty::ParamEnv<'tcx>,
-) -> EvalResult<'tcx, CompileTimeEvalContext<'a, 'tcx, 'tcx>> {
- debug!("mk_eval_cx: {:?}, {:?}", instance, param_env);
- let span = tcx.def_span(instance.def_id());
- let mir = tcx.optimized_mir(instance.def.def_id());
- mk_eval_cx_inner(tcx, instance, mir, span, param_env)
+) -> CompileTimeEvalContext<'a, 'mir, 'tcx> {
+ debug!("mk_eval_cx: {:?}", param_env);
+ EvalContext::new(tcx.at(span), param_env, CompileTimeInterpreter::new())
}
pub(crate) fn eval_promoted<'a, 'mir, 'tcx>(
mir: &'mir mir::Mir<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> EvalResult<'tcx, MPlaceTy<'tcx>> {
- let mut ecx = mk_borrowck_eval_cx(tcx, cid.instance, mir, DUMMY_SP).unwrap();
+ let span = tcx.def_span(cid.instance.def_id());
+ let mut ecx = mk_eval_cx(tcx, span, param_env);
eval_body_using_ecx(&mut ecx, cid, Some(mir), param_env)
}
-// FIXME: These two conversion functions are bad hacks. We should just always use allocations.
-pub fn op_to_const<'tcx>(
+fn mplace_to_const<'tcx>(
+ ecx: &CompileTimeEvalContext<'_, '_, 'tcx>,
+ mplace: MPlaceTy<'tcx>,
+) -> EvalResult<'tcx, ty::Const<'tcx>> {
+ let MemPlace { ptr, align, meta } = *mplace;
+ // extract alloc-offset pair
+ assert!(meta.is_none());
+ let ptr = ptr.to_ptr()?;
+ let alloc = ecx.memory.get(ptr.alloc_id)?;
+ assert!(alloc.align >= align);
+ assert!(alloc.bytes.len() as u64 - ptr.offset.bytes() >= mplace.layout.size.bytes());
+ let mut alloc = alloc.clone();
+ alloc.align = align;
+ // FIXME shouldn't it be the case that `mark_static_initialized` has already
+ // interned this? I thought that is the entire point of that `FinishStatic` stuff?
+ let alloc = ecx.tcx.intern_const_alloc(alloc);
+ let val = ConstValue::ByRef(ptr, alloc);
+ Ok(ty::Const { val, ty: mplace.layout.ty })
+}
+
+fn op_to_const<'tcx>(
ecx: &CompileTimeEvalContext<'_, '_, 'tcx>,
op: OpTy<'tcx>,
- may_normalize: bool,
) -> EvalResult<'tcx, ty::Const<'tcx>> {
- // 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,
- };
+ // We do not normalize just any data. Only scalar layout and slices.
+ let normalize = match op.layout.abi {
+ layout::Abi::Scalar(..) => true,
+ layout::Abi::ScalarPair(..) => op.layout.ty.is_slice(),
+ _ => false,
+ };
let normalized_op = if normalize {
- ecx.try_read_immediate(op)?
+ Err(*ecx.read_immediate(op).expect("normalization works on validated constants"))
} else {
- match op.op {
- Operand::Indirect(mplace) => Err(mplace),
- Operand::Immediate(val) => Ok(val)
- }
+ op.try_as_mplace()
};
let val = match normalized_op {
- Err(MemPlace { ptr, align, meta }) => {
- // extract alloc-offset pair
- assert!(meta.is_none());
- let ptr = ptr.to_ptr()?;
- let alloc = ecx.memory.get(ptr.alloc_id)?;
- assert!(alloc.align >= align);
- assert!(alloc.bytes.len() as u64 - ptr.offset.bytes() >= op.layout.size.bytes());
- let mut alloc = alloc.clone();
- alloc.align = align;
- // FIXME shouldn't it be the case that `mark_static_initialized` has already
- // interned this? I thought that is the entire point of that `FinishStatic` stuff?
- let alloc = ecx.tcx.intern_const_alloc(alloc);
- ConstValue::ByRef(ptr.alloc_id, alloc, ptr.offset)
- },
- Ok(Immediate::Scalar(x)) =>
+ Ok(mplace) => return mplace_to_const(ecx, mplace),
+ Err(Immediate::Scalar(x)) =>
ConstValue::Scalar(x.not_undef()?),
- Ok(Immediate::ScalarPair(a, b)) =>
- ConstValue::ScalarPair(a.not_undef()?, b.not_undef()?),
+ Err(Immediate::ScalarPair(a, b)) =>
+ ConstValue::Slice(a.not_undef()?, b.to_usize(ecx)?),
};
Ok(ty::Const { val, ty: op.layout.ty })
}
-pub fn lazy_const_to_op<'tcx>(
- ecx: &CompileTimeEvalContext<'_, '_, 'tcx>,
- cnst: ty::LazyConst<'tcx>,
- ty: ty::Ty<'tcx>,
-) -> EvalResult<'tcx, OpTy<'tcx>> {
- let op = ecx.const_value_to_op(cnst)?;
- Ok(OpTy { op, layout: ecx.layout_of(ty)? })
-}
-
fn eval_body_and_ecx<'a, 'mir, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
cid: GlobalId<'tcx>,
}
impl fmt::Display for ConstEvalError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use self::ConstEvalError::*;
match *self {
NeedsRfc(ref msg) => {
ret: Option<mir::BasicBlock>,
) -> EvalResult<'tcx, Option<&'mir mir::Mir<'tcx>>> {
debug!("eval_fn_call: {:?}", instance);
- // Execution might have wandered off into other crates, so we cannot to a stability-
- // sensitive check here. But we can at least rule out functions that are not const
- // at all.
- if !ecx.tcx.is_const_fn_raw(instance.def_id()) {
- // Some functions we support even if they are non-const -- but avoid testing
- // that for const fn! We certainly do *not* want to actually call the fn
- // though, so be sure we return here.
- return if ecx.hook_fn(instance, args, dest)? {
- ecx.goto_block(ret)?; // fully evaluated and done
- Ok(None)
- } else {
- err!(MachineError(format!("calling non-const function `{}`", instance)))
- };
+ // Only check non-glue functions
+ if let ty::InstanceDef::Item(def_id) = instance.def {
+ // Execution might have wandered off into other crates, so we cannot to a stability-
+ // sensitive check here. But we can at least rule out functions that are not const
+ // at all.
+ if !ecx.tcx.is_const_fn_raw(def_id) {
+ // Some functions we support even if they are non-const -- but avoid testing
+ // that for const fn! We certainly do *not* want to actually call the fn
+ // though, so be sure we return here.
+ return if ecx.hook_fn(instance, args, dest)? {
+ ecx.goto_block(ret)?; // fully evaluated and done
+ Ok(None)
+ } else {
+ err!(MachineError(format!("calling non-const function `{}`", instance)))
+ };
+ }
}
// This is a const fn. Call it.
Ok(Some(match ecx.load_mir(instance.def) {
fn ptr_op(
_ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
_bin_op: mir::BinOp,
- _left: Scalar,
- _left_layout: TyLayout<'tcx>,
- _right: Scalar,
- _right_layout: TyLayout<'tcx>,
+ _left: ImmTy<'tcx>,
+ _right: ImmTy<'tcx>,
) -> EvalResult<'tcx, (Scalar, bool)> {
Err(
ConstEvalError::NeedsRfc("pointer arithmetic or comparison".to_string()).into(),
Ok(())
}
- /// Called immediately before a stack frame gets popped
+ /// Called immediately before a stack frame gets popped.
#[inline(always)]
fn stack_pop(
_ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
}
}
-/// Project to a field of a (variant of a) const
+/// Projects to a field of a (variant of a) const.
pub fn const_field<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
- instance: ty::Instance<'tcx>,
variant: Option<VariantIdx>,
field: mir::Field,
value: ty::Const<'tcx>,
) -> ::rustc::mir::interpret::ConstEvalResult<'tcx> {
- trace!("const_field: {:?}, {:?}, {:?}", instance, field, value);
- let ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
+ trace!("const_field: {:?}, {:?}", field, value);
+ let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env);
let result = (|| {
// get the operand again
- let op = lazy_const_to_op(&ecx, ty::LazyConst::Evaluated(value), value.ty)?;
+ let op = ecx.const_to_op(value, None)?;
// downcast
let down = match variant {
None => op,
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)
+ op_to_const(&ecx, field)
})();
result.map_err(|error| {
let err = error_to_const_error(&ecx, error);
pub fn const_variant_index<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
- instance: ty::Instance<'tcx>,
val: ty::Const<'tcx>,
) -> EvalResult<'tcx, VariantIdx> {
- trace!("const_variant_index: {:?}, {:?}", instance, val);
- let ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
- let op = lazy_const_to_op(&ecx, ty::LazyConst::Evaluated(val), val.ty)?;
+ trace!("const_variant_index: {:?}", val);
+ let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env);
+ let op = ecx.const_to_op(val, None)?;
Ok(ecx.read_discriminant(op)?.1)
}
key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
) -> ::rustc::mir::interpret::ConstEvalResult<'tcx> {
let cid = key.value;
- let ecx = mk_eval_cx(tcx, cid.instance, key.param_env).unwrap();
+ let ecx = mk_eval_cx(tcx, tcx.def_span(key.value.instance.def_id()), key.param_env);
let val = (|| {
- let op = ecx.raw_const_to_mplace(constant)?.into();
- // FIXME: Once the visitor infrastructure landed, change validation to
- // work directly on `MPlaceTy`.
- let mut ref_tracking = RefTracking::new(op);
- while let Some((op, path)) = ref_tracking.todo.pop() {
+ let mplace = ecx.raw_const_to_mplace(constant)?;
+ let mut ref_tracking = RefTracking::new(mplace);
+ while let Some((mplace, path)) = ref_tracking.todo.pop() {
ecx.validate_operand(
- op,
+ mplace.into(),
path,
Some(&mut ref_tracking),
- /* const_mode */ true,
+ true, // const mode
)?;
}
- // Now that we validated, turn this into a proper constant
+ // Now that we validated, turn this into a proper constant.
let def_id = cid.instance.def.def_id();
- let normalize = tcx.is_static(def_id).is_none() && cid.promoted.is_none();
- op_to_const(&ecx, op, normalize)
+ if tcx.is_static(def_id).is_some() || cid.promoted.is_some() {
+ mplace_to_const(&ecx, mplace)
+ } else {
+ op_to_const(&ecx, mplace.into())
+ }
})();
val.map_err(|error| {