Move more non-CTFE operations to the Machine

上级 dc6e877e
......@@ -7,6 +7,7 @@
#[macro_use]
extern crate log;
extern crate log_settings;
#[macro_use]
extern crate rustc;
extern crate rustc_const_math;
extern crate rustc_data_structures;
......@@ -25,8 +26,10 @@
pub use rustc_miri::interpret::*;
mod missing_fns;
mod operator;
use missing_fns::EvalContextExt as MissingFnsEvalContextExt;
use operator::EvalContextExt as OperatorEvalContextExt;
pub fn eval_main<'a, 'tcx: 'a>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
......@@ -280,4 +283,18 @@ fn call_missing_fn<'a>(
) -> EvalResult<'tcx> {
ecx.call_missing_fn(instance, destination, arg_operands, sig, path)
}
fn ptr_op<'a>(
ecx: &rustc_miri::interpret::EvalContext<'a, 'tcx, Self>,
bin_op: mir::BinOp,
left: PrimVal,
left_ty: ty::Ty<'tcx>,
right: PrimVal,
right_ty: ty::Ty<'tcx>,
) -> EvalResult<'tcx, Option<(PrimVal, bool)>> {
ecx.ptr_op(bin_op, left, left_ty, right, right_ty)
}
fn check_non_const_fn_call(_instance: ty::Instance<'tcx>) -> EvalResult<'tcx> {
Ok(())
}
}
use rustc::ty;
use rustc::mir;
use rustc_miri::interpret::*;
pub trait EvalContextExt<'tcx> {
fn ptr_op(
&self,
bin_op: mir::BinOp,
left: PrimVal,
left_ty: ty::Ty<'tcx>,
right: PrimVal,
right_ty: ty::Ty<'tcx>,
) -> EvalResult<'tcx, Option<(PrimVal, bool)>>;
fn ptr_int_arithmetic(
&self,
bin_op: mir::BinOp,
left: MemoryPointer,
right: i128,
signed: bool,
) -> EvalResult<'tcx, (PrimVal, bool)>;
}
impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator> {
fn ptr_op(
&self,
bin_op: mir::BinOp,
left: PrimVal,
left_ty: ty::Ty<'tcx>,
right: PrimVal,
right_ty: ty::Ty<'tcx>,
) -> EvalResult<'tcx, Option<(PrimVal, bool)>> {
use rustc_miri::interpret::PrimValKind::*;
use rustc::mir::BinOp::*;
let usize = PrimValKind::from_uint_size(self.memory.pointer_size());
let isize = PrimValKind::from_int_size(self.memory.pointer_size());
let left_kind = self.ty_to_primval_kind(left_ty)?;
let right_kind = self.ty_to_primval_kind(right_ty)?;
match bin_op {
Offset if left_kind == Ptr && right_kind == usize => {
let pointee_ty = left_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).expect("Offset called on non-ptr type").ty;
let ptr = self.pointer_offset(left.into(), pointee_ty, right.to_bytes()? as i64)?;
Ok(Some((ptr.into_inner_primval(), false)))
},
// These work on anything
Eq if left_kind == right_kind => {
let result = match (left, right) {
(PrimVal::Bytes(left), PrimVal::Bytes(right)) => left == right,
(PrimVal::Ptr(left), PrimVal::Ptr(right)) => left == right,
(PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes),
_ => false,
};
Ok(Some((PrimVal::from_bool(result), false)))
}
Ne if left_kind == right_kind => {
let result = match (left, right) {
(PrimVal::Bytes(left), PrimVal::Bytes(right)) => left != right,
(PrimVal::Ptr(left), PrimVal::Ptr(right)) => left != right,
(PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes),
_ => true,
};
Ok(Some((PrimVal::from_bool(result), false)))
}
// These need both pointers to be in the same allocation
Lt | Le | Gt | Ge | Sub
if left_kind == right_kind
&& (left_kind == Ptr || left_kind == usize || left_kind == isize)
&& left.is_ptr() && right.is_ptr() => {
let left = left.to_ptr()?;
let right = right.to_ptr()?;
if left.alloc_id == right.alloc_id {
let res = match bin_op {
Lt => left.offset < right.offset,
Le => left.offset <= right.offset,
Gt => left.offset > right.offset,
Ge => left.offset >= right.offset,
Sub => return self.binary_op(
Sub,
PrimVal::Bytes(left.offset as u128),
self.tcx.types.usize,
PrimVal::Bytes(right.offset as u128),
self.tcx.types.usize,
).map(Some),
_ => bug!("We already established it has to be one of these operators."),
};
Ok(Some((PrimVal::from_bool(res), false)))
} else {
// Both are pointers, but from different allocations.
Err(EvalError::InvalidPointerMath)
}
}
// These work if one operand is a pointer, the other an integer
Add | BitAnd | Sub
if left_kind == right_kind && (left_kind == usize || left_kind == isize)
&& left.is_ptr() && right.is_bytes() => {
// Cast to i128 is fine as we checked the kind to be ptr-sized
self.ptr_int_arithmetic(bin_op, left.to_ptr()?, right.to_bytes()? as i128, left_kind == isize).map(Some)
}
Add | BitAnd
if left_kind == right_kind && (left_kind == usize || left_kind == isize)
&& left.is_bytes() && right.is_ptr() => {
// This is a commutative operation, just swap the operands
self.ptr_int_arithmetic(bin_op, right.to_ptr()?, left.to_bytes()? as i128, left_kind == isize).map(Some)
}
_ => Ok(None)
}
}
fn ptr_int_arithmetic(
&self,
bin_op: mir::BinOp,
left: MemoryPointer,
right: i128,
signed: bool,
) -> EvalResult<'tcx, (PrimVal, bool)> {
use rustc::mir::BinOp::*;
fn map_to_primval((res, over) : (MemoryPointer, bool)) -> (PrimVal, bool) {
(PrimVal::Ptr(res), over)
}
Ok(match bin_op {
Sub =>
// The only way this can overflow is by underflowing, so signdeness of the right operands does not matter
map_to_primval(left.overflowing_signed_offset(-right, self)),
Add if signed =>
map_to_primval(left.overflowing_signed_offset(right, self)),
Add if !signed =>
map_to_primval(left.overflowing_offset(right as u64, self)),
BitAnd if !signed => {
let base_mask : u64 = !(self.memory.get(left.alloc_id)?.align - 1);
let right = right as u64;
if right & base_mask == base_mask {
// Case 1: The base address bits are all preserved, i.e., right is all-1 there
(PrimVal::Ptr(MemoryPointer::new(left.alloc_id, left.offset & right)), false)
} else if right & base_mask == 0 {
// Case 2: The base address bits are all taken away, i.e., right is all-0 there
(PrimVal::from_u128((left.offset & right) as u128), false)
} else {
return Err(EvalError::ReadPointerAsBytes);
}
}
_ => {
let msg = format!("unimplemented binary op on pointer {:?}: {:?}, {:?} ({})", bin_op, left, right, if signed { "signed" } else { "unsigned" });
return Err(EvalError::Unimplemented(msg));
}
})
}
}
......@@ -5,7 +5,7 @@
use syntax::ast::Mutability;
use super::{
EvalError, EvalResult,
EvalResult, EvalError,
Global, GlobalId, Lvalue,
PrimVal,
EvalContext, StackPopCleanup,
......@@ -13,6 +13,9 @@
use rustc_const_math::ConstInt;
use std::fmt;
use std::error::Error;
pub fn eval_body_as_primval<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
instance: Instance<'tcx>,
......@@ -21,7 +24,7 @@ pub fn eval_body_as_primval<'a, 'tcx>(
let mut ecx = EvalContext::<Evaluator>::new(tcx, limits, (), ());
let cid = GlobalId { instance, promoted: None };
if ecx.tcx.has_attr(instance.def_id(), "linkage") {
return Err(EvalError::NotConst("extern global".to_string()));
return Err(ConstEvalError::NotConst("extern global".to_string()).into());
}
let mir = ecx.load_mir(instance.def)?;
......@@ -75,12 +78,52 @@ pub fn eval_body_as_integer<'a, 'tcx>(
TyUint(UintTy::U64) => ConstInt::U64(prim as u64),
TyUint(UintTy::U128) => ConstInt::U128(prim),
TyUint(UintTy::Us) => ConstInt::Usize(ConstUsize::new(prim as u64, tcx.sess.target.uint_type).expect("miri should already have errored")),
_ => return Err(EvalError::NeedsRfc("evaluating anything other than isize/usize during typeck".to_string())),
_ => return Err(ConstEvalError::NeedsRfc("evaluating anything other than isize/usize during typeck".to_string()).into()),
})
}
struct Evaluator;
impl<'tcx> Into<EvalError<'tcx>> for ConstEvalError {
fn into(self) -> EvalError<'tcx> {
EvalError::MachineError(Box::new(self))
}
}
#[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",
}
}
fn cause(&self) -> Option<&Error> {
None
}
}
impl<'tcx> super::Machine<'tcx> for Evaluator {
type Data = ();
type MemoryData = ();
......@@ -93,6 +136,21 @@ fn call_missing_fn<'a>(
path: String,
) -> EvalResult<'tcx> {
// some simple things like `malloc` might get accepted in the future
Err(EvalError::NeedsRfc(format!("calling extern function `{}`", path)))
Err(ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path)).into())
}
fn 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)>> {
Err(ConstEvalError::NeedsRfc("Pointer arithmetic or comparison".to_string()).into())
}
fn check_non_const_fn_call(instance: ty::Instance<'tcx>) -> EvalResult<'tcx> {
return Err(ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into());
}
}
......@@ -10,8 +10,11 @@
use rustc_const_math::ConstMathErr;
use syntax::codemap::Span;
#[derive(Clone, Debug)]
#[derive(Debug)]
pub enum EvalError<'tcx> {
/// This variant is used by machines to signal their own errors that do not
/// match an existing variant
MachineError(Box<Error>),
FunctionPointerTyMismatch(FnSig<'tcx>, FnSig<'tcx>),
NoMirFor(String),
UnterminatedCString(MemoryPointer),
......@@ -95,8 +98,6 @@ pub enum EvalError<'tcx> {
HeapAllocNonPowerOfTwoAlignment(u64),
Unreachable,
Panic,
NeedsRfc(String),
NotConst(String),
ReadFromReturnPointer,
PathNotFound(Vec<String>),
}
......@@ -107,6 +108,7 @@ impl<'tcx> Error for EvalError<'tcx> {
fn description(&self) -> &str {
use self::EvalError::*;
match *self {
MachineError(ref inner) => inner.description(),
FunctionPointerTyMismatch(..) =>
"tried to call a function through a function pointer of a different type",
InvalidMemoryAccess =>
......@@ -211,10 +213,6 @@ fn description(&self) -> &str {
"entered unreachable code",
Panic =>
"the evaluated program panicked",
NeedsRfc(_) =>
"this feature needs an rfc before being allowed inside constants",
NotConst(_) =>
"this feature is not compatible with constant evaluation",
ReadFromReturnPointer =>
"tried to read from the return pointer",
EvalError::PathNotFound(_) =>
......@@ -222,7 +220,13 @@ fn description(&self) -> &str {
}
}
fn cause(&self) -> Option<&Error> { None }
fn cause(&self) -> Option<&Error> {
use self::EvalError::*;
match *self {
MachineError(ref inner) => Some(&**inner),
_ => None,
}
}
}
impl<'tcx> fmt::Display for EvalError<'tcx> {
......@@ -278,12 +282,10 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "expected primitive type, got {}", ty),
Layout(ref err) =>
write!(f, "rustc layout computation failed: {:?}", err),
NeedsRfc(ref msg) =>
write!(f, "\"{}\" needs an rfc before being allowed inside constants", msg),
NotConst(ref msg) =>
write!(f, "Cannot evaluate within constants: \"{}\"", msg),
EvalError::PathNotFound(ref path) =>
PathNotFound(ref path) =>
write!(f, "Cannot find path {:?}", path),
MachineError(ref inner) =>
write!(f, "machine error: {}", inner),
_ => write!(f, "{}", self.description()),
}
}
......
......@@ -666,9 +666,7 @@ pub(super) fn eval_rvalue_into_lvalue(
}
Len(ref lvalue) => {
if self.const_env() {
return Err(EvalError::NeedsRfc("computing the length of arrays".to_string()));
}
// FIXME(CTFE): don't allow computing the length of arrays in const eval
let src = self.eval_lvalue(lvalue)?;
let ty = self.lvalue_ty(lvalue);
let (_, len) = src.elem_ty_and_len(ty);
......@@ -692,9 +690,7 @@ pub(super) fn eval_rvalue_into_lvalue(
}
NullaryOp(mir::NullOp::Box, ty) => {
if self.const_env() {
return Err(EvalError::NeedsRfc("\"heap\" allocations".to_string()));
}
// FIXME(CTFE): don't allow heap allocations in const eval
// FIXME: call the `exchange_malloc` lang item if available
let size = self.type_size(ty)?.expect("box only works with sized types");
if size == 0 {
......@@ -708,9 +704,6 @@ pub(super) fn eval_rvalue_into_lvalue(
}
NullaryOp(mir::NullOp::SizeOf, ty) => {
if self.const_env() {
return Err(EvalError::NeedsRfc("computing the size of types (size_of)".to_string()));
}
let size = self.type_size(ty)?.expect("SizeOf nullary MIR operator called for unsized type");
self.write_primval(dest, PrimVal::from_u128(size as u128), dest_ty)?;
}
......@@ -944,7 +937,7 @@ pub(super) fn wrapping_pointer_offset(&self, ptr: Pointer, pointee_ty: Ty<'tcx>,
ptr.wrapping_signed_offset(offset, self)
}
pub(super) fn pointer_offset(&self, ptr: Pointer, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, Pointer> {
pub fn pointer_offset(&self, ptr: Pointer, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, Pointer> {
// This function raises an error if the offset moves the pointer outside of its allocation. We consider
// ZSTs their own huge allocation that doesn't overlap with anything (and nothing moves in there because the size is 0).
// We also consider the NULL pointer its own separate allocation, and all the remaining integers pointers their own
......
......@@ -6,6 +6,7 @@
EvalResult,
EvalContext,
Lvalue,
PrimVal
};
use rustc::{mir, ty};
......@@ -29,5 +30,25 @@ fn call_missing_fn<'a>(
sig: ty::FnSig<'tcx>,
path: String,
) -> EvalResult<'tcx>;
/// Called when operating on the value of pointers.
///
/// Returns `None` if the operation should be handled by the integer
/// op code
///
/// Returns a (value, overflowed) pair otherwise
fn ptr_op<'a>(
ecx: &EvalContext<'a, 'tcx, Self>,
bin_op: mir::BinOp,
left: PrimVal,
left_ty: ty::Ty<'tcx>,
right: PrimVal,
right_ty: ty::Ty<'tcx>,
) -> EvalResult<'tcx, Option<(PrimVal, bool)>>;
/// Called when adding a frame for a function that's not `const fn`
///
/// Const eval returns `Err`, miri returns `Ok`
fn check_non_const_fn_call(instance: ty::Instance<'tcx>) -> EvalResult<'tcx>;
}
......@@ -198,7 +198,7 @@ pub(crate) fn wrapping_signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> S
MemoryPointer::new(self.alloc_id, cx.data_layout().wrapping_signed_offset(self.offset, i))
}
pub(crate) fn overflowing_signed_offset<C: HasDataLayout>(self, i: i128, cx: C) -> (Self, bool) {
pub fn overflowing_signed_offset<C: HasDataLayout>(self, i: i128, cx: C) -> (Self, bool) {
let (res, over) = cx.data_layout().overflowing_signed_offset(self.offset, i);
(MemoryPointer::new(self.alloc_id, res), over)
}
......@@ -207,7 +207,7 @@ pub(crate) fn signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> EvalResult
Ok(MemoryPointer::new(self.alloc_id, cx.data_layout().signed_offset(self.offset, i)?))
}
pub(crate) fn overflowing_offset<C: HasDataLayout>(self, i: u64, cx: C) -> (Self, bool) {
pub fn overflowing_offset<C: HasDataLayout>(self, i: u64, cx: C) -> (Self, bool) {
let (res, over) = cx.data_layout().overflowing_offset(self.offset, i);
(MemoryPointer::new(self.alloc_id, res), over)
}
......
use rustc::mir;
use rustc::ty::{self, Ty};
use rustc::ty::Ty;
use super::{
EvalError, EvalResult,
EvalContext,
MemoryPointer,
Lvalue,
Machine,
};
......@@ -153,75 +152,9 @@ pub fn binary_op(
//trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind);
// I: Handle operations that support pointers
let usize = PrimValKind::from_uint_size(self.memory.pointer_size());
let isize = PrimValKind::from_int_size(self.memory.pointer_size());
if !left_kind.is_float() && !right_kind.is_float() {
if (!left.is_bytes() && !right.is_bytes()) && self.const_env() {
return Err(EvalError::NeedsRfc("Pointer arithmetic or comparison".to_string()));
}
match bin_op {
Offset if left_kind == Ptr && right_kind == usize => {
let pointee_ty = left_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).expect("Offset called on non-ptr type").ty;
let ptr = self.pointer_offset(left.into(), pointee_ty, right.to_bytes()? as i64)?;
return Ok((ptr.into_inner_primval(), false));
},
// These work on anything
Eq if left_kind == right_kind => {
let result = match (left, right) {
(PrimVal::Bytes(left), PrimVal::Bytes(right)) => left == right,
(PrimVal::Ptr(left), PrimVal::Ptr(right)) => left == right,
(PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes),
_ => false,
};
return Ok((PrimVal::from_bool(result), false));
}
Ne if left_kind == right_kind => {
let result = match (left, right) {
(PrimVal::Bytes(left), PrimVal::Bytes(right)) => left != right,
(PrimVal::Ptr(left), PrimVal::Ptr(right)) => left != right,
(PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes),
_ => true,
};
return Ok((PrimVal::from_bool(result), false));
}
// These need both pointers to be in the same allocation
Lt | Le | Gt | Ge | Sub
if left_kind == right_kind
&& (left_kind == Ptr || left_kind == usize || left_kind == isize)
&& left.is_ptr() && right.is_ptr() => {
let left = left.to_ptr()?;
let right = right.to_ptr()?;
if left.alloc_id == right.alloc_id {
let res = match bin_op {
Lt => left.offset < right.offset,
Le => left.offset <= right.offset,
Gt => left.offset > right.offset,
Ge => left.offset >= right.offset,
Sub => {
return int_arithmetic!(left_kind, overflowing_sub, left.offset, right.offset);
}
_ => bug!("We already established it has to be one of these operators."),
};
return Ok((PrimVal::from_bool(res), false));
} else {
// Both are pointers, but from different allocations.
return Err(EvalError::InvalidPointerMath);
}
}
// These work if one operand is a pointer, the other an integer
Add | BitAnd | Sub
if left_kind == right_kind && (left_kind == usize || left_kind == isize)
&& left.is_ptr() && right.is_bytes() => {
// Cast to i128 is fine as we checked the kind to be ptr-sized
return self.ptr_int_arithmetic(bin_op, left.to_ptr()?, right.to_bytes()? as i128, left_kind == isize);
}
Add | BitAnd
if left_kind == right_kind && (left_kind == usize || left_kind == isize)
&& left.is_bytes() && right.is_ptr() => {
// This is a commutative operation, just swap the operands
return self.ptr_int_arithmetic(bin_op, right.to_ptr()?, left.to_bytes()? as i128, left_kind == isize);
}
_ => {}
if let Some(handled) = M::ptr_op(self, bin_op, left, left_ty, right, right_ty)? {
return Ok(handled);
}
}
......@@ -270,6 +203,9 @@ pub fn binary_op(
(Div, F64) => f64_arithmetic!(/, l, r),
(Rem, F64) => f64_arithmetic!(%, l, r),
(Eq, _) => PrimVal::from_bool(l == r),
(Ne, _) => PrimVal::from_bool(l != r),
(Lt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) < (r as i128)),
(Lt, _) => PrimVal::from_bool(l < r),
(Le, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) <= (r as i128)),
......@@ -297,49 +233,6 @@ pub fn binary_op(
Ok((val, false))
}
fn ptr_int_arithmetic(
&self,
bin_op: mir::BinOp,
left: MemoryPointer,
right: i128,
signed: bool,
) -> EvalResult<'tcx, (PrimVal, bool)> {
use rustc::mir::BinOp::*;
fn map_to_primval((res, over) : (MemoryPointer, bool)) -> (PrimVal, bool) {
(PrimVal::Ptr(res), over)
}
Ok(match bin_op {
Sub =>
// The only way this can overflow is by underflowing, so signdeness of the right operands does not matter
map_to_primval(left.overflowing_signed_offset(-right, self)),
Add if signed =>
map_to_primval(left.overflowing_signed_offset(right, self)),
Add if !signed =>
map_to_primval(left.overflowing_offset(right as u64, self)),
BitAnd if !signed => {
let base_mask : u64 = !(self.memory.get(left.alloc_id)?.align - 1);
let right = right as u64;
if right & base_mask == base_mask {
// Case 1: The base address bits are all preserved, i.e., right is all-1 there
(PrimVal::Ptr(MemoryPointer::new(left.alloc_id, left.offset & right)), false)
} else if right & base_mask == 0 {
// Case 2: The base address bits are all taken away, i.e., right is all-0 there
(PrimVal::from_u128((left.offset & right) as u128), false)
} else {
return Err(EvalError::ReadPointerAsBytes);
}
}
_ => {
let msg = format!("unimplemented binary op on pointer {:?}: {:?}, {:?} ({})", bin_op, left, right, if signed { "signed" } else { "unsigned" });
return Err(EvalError::Unimplemented(msg));
}
})
}
}
pub fn unary_op<'tcx>(
......
......@@ -40,9 +40,7 @@ pub(super) fn eval_terminator(
Goto { target } => self.goto_block(target),
SwitchInt { ref discr, ref values, ref targets, .. } => {
if self.const_env() {
return Err(EvalError::NeedsRfc("branching (if, match, loop, ...)".to_string()));
}
// FIXME(CTFE): forbid branching
let discr_val = self.eval_operand(discr)?;
let discr_ty = self.operand_ty(discr);
let discr_prim = self.value_to_primval(discr_val, discr_ty)?;
......@@ -100,9 +98,7 @@ pub(super) fn eval_terminator(
Drop { ref location, target, .. } => {
trace!("TerminatorKind::drop: {:?}, {:?}", location, self.substs());
if self.const_env() {
return Err(EvalError::NeedsRfc("invoking `Drop::drop`".to_string()));
}
// FIXME(CTFE): forbid drop in const eval
let lval = self.eval_lvalue(location)?;
let ty = self.lvalue_ty(location);
self.goto_block(target);
......@@ -436,17 +432,14 @@ fn eval_fn_call_inner(
let mir = match self.load_mir(instance.def) {
Ok(mir) => mir,
Err(EvalError::NoMirFor(path)) => {
if self.const_env() {
return Err(EvalError::NeedsRfc(format!("calling extern function `{}`", path)));
}
M::call_missing_fn(self, instance, destination, arg_operands, sig, path)?;
return Ok(true);
},
Err(other) => return Err(other),
};
if self.const_env() && !self.tcx.is_const_fn(instance.def_id()) {
return Err(EvalError::NotConst(format!("calling non-const fn `{}`", instance)));
if !self.tcx.is_const_fn(instance.def_id()) {
M::check_non_const_fn_call(instance)?;
}
let (return_lvalue, return_to_block) = match destination {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册