Fully use miri in trans
This commit is contained in:
parent
b2b101befc
commit
b33e4e784e
78 changed files with 1935 additions and 2186 deletions
|
@ -15,9 +15,9 @@ use ty::{self, TyCtxt, layout};
|
|||
use ty::subst::Substs;
|
||||
use rustc_const_math::*;
|
||||
use mir::interpret::{Value, PrimVal};
|
||||
use errors::DiagnosticBuilder;
|
||||
|
||||
use graphviz::IntoCow;
|
||||
use errors::DiagnosticBuilder;
|
||||
use serialize;
|
||||
use syntax_pos::Span;
|
||||
|
||||
|
@ -169,6 +169,7 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> {
|
|||
|
||||
TypeckError => simple!("type-checking failed"),
|
||||
CheckMatchError => simple!("match-checking failed"),
|
||||
// FIXME: report a full backtrace
|
||||
Miri(ref err) => simple!("miri failed: {}", err),
|
||||
}
|
||||
}
|
||||
|
@ -186,7 +187,7 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> {
|
|||
err = i_err;
|
||||
}
|
||||
|
||||
let mut diag = struct_span_err!(tcx.sess, err.span, E0080, "constant evaluation error");
|
||||
let mut diag = struct_error(tcx, err.span, "constant evaluation error");
|
||||
err.note(tcx, primary_span, primary_kind, &mut diag);
|
||||
diag
|
||||
}
|
||||
|
@ -221,3 +222,11 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> {
|
|||
self.struct_error(tcx, primary_span, primary_kind).emit();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn struct_error<'a, 'gcx, 'tcx>(
|
||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
span: Span,
|
||||
msg: &str,
|
||||
) -> DiagnosticBuilder<'gcx> {
|
||||
struct_span_err!(tcx.sess, span, E0080, "{}", msg)
|
||||
}
|
||||
|
|
|
@ -286,8 +286,8 @@ impl<'tcx> fmt::Display for EvalError<'tcx> {
|
|||
write!(f, "tried to reallocate memory from {} to {}", old, new),
|
||||
DeallocatedWrongMemoryKind(ref old, ref new) =>
|
||||
write!(f, "tried to deallocate {} memory but gave {} as the kind", old, new),
|
||||
Math(span, ref err) =>
|
||||
write!(f, "{:?} at {:?}", err, span),
|
||||
Math(_, ref err) =>
|
||||
write!(f, "{}", err.description()),
|
||||
Intrinsic(ref err) =>
|
||||
write!(f, "{}", err),
|
||||
InvalidChar(c) =>
|
||||
|
|
|
@ -169,6 +169,8 @@ pub struct Allocation {
|
|||
pub undef_mask: UndefMask,
|
||||
/// The alignment of the allocation to detect unaligned reads.
|
||||
pub align: Align,
|
||||
/// Whether the allocation should be put into mutable memory when translating via llvm
|
||||
pub mutable: bool,
|
||||
}
|
||||
|
||||
impl Allocation {
|
||||
|
@ -180,6 +182,7 @@ impl Allocation {
|
|||
relocations: BTreeMap::new(),
|
||||
undef_mask,
|
||||
align: Align::from_bytes(1, 1).unwrap(),
|
||||
mutable: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1898,13 +1898,16 @@ fn print_miri_value<W: Write>(value: Value, ty: Ty, f: &mut W) -> fmt::Result {
|
|||
let alloc = tcx
|
||||
.interpret_interner
|
||||
.borrow()
|
||||
.get_alloc(ptr.alloc_id)
|
||||
.expect("miri alloc not found");
|
||||
assert_eq!(len as usize as u128, len);
|
||||
let slice = &alloc.bytes[(ptr.offset as usize)..][..(len as usize)];
|
||||
let s = ::std::str::from_utf8(slice)
|
||||
.expect("non utf8 str from miri");
|
||||
write!(f, "{:?}", s)
|
||||
.get_alloc(ptr.alloc_id);
|
||||
if let Some(alloc) = alloc {
|
||||
assert_eq!(len as usize as u128, len);
|
||||
let slice = &alloc.bytes[(ptr.offset as usize)..][..(len as usize)];
|
||||
let s = ::std::str::from_utf8(slice)
|
||||
.expect("non utf8 str from miri");
|
||||
write!(f, "{:?}", s)
|
||||
} else {
|
||||
write!(f, "pointer to erroneous constant {:?}, {:?}", ptr, len)
|
||||
}
|
||||
})
|
||||
},
|
||||
_ => write!(f, "{:?}:{}", value, ty),
|
||||
|
|
|
@ -32,7 +32,6 @@ use hir;
|
|||
use hir::def_id::DefId;
|
||||
use infer::{self, InferCtxt};
|
||||
use infer::type_variable::TypeVariableOrigin;
|
||||
use middle::const_val;
|
||||
use std::fmt;
|
||||
use syntax::ast;
|
||||
use session::DiagnosticMessageId;
|
||||
|
@ -776,7 +775,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
|
||||
ConstEvalFailure(ref err) => {
|
||||
if let const_val::ErrKind::TypeckError = err.kind {
|
||||
if let ::middle::const_val::ErrKind::TypeckError = err.kind {
|
||||
return;
|
||||
}
|
||||
err.struct_error(self.tcx, span, "constant expression")
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
// except according to those terms.
|
||||
|
||||
use infer::{RegionObligation, InferCtxt};
|
||||
use middle::const_val::ConstEvalErr;
|
||||
use middle::const_val::ErrKind::TypeckError;
|
||||
use mir::interpret::GlobalId;
|
||||
use ty::{self, Ty, TypeFoldable, ToPolyTraitRef, ToPredicate};
|
||||
use ty::error::ExpectedFound;
|
||||
|
@ -18,6 +16,7 @@ use rustc_data_structures::obligation_forest::{ObligationForest, Error};
|
|||
use rustc_data_structures::obligation_forest::{ForestObligation, ObligationProcessor};
|
||||
use std::marker::PhantomData;
|
||||
use hir::def_id::DefId;
|
||||
use middle::const_val::{ConstEvalErr, ErrKind};
|
||||
|
||||
use super::CodeAmbiguity;
|
||||
use super::CodeProjectionError;
|
||||
|
@ -532,12 +531,12 @@ fn process_predicate<'a, 'gcx, 'tcx>(
|
|||
match selcx.tcx().at(obligation.cause.span)
|
||||
.const_eval(param_env.and(cid)) {
|
||||
Ok(_) => Ok(Some(vec![])),
|
||||
Err(e) => Err(CodeSelectionError(ConstEvalFailure(e)))
|
||||
Err(err) => Err(CodeSelectionError(ConstEvalFailure(err)))
|
||||
}
|
||||
} else {
|
||||
Err(CodeSelectionError(ConstEvalFailure(ConstEvalErr {
|
||||
span: selcx.tcx().def_span(def_id),
|
||||
kind: TypeckError,
|
||||
span: obligation.cause.span,
|
||||
kind: ErrKind::UnimplementedConstVal("could not resolve"),
|
||||
})))
|
||||
}
|
||||
},
|
||||
|
|
|
@ -20,8 +20,8 @@ pub use self::ObligationCauseCode::*;
|
|||
use hir;
|
||||
use hir::def_id::DefId;
|
||||
use infer::outlives::env::OutlivesEnvironment;
|
||||
use middle::const_val::ConstEvalErr;
|
||||
use middle::region;
|
||||
use middle::const_val::ConstEvalErr;
|
||||
use ty::subst::Substs;
|
||||
use ty::{self, AdtKind, Ty, TyCtxt, TypeFoldable, ToPredicate};
|
||||
use ty::error::{ExpectedFound, TypeError};
|
||||
|
|
|
@ -907,16 +907,17 @@ pub struct InterpretInterner<'tcx> {
|
|||
alloc_by_id: FxHashMap<interpret::AllocId, &'tcx interpret::Allocation>,
|
||||
|
||||
/// Reverse map of `alloc_cache`
|
||||
///
|
||||
/// Multiple globals may share the same memory
|
||||
global_cache: FxHashMap<interpret::AllocId, Vec<interpret::GlobalId<'tcx>>>,
|
||||
global_cache: FxHashMap<interpret::AllocId, DefId>,
|
||||
|
||||
/// The AllocId to assign to the next new regular allocation.
|
||||
/// Always incremented, never gets smaller.
|
||||
next_id: interpret::AllocId,
|
||||
|
||||
/// Allows checking whether a constant already has an allocation
|
||||
alloc_cache: FxHashMap<interpret::GlobalId<'tcx>, interpret::AllocId>,
|
||||
/// Allows checking whether a static already has an allocation
|
||||
///
|
||||
/// This is only important for detecting statics referring to themselves
|
||||
// FIXME(oli-obk) move it to the EvalContext?
|
||||
alloc_cache: FxHashMap<DefId, interpret::AllocId>,
|
||||
|
||||
/// A cache for basic byte allocations keyed by their contents. This is used to deduplicate
|
||||
/// allocations for string and bytestring literals.
|
||||
|
@ -951,30 +952,27 @@ impl<'tcx> InterpretInterner<'tcx> {
|
|||
|
||||
pub fn get_cached(
|
||||
&self,
|
||||
global_id: interpret::GlobalId<'tcx>,
|
||||
static_id: DefId,
|
||||
) -> Option<interpret::AllocId> {
|
||||
self.alloc_cache.get(&global_id).cloned()
|
||||
self.alloc_cache.get(&static_id).cloned()
|
||||
}
|
||||
|
||||
pub fn cache(
|
||||
&mut self,
|
||||
global_id: interpret::GlobalId<'tcx>,
|
||||
static_id: DefId,
|
||||
alloc_id: interpret::AllocId,
|
||||
) {
|
||||
self.global_cache.entry(alloc_id).or_default().push(global_id);
|
||||
if let Some(old) = self.alloc_cache.insert(global_id, alloc_id) {
|
||||
bug!("tried to cache {:?}, but was already existing as {:#?}", global_id, old);
|
||||
self.global_cache.insert(alloc_id, static_id);
|
||||
if let Some(old) = self.alloc_cache.insert(static_id, alloc_id) {
|
||||
bug!("tried to cache {:?}, but was already existing as {:#?}", static_id, old);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_globals(
|
||||
pub fn get_corresponding_static_def_id(
|
||||
&self,
|
||||
ptr: interpret::AllocId,
|
||||
) -> &[interpret::GlobalId<'tcx>] {
|
||||
match self.global_cache.get(&ptr) {
|
||||
Some(v) => v,
|
||||
None => &[],
|
||||
}
|
||||
) -> Option<DefId> {
|
||||
self.global_cache.get(&ptr).cloned()
|
||||
}
|
||||
|
||||
pub fn intern_at_reserved(
|
||||
|
|
|
@ -16,7 +16,6 @@ use hir::{self, TraitCandidate, ItemLocalId, TransFnAttrs};
|
|||
use hir::svh::Svh;
|
||||
use lint;
|
||||
use middle::borrowck::BorrowCheckResult;
|
||||
use middle::const_val;
|
||||
use middle::cstore::{ExternCrate, LinkagePreference, NativeLibrary,
|
||||
ExternBodyNestedBodies};
|
||||
use middle::cstore::{NativeLibraryKind, DepKind, CrateSource, ExternConstBody};
|
||||
|
@ -27,6 +26,7 @@ use middle::resolve_lifetime::{ResolveLifetimes, Region, ObjectLifetimeDefault};
|
|||
use middle::stability::{self, DeprecationEntry};
|
||||
use middle::lang_items::{LanguageItems, LangItem};
|
||||
use middle::exported_symbols::{SymbolExportLevel, ExportedSymbol};
|
||||
use middle::const_val::EvalResult;
|
||||
use mir::mono::{CodegenUnit, Stats};
|
||||
use mir;
|
||||
use mir::interpret::{GlobalId};
|
||||
|
@ -212,7 +212,7 @@ define_maps! { <'tcx>
|
|||
/// Results of evaluating const items or constants embedded in
|
||||
/// other items (such as enum variant explicit discriminants).
|
||||
[] fn const_eval: const_eval_dep_node(ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>)
|
||||
-> const_val::EvalResult<'tcx>,
|
||||
-> EvalResult<'tcx>,
|
||||
|
||||
[] fn check_match: CheckMatch(DefId)
|
||||
-> Result<(), ErrorReported>,
|
||||
|
|
|
@ -1854,11 +1854,11 @@ impl<'a, 'gcx, 'tcx> AdtDef {
|
|||
b, uint_type, tcx.sess.target.usize_ty).unwrap(),
|
||||
};
|
||||
}
|
||||
err => {
|
||||
_ => {
|
||||
if !expr_did.is_local() {
|
||||
span_bug!(tcx.def_span(expr_did),
|
||||
"variant discriminant evaluation succeeded \
|
||||
in its crate but failed locally: {:?}", err);
|
||||
in its crate but failed locally");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1910,11 +1910,11 @@ impl<'a, 'gcx, 'tcx> AdtDef {
|
|||
};
|
||||
break;
|
||||
}
|
||||
err => {
|
||||
_ => {
|
||||
if !expr_did.is_local() {
|
||||
span_bug!(tcx.def_span(expr_did),
|
||||
"variant discriminant evaluation succeeded \
|
||||
in its crate but failed locally: {:?}", err);
|
||||
in its crate but failed locally");
|
||||
}
|
||||
if explicit_index == 0 {
|
||||
break;
|
||||
|
|
|
@ -691,22 +691,32 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Check if the node pointed to by def_id is a mutable static item
|
||||
pub fn is_static_mut(&self, def_id: DefId) -> bool {
|
||||
/// Return whether the node pointed to by def_id is a static item, and its mutability
|
||||
pub fn is_static(&self, def_id: DefId) -> Option<hir::Mutability> {
|
||||
if let Some(node) = self.hir.get_if_local(def_id) {
|
||||
match node {
|
||||
Node::NodeItem(&hir::Item {
|
||||
node: hir::ItemStatic(_, hir::MutMutable, _), ..
|
||||
}) => true,
|
||||
node: hir::ItemStatic(_, mutbl, _), ..
|
||||
}) => Some(mutbl),
|
||||
Node::NodeForeignItem(&hir::ForeignItem {
|
||||
node: hir::ForeignItemStatic(_, mutbl), ..
|
||||
}) => mutbl,
|
||||
_ => false
|
||||
node: hir::ForeignItemStatic(_, is_mutbl), ..
|
||||
}) =>
|
||||
Some(if is_mutbl {
|
||||
hir::Mutability::MutMutable
|
||||
} else {
|
||||
hir::Mutability::MutImmutable
|
||||
}),
|
||||
_ => None
|
||||
}
|
||||
} else {
|
||||
match self.describe_def(def_id) {
|
||||
Some(Def::Static(_, mutbl)) => mutbl,
|
||||
_ => false
|
||||
Some(Def::Static(_, is_mutbl)) =>
|
||||
Some(if is_mutbl {
|
||||
hir::Mutability::MutMutable
|
||||
} else {
|
||||
hir::Mutability::MutImmutable
|
||||
}),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -664,6 +664,16 @@ extern "C" {
|
|||
pub fn LLVMConstShl(LHSConstant: ValueRef, RHSConstant: ValueRef) -> ValueRef;
|
||||
pub fn LLVMConstLShr(LHSConstant: ValueRef, RHSConstant: ValueRef) -> ValueRef;
|
||||
pub fn LLVMConstAShr(LHSConstant: ValueRef, RHSConstant: ValueRef) -> ValueRef;
|
||||
pub fn LLVMConstGEP(
|
||||
ConstantVal: ValueRef,
|
||||
ConstantIndices: *const ValueRef,
|
||||
NumIndices: c_uint,
|
||||
) -> ValueRef;
|
||||
pub fn LLVMConstInBoundsGEP(
|
||||
ConstantVal: ValueRef,
|
||||
ConstantIndices: *const ValueRef,
|
||||
NumIndices: c_uint,
|
||||
) -> ValueRef;
|
||||
pub fn LLVMConstTrunc(ConstantVal: ValueRef, ToType: TypeRef) -> ValueRef;
|
||||
pub fn LLVMConstZExt(ConstantVal: ValueRef, ToType: TypeRef) -> ValueRef;
|
||||
pub fn LLVMConstUIToFP(ConstantVal: ValueRef, ToType: TypeRef) -> ValueRef;
|
||||
|
|
|
@ -298,9 +298,7 @@ impl<'a, 'tcx> SpecializedDecoder<interpret::AllocId> for DecodeContext<'a, 'tcx
|
|||
let allocation = self.tcx.unwrap().intern_const_alloc(allocation);
|
||||
interpret_interner().intern_at_reserved(alloc_id, allocation);
|
||||
|
||||
let num = usize::decode(self)?;
|
||||
for _ in 0..num {
|
||||
let glob = interpret::GlobalId::decode(self)?;
|
||||
if let Some(glob) = Option::<DefId>::decode(self)? {
|
||||
interpret_interner().cache(glob, alloc_id);
|
||||
}
|
||||
|
||||
|
|
|
@ -210,11 +210,9 @@ impl<'a, 'tcx> SpecializedEncoder<interpret::AllocId> for EncodeContext<'a, 'tcx
|
|||
trace!("encoding {:?} with {:#?}", alloc_id, alloc);
|
||||
usize::max_value().encode(self)?;
|
||||
alloc.encode(self)?;
|
||||
let globals = interpret_interner.get_globals(*alloc_id);
|
||||
globals.len().encode(self)?;
|
||||
for glob in globals {
|
||||
glob.encode(self)?;
|
||||
}
|
||||
interpret_interner
|
||||
.get_corresponding_static_def_id(*alloc_id)
|
||||
.encode(self)?;
|
||||
} else if let Some(fn_instance) = interpret_interner.get_fn(*alloc_id) {
|
||||
trace!("encoding {:?} with {:#?}", alloc_id, fn_instance);
|
||||
(usize::max_value() - 1).encode(self)?;
|
||||
|
|
|
@ -1635,11 +1635,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
|||
Mutability::Mut => Ok(()),
|
||||
}
|
||||
}
|
||||
Place::Static(ref static_) => if !self.tcx.is_static_mut(static_.def_id) {
|
||||
Err(place)
|
||||
} else {
|
||||
Ok(())
|
||||
},
|
||||
Place::Static(ref static_) =>
|
||||
if self.tcx.is_static(static_.def_id) != Some(hir::Mutability::MutMutable) {
|
||||
Err(place)
|
||||
} else {
|
||||
Ok(())
|
||||
},
|
||||
Place::Projection(ref proj) => {
|
||||
match proj.elem {
|
||||
ProjectionElem::Deref => {
|
||||
|
@ -1792,7 +1793,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
|||
if static1.def_id != static2.def_id {
|
||||
debug!("place_element_conflict: DISJOINT-STATIC");
|
||||
Overlap::Disjoint
|
||||
} else if self.tcx.is_static_mut(static1.def_id) {
|
||||
} else if self.tcx.is_static(static1.def_id) == Some(hir::Mutability::MutMutable) {
|
||||
// We ignore mutable statics - they can only be unsafe code.
|
||||
debug!("place_element_conflict: IGNORE-STATIC-MUT");
|
||||
Overlap::Disjoint
|
||||
|
|
|
@ -11,47 +11,77 @@
|
|||
//! Lints statically known runtime failures
|
||||
|
||||
use rustc::mir::*;
|
||||
use rustc::hir;
|
||||
use rustc::hir::map::Node;
|
||||
use rustc::mir::visit::Visitor;
|
||||
use rustc::mir::interpret::{Value, PrimVal, GlobalId};
|
||||
use rustc::middle::const_val::{ConstVal, ConstEvalErr, ErrKind};
|
||||
use rustc::hir::def::Def;
|
||||
use rustc::traits;
|
||||
use interpret::{eval_body_as_integer, check_body};
|
||||
use rustc::ty::{TyCtxt, ParamEnv, self};
|
||||
use interpret::eval_body_with_mir;
|
||||
use rustc::ty::{TyCtxt, ParamEnv};
|
||||
use rustc::ty::Instance;
|
||||
use rustc::ty::layout::LayoutOf;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::ty::subst::Substs;
|
||||
|
||||
fn is_const<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
|
||||
if let Some(node) = tcx.hir.get_if_local(def_id) {
|
||||
match node {
|
||||
Node::NodeItem(&hir::Item {
|
||||
node: hir::ItemConst(..), ..
|
||||
}) => true,
|
||||
_ => false
|
||||
}
|
||||
} else {
|
||||
match tcx.describe_def(def_id) {
|
||||
Some(Def::Const(_)) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
|
||||
if tcx.is_closure(def_id) {
|
||||
return;
|
||||
let mir = &tcx.optimized_mir(def_id);
|
||||
let substs = Substs::identity_for_item(tcx, def_id);
|
||||
let instance = Instance::new(def_id, substs);
|
||||
let param_env = tcx.param_env(def_id);
|
||||
|
||||
if is_const(tcx, def_id) {
|
||||
let cid = GlobalId {
|
||||
instance,
|
||||
promoted: None,
|
||||
};
|
||||
eval_body_with_mir(tcx, cid, mir, param_env);
|
||||
}
|
||||
let generics = tcx.generics_of(def_id);
|
||||
|
||||
ConstErrVisitor {
|
||||
tcx,
|
||||
mir,
|
||||
}.visit_mir(mir);
|
||||
let outer_def_id = if tcx.is_closure(def_id) {
|
||||
tcx.closure_base_def_id(def_id)
|
||||
} else {
|
||||
def_id
|
||||
};
|
||||
let generics = tcx.generics_of(outer_def_id);
|
||||
// FIXME: miri should be able to eval stuff that doesn't need info
|
||||
// from the generics
|
||||
if generics.parent_types as usize + generics.types.len() > 0 {
|
||||
return;
|
||||
}
|
||||
let mir = &tcx.optimized_mir(def_id);
|
||||
ConstErrVisitor {
|
||||
tcx,
|
||||
def_id,
|
||||
mir,
|
||||
}.visit_mir(mir);
|
||||
let param_env = ParamEnv::empty(traits::Reveal::All);
|
||||
let instance = Instance::mono(tcx, def_id);
|
||||
for i in 0.. mir.promoted.len() {
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
let cid = GlobalId {
|
||||
instance,
|
||||
promoted: Some(Promoted::new(i)),
|
||||
};
|
||||
check_body(tcx, cid, param_env);
|
||||
eval_body_with_mir(tcx, cid, mir, param_env);
|
||||
}
|
||||
}
|
||||
|
||||
struct ConstErrVisitor<'a, 'tcx: 'a> {
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
def_id: DefId,
|
||||
mir: &'a Mir<'tcx>,
|
||||
}
|
||||
|
||||
|
@ -61,22 +91,13 @@ impl<'a, 'tcx> ConstErrVisitor<'a, 'tcx> {
|
|||
Operand::Constant(ref c) => c,
|
||||
_ => return None,
|
||||
};
|
||||
let param_env = ParamEnv::empty(traits::Reveal::All);
|
||||
let val = match op.literal {
|
||||
match op.literal {
|
||||
Literal::Value { value } => match value.val {
|
||||
ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))) => b,
|
||||
ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))) => Some(b),
|
||||
_ => return None,
|
||||
},
|
||||
Literal::Promoted { index } => {
|
||||
let instance = Instance::mono(self.tcx, self.def_id);
|
||||
let cid = GlobalId {
|
||||
instance,
|
||||
promoted: Some(index),
|
||||
};
|
||||
eval_body_as_integer(self.tcx, cid, param_env).unwrap()
|
||||
}
|
||||
};
|
||||
Some(val)
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,13 +108,14 @@ impl<'a, 'tcx> Visitor<'tcx> for ConstErrVisitor<'a, 'tcx> {
|
|||
location: Location) {
|
||||
self.super_terminator(block, terminator, location);
|
||||
match terminator.kind {
|
||||
TerminatorKind::Assert { cond: Operand::Constant(box Constant {
|
||||
literal: Literal::Value {
|
||||
value: &ty::Const {
|
||||
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(cond))),
|
||||
.. }
|
||||
}, ..
|
||||
}), expected, ref msg, .. } if (cond == 1) != expected => {
|
||||
TerminatorKind::Assert { ref cond, expected, ref msg, .. } => {
|
||||
let cond = match self.eval_op(cond) {
|
||||
Some(val) => val,
|
||||
None => return,
|
||||
};
|
||||
if (cond == 1) == expected {
|
||||
return;
|
||||
}
|
||||
assert!(cond <= 1);
|
||||
// If we know we always panic, and the error message
|
||||
// is also constant, then we can produce a warning.
|
||||
|
|
|
@ -138,8 +138,18 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
|
|||
PatternError::AssociatedConstInPattern(span) => {
|
||||
self.span_e0158(span, "associated consts cannot be referenced in patterns")
|
||||
}
|
||||
PatternError::ConstEval(ref err) => {
|
||||
err.report(self.tcx, pat_span, "pattern");
|
||||
PatternError::FloatBug => {
|
||||
// FIXME(#31407) this is only necessary because float parsing is buggy
|
||||
::rustc::middle::const_val::struct_error(
|
||||
self.tcx, pat_span,
|
||||
"could not evaluate float literal (see issue #31407)",
|
||||
).emit();
|
||||
}
|
||||
PatternError::NonConstPath(span) => {
|
||||
::rustc::middle::const_val::struct_error(
|
||||
self.tcx, span,
|
||||
"runtime values cannot be referenced in patterns",
|
||||
).emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,7 @@
|
|||
// except according to those terms.
|
||||
|
||||
use rustc::middle::const_val::ConstVal::*;
|
||||
use rustc::middle::const_val::ErrKind::*;
|
||||
use rustc::middle::const_val::{ConstVal, ErrKind};
|
||||
use rustc::middle::const_val::ConstVal;
|
||||
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::ty::{self, Ty, TyCtxt};
|
||||
|
@ -39,7 +38,7 @@ pub fn lit_to_const<'a, 'tcx>(lit: &'tcx ast::LitKind,
|
|||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
neg: bool)
|
||||
-> Result<ConstVal<'tcx>, ErrKind<'tcx>> {
|
||||
-> Result<ConstVal<'tcx>, ()> {
|
||||
use syntax::ast::*;
|
||||
|
||||
use rustc::mir::interpret::*;
|
||||
|
@ -126,11 +125,8 @@ pub fn lit_to_const<'a, 'tcx>(lit: &'tcx ast::LitKind,
|
|||
}
|
||||
|
||||
fn parse_float<'tcx>(num: &str, fty: ast::FloatTy)
|
||||
-> Result<ConstFloat, ErrKind<'tcx>> {
|
||||
ConstFloat::from_str(num, fty).map_err(|_| {
|
||||
// FIXME(#31407) this is only necessary because float parsing is buggy
|
||||
UnimplementedConstVal("could not evaluate float literal (see issue #31407)")
|
||||
})
|
||||
-> Result<ConstFloat, ()> {
|
||||
ConstFloat::from_str(num, fty).map_err(|_| ())
|
||||
}
|
||||
|
||||
pub fn compare_const_vals(a: &ConstVal, b: &ConstVal, ty: Ty) -> Option<Ordering> {
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
use interpret::{const_val_field, const_discr};
|
||||
|
||||
use rustc::middle::const_val::{ConstEvalErr, ErrKind, ConstVal};
|
||||
use rustc::middle::const_val::ConstVal;
|
||||
use rustc::mir::{Field, BorrowKind, Mutability};
|
||||
use rustc::mir::interpret::{GlobalId, Value, PrimVal};
|
||||
use rustc::ty::{self, TyCtxt, AdtDef, Ty, Region};
|
||||
|
@ -28,10 +28,11 @@ use syntax::ptr::P;
|
|||
use syntax_pos::Span;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum PatternError<'tcx> {
|
||||
pub enum PatternError {
|
||||
AssociatedConstInPattern(Span),
|
||||
StaticInPattern(Span),
|
||||
ConstEval(ConstEvalErr<'tcx>),
|
||||
FloatBug,
|
||||
NonConstPath(Span),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
@ -279,7 +280,7 @@ pub struct PatternContext<'a, 'tcx: 'a> {
|
|||
pub param_env: ty::ParamEnv<'tcx>,
|
||||
pub tables: &'a ty::TypeckTables<'tcx>,
|
||||
pub substs: &'tcx Substs<'tcx>,
|
||||
pub errors: Vec<PatternError<'tcx>>,
|
||||
pub errors: Vec<PatternError>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Pattern<'tcx> {
|
||||
|
@ -650,10 +651,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
|
|||
}
|
||||
|
||||
_ => {
|
||||
self.errors.push(PatternError::ConstEval(ConstEvalErr {
|
||||
span,
|
||||
kind: ErrKind::NonConstPath,
|
||||
}));
|
||||
self.errors.push(PatternError::NonConstPath(span));
|
||||
PatternKind::Wild
|
||||
}
|
||||
}
|
||||
|
@ -673,24 +671,35 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
|
|||
let kind = match def {
|
||||
Def::Const(def_id) | Def::AssociatedConst(def_id) => {
|
||||
let substs = self.tables.node_substs(id);
|
||||
let instance = ty::Instance::resolve(
|
||||
match ty::Instance::resolve(
|
||||
self.tcx,
|
||||
self.param_env,
|
||||
def_id,
|
||||
substs,
|
||||
).unwrap();
|
||||
let cid = GlobalId {
|
||||
instance,
|
||||
promoted: None,
|
||||
};
|
||||
match self.tcx.at(span).const_eval(self.param_env.and(cid)) {
|
||||
Ok(value) => {
|
||||
return self.const_to_pat(instance, value, id, span)
|
||||
) {
|
||||
Some(instance) => {
|
||||
let cid = GlobalId {
|
||||
instance,
|
||||
promoted: None,
|
||||
};
|
||||
match self.tcx.at(span).const_eval(self.param_env.and(cid)) {
|
||||
Ok(value) => {
|
||||
return self.const_to_pat(instance, value, id, span)
|
||||
},
|
||||
Err(err) => {
|
||||
err.report(self.tcx, span, "pattern");
|
||||
PatternKind::Wild
|
||||
},
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
self.errors.push(PatternError::ConstEval(e));
|
||||
None => {
|
||||
self.errors.push(if is_associated_const {
|
||||
PatternError::AssociatedConstInPattern(span)
|
||||
} else {
|
||||
PatternError::StaticInPattern(span)
|
||||
});
|
||||
PatternKind::Wild
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
_ => self.lower_variant_or_leaf(def, span, ty, vec![]),
|
||||
|
@ -716,11 +725,8 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
|
|||
let cv = self.tcx.mk_const(ty::Const { val, ty });
|
||||
*self.const_to_pat(instance, cv, expr.hir_id, lit.span).kind
|
||||
},
|
||||
Err(e) => {
|
||||
self.errors.push(PatternError::ConstEval(ConstEvalErr {
|
||||
span: lit.span,
|
||||
kind: e,
|
||||
}));
|
||||
Err(()) => {
|
||||
self.errors.push(PatternError::FloatBug);
|
||||
PatternKind::Wild
|
||||
},
|
||||
}
|
||||
|
@ -733,17 +739,16 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
|
|||
_ => span_bug!(expr.span, "not a literal: {:?}", expr),
|
||||
};
|
||||
match super::eval::lit_to_const(&lit.node, self.tcx, ty, true) {
|
||||
Ok(value) => PatternKind::Constant {
|
||||
value: self.tcx.mk_const(ty::Const {
|
||||
ty,
|
||||
val: value,
|
||||
}),
|
||||
Ok(val) => {
|
||||
let instance = ty::Instance::new(
|
||||
self.tables.local_id_root.expect("literal outside any scope"),
|
||||
self.substs,
|
||||
);
|
||||
let cv = self.tcx.mk_const(ty::Const { val, ty });
|
||||
*self.const_to_pat(instance, cv, expr.hir_id, lit.span).kind
|
||||
},
|
||||
Err(e) => {
|
||||
self.errors.push(PatternError::ConstEval(ConstEvalErr {
|
||||
span: lit.span,
|
||||
kind: e,
|
||||
}));
|
||||
Err(()) => {
|
||||
self.errors.push(PatternError::FloatBug);
|
||||
PatternKind::Wild
|
||||
},
|
||||
}
|
||||
|
|
|
@ -783,7 +783,7 @@ fn is_unsafe_place<'a, 'gcx: 'tcx, 'tcx: 'a>(
|
|||
|
||||
match *place {
|
||||
Local(_) => false,
|
||||
Static(ref static_) => tcx.is_static_mut(static_.def_id),
|
||||
Static(ref static_) => tcx.is_static(static_.def_id) == Some(hir::Mutability::MutMutable),
|
||||
Projection(ref proj) => {
|
||||
match proj.elem {
|
||||
ProjectionElem::Field(..) |
|
||||
|
|
|
@ -524,7 +524,10 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
|
|||
};
|
||||
let count = match cx.tcx.at(c.span).const_eval(cx.param_env.and(global_id)) {
|
||||
Ok(cv) => cv.val.unwrap_usize(cx.tcx),
|
||||
Err(s) => cx.fatal_const_eval_err(&s, c.span, "expression")
|
||||
Err(e) => {
|
||||
e.report(cx.tcx, cx.tcx.def_span(def_id), "array length");
|
||||
ConstUsize::new(0, cx.tcx.sess.target.usize_ty).unwrap()
|
||||
},
|
||||
};
|
||||
|
||||
ExprKind::Repeat {
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
use hair::*;
|
||||
|
||||
use rustc::middle::const_val::{ConstEvalErr, ConstVal};
|
||||
use rustc::middle::const_val::ConstVal;
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
use rustc::hir::def_id::{DefId, LOCAL_CRATE};
|
||||
use rustc::hir::map::blocks::FnLikeNode;
|
||||
|
@ -238,17 +238,6 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
|
|||
p)
|
||||
}
|
||||
|
||||
pub fn fatal_const_eval_err(&mut self,
|
||||
err: &ConstEvalErr<'tcx>,
|
||||
primary_span: Span,
|
||||
primary_kind: &str)
|
||||
-> !
|
||||
{
|
||||
err.report(self.tcx, primary_span, primary_kind);
|
||||
self.tcx.sess.abort_if_errors();
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
pub fn trait_method(&mut self,
|
||||
trait_def_id: DefId,
|
||||
method_name: &str,
|
||||
|
|
|
@ -7,7 +7,7 @@ use rustc::mir::interpret::{PrimVal, EvalResult, MemoryPointer, PointerArithmeti
|
|||
use rustc_apfloat::ieee::{Single, Double};
|
||||
use rustc_apfloat::Float;
|
||||
|
||||
impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
pub(super) fn cast_primval(
|
||||
&self,
|
||||
val: PrimVal,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use rustc::hir;
|
||||
use rustc::middle::const_val::ErrKind::{CheckMatchError, TypeckError};
|
||||
use rustc::middle::const_val::{ConstEvalErr, ConstVal};
|
||||
use rustc::middle::const_val::ErrKind::{TypeckError, CheckMatchError};
|
||||
use rustc::mir;
|
||||
use rustc::ty::{self, TyCtxt, Ty, Instance};
|
||||
use rustc::ty::layout::{self, LayoutOf};
|
||||
|
@ -9,17 +9,37 @@ use rustc::ty::subst::Subst;
|
|||
use syntax::ast::Mutability;
|
||||
use syntax::codemap::Span;
|
||||
|
||||
use rustc::mir::interpret::{EvalResult, EvalError, EvalErrorKind, GlobalId, Value, MemoryPointer, Pointer, PrimVal};
|
||||
use super::{Place, EvalContext, StackPopCleanup, ValTy, PlaceExtra};
|
||||
use rustc::mir::interpret::{EvalResult, EvalError, EvalErrorKind, GlobalId, Value, MemoryPointer, Pointer, PrimVal, AllocId};
|
||||
use super::{Place, EvalContext, StackPopCleanup, ValTy, PlaceExtra, Memory};
|
||||
|
||||
use std::fmt;
|
||||
use std::error::Error;
|
||||
|
||||
pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>(
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
instance: Instance<'tcx>,
|
||||
mir: &'mir mir::Mir<'tcx>,
|
||||
) -> EvalResult<'tcx, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>> {
|
||||
debug!("mk_borrowck_eval_cx: {:?}", instance);
|
||||
let param_env = tcx.param_env(instance.def_id());
|
||||
let limits = super::ResourceLimits::default();
|
||||
let mut ecx = EvalContext::new(tcx, param_env, limits, CompileTimeEvaluator, ());
|
||||
// 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)
|
||||
}
|
||||
|
||||
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>> {
|
||||
) -> EvalResult<'tcx, EvalContext<'a, 'tcx, 'tcx, CompileTimeEvaluator>> {
|
||||
debug!("mk_eval_cx: {:?}, {:?}", instance, param_env);
|
||||
let limits = super::ResourceLimits::default();
|
||||
let mut ecx = EvalContext::new(tcx, param_env, limits, CompileTimeEvaluator, ());
|
||||
|
@ -35,64 +55,95 @@ pub fn mk_eval_cx<'a, 'tcx>(
|
|||
Ok(ecx)
|
||||
}
|
||||
|
||||
pub fn eval_body_with_mir<'a, 'mir, 'tcx>(
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
cid: GlobalId<'tcx>,
|
||||
mir: &'mir mir::Mir<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> Option<(Value, Pointer, Ty<'tcx>)> {
|
||||
let (res, ecx) = eval_body_and_ecx(tcx, cid, Some(mir), param_env);
|
||||
match res {
|
||||
Ok(val) => Some(val),
|
||||
Err(mut err) => {
|
||||
ecx.report(&mut err, true);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eval_body<'a, 'tcx>(
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
cid: GlobalId<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> EvalResult<'tcx, (Value, Pointer, Ty<'tcx>)> {
|
||||
eval_body_and_ecx(tcx, cid, param_env).0
|
||||
}
|
||||
|
||||
pub fn check_body<'a, 'tcx>(
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
cid: GlobalId<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) {
|
||||
let (res, ecx) = eval_body_and_ecx(tcx, cid, param_env);
|
||||
if let Err(mut err) = res {
|
||||
ecx.report(&mut err);
|
||||
) -> Option<(Value, Pointer, Ty<'tcx>)> {
|
||||
let (res, ecx) = eval_body_and_ecx(tcx, cid, None, param_env);
|
||||
match res {
|
||||
Ok(val) => Some(val),
|
||||
Err(mut err) => {
|
||||
ecx.report(&mut err, true);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_body_and_ecx<'a, 'tcx>(
|
||||
fn eval_body_and_ecx<'a, 'mir, 'tcx>(
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
cid: GlobalId<'tcx>,
|
||||
mir: Option<&'mir mir::Mir<'tcx>>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> (EvalResult<'tcx, (Value, Pointer, Ty<'tcx>)>, EvalContext<'a, 'tcx, CompileTimeEvaluator>) {
|
||||
) -> (EvalResult<'tcx, (Value, Pointer, Ty<'tcx>)>, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>) {
|
||||
debug!("eval_body: {:?}, {:?}", cid, param_env);
|
||||
let limits = super::ResourceLimits::default();
|
||||
let mut ecx = EvalContext::new(tcx, param_env, limits, CompileTimeEvaluator, ());
|
||||
let res = (|| {
|
||||
let mut mir = ecx.load_mir(cid.instance.def)?;
|
||||
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))?;
|
||||
if ecx.tcx.has_attr(cid.instance.def_id(), "linkage") {
|
||||
return Err(ConstEvalError::NotConst("extern global".to_string()).into());
|
||||
}
|
||||
if tcx.interpret_interner.borrow().get_cached(cid).is_none() {
|
||||
assert!(!layout.is_unsized());
|
||||
let ptr = ecx.memory.allocate(
|
||||
layout.size.bytes(),
|
||||
layout.align,
|
||||
None,
|
||||
)?;
|
||||
tcx.interpret_interner.borrow_mut().cache(cid, ptr.alloc_id);
|
||||
let cleanup = StackPopCleanup::MarkStatic(Mutability::Immutable);
|
||||
let name = ty::tls::with(|tcx| tcx.item_path_str(cid.instance.def_id()));
|
||||
trace!("const_eval: pushing stack frame for global: {}", name);
|
||||
ecx.push_stack_frame(
|
||||
cid.instance,
|
||||
mir.span,
|
||||
mir,
|
||||
Place::from_ptr(ptr, layout.align),
|
||||
cleanup.clone(),
|
||||
)?;
|
||||
let alloc = tcx.interpret_interner.borrow().get_cached(cid.instance.def_id());
|
||||
let alloc = match alloc {
|
||||
Some(alloc) => {
|
||||
assert!(cid.promoted.is_none());
|
||||
assert!(param_env.caller_bounds.is_empty());
|
||||
alloc
|
||||
},
|
||||
None => {
|
||||
assert!(!layout.is_unsized());
|
||||
let ptr = ecx.memory.allocate(
|
||||
layout.size.bytes(),
|
||||
layout.align,
|
||||
None,
|
||||
)?;
|
||||
if tcx.is_static(cid.instance.def_id()).is_some() {
|
||||
tcx.interpret_interner.borrow_mut().cache(cid.instance.def_id(), ptr.alloc_id);
|
||||
}
|
||||
let span = tcx.def_span(cid.instance.def_id());
|
||||
let internally_mutable = !layout.ty.is_freeze(tcx, param_env, span);
|
||||
let mutability = tcx.is_static(cid.instance.def_id());
|
||||
let mutability = if mutability == 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()));
|
||||
trace!("const_eval: pushing stack frame for global: {}", name);
|
||||
ecx.push_stack_frame(
|
||||
cid.instance,
|
||||
mir.span,
|
||||
mir,
|
||||
Place::from_ptr(ptr, layout.align),
|
||||
cleanup,
|
||||
)?;
|
||||
|
||||
while ecx.step()? {}
|
||||
}
|
||||
let alloc = tcx.interpret_interner.borrow().get_cached(cid).expect("global not cached");
|
||||
while ecx.step()? {}
|
||||
ptr.alloc_id
|
||||
}
|
||||
};
|
||||
let ptr = MemoryPointer::new(alloc, 0).into();
|
||||
let value = match ecx.try_read_value(ptr, layout.align, layout.ty)? {
|
||||
Some(val) => val,
|
||||
|
@ -103,18 +154,6 @@ fn eval_body_and_ecx<'a, 'tcx>(
|
|||
(res, ecx)
|
||||
}
|
||||
|
||||
pub fn eval_body_as_integer<'a, 'tcx>(
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
cid: GlobalId<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> EvalResult<'tcx, u128> {
|
||||
let (value, _, ty) = eval_body(tcx, cid, param_env)?;
|
||||
match value {
|
||||
Value::ByVal(prim) => prim.to_bytes(),
|
||||
_ => err!(TypeNotPrimitive(ty)),
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CompileTimeEvaluator;
|
||||
|
||||
impl<'tcx> Into<EvalError<'tcx>> for ConstEvalError {
|
||||
|
@ -159,11 +198,11 @@ impl Error for ConstEvalError {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> super::Machine<'tcx> for CompileTimeEvaluator {
|
||||
impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
|
||||
type MemoryData = ();
|
||||
type MemoryKinds = !;
|
||||
fn eval_fn_call<'a>(
|
||||
ecx: &mut EvalContext<'a, 'tcx, Self>,
|
||||
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
instance: ty::Instance<'tcx>,
|
||||
destination: Option<(Place, mir::BasicBlock)>,
|
||||
_args: &[ValTy<'tcx>],
|
||||
|
@ -204,7 +243,7 @@ impl<'tcx> super::Machine<'tcx> for CompileTimeEvaluator {
|
|||
|
||||
|
||||
fn call_intrinsic<'a>(
|
||||
ecx: &mut EvalContext<'a, 'tcx, Self>,
|
||||
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
instance: ty::Instance<'tcx>,
|
||||
_args: &[ValTy<'tcx>],
|
||||
dest: Place,
|
||||
|
@ -246,7 +285,7 @@ impl<'tcx> super::Machine<'tcx> for CompileTimeEvaluator {
|
|||
}
|
||||
|
||||
fn try_ptr_op<'a>(
|
||||
_ecx: &EvalContext<'a, 'tcx, Self>,
|
||||
_ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
_bin_op: mir::BinOp,
|
||||
left: PrimVal,
|
||||
_left_ty: Ty<'tcx>,
|
||||
|
@ -262,12 +301,16 @@ impl<'tcx> super::Machine<'tcx> for CompileTimeEvaluator {
|
|||
}
|
||||
}
|
||||
|
||||
fn mark_static_initialized(m: !) -> EvalResult<'tcx> {
|
||||
m
|
||||
fn mark_static_initialized<'a>(
|
||||
_mem: &mut Memory<'a, 'mir, 'tcx, Self>,
|
||||
_id: AllocId,
|
||||
_mutability: Mutability,
|
||||
) -> EvalResult<'tcx, bool> {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn box_alloc<'a>(
|
||||
_ecx: &mut EvalContext<'a, 'tcx, Self>,
|
||||
_ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
_ty: Ty<'tcx>,
|
||||
_dest: Place,
|
||||
) -> EvalResult<'tcx> {
|
||||
|
@ -277,7 +320,7 @@ impl<'tcx> super::Machine<'tcx> for CompileTimeEvaluator {
|
|||
}
|
||||
|
||||
fn global_item_with_linkage<'a>(
|
||||
_ecx: &mut EvalContext<'a, 'tcx, Self>,
|
||||
_ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
_instance: ty::Instance<'tcx>,
|
||||
_mutability: Mutability,
|
||||
) -> EvalResult<'tcx> {
|
||||
|
@ -374,14 +417,34 @@ pub fn const_eval_provider<'a, 'tcx>(
|
|||
let def_id = cid.instance.def.def_id();
|
||||
let span = tcx.def_span(def_id);
|
||||
|
||||
if tcx.is_foreign_item(def_id) {
|
||||
let id = tcx.interpret_interner.borrow().get_cached(def_id);
|
||||
let id = match id {
|
||||
// FIXME: due to caches this shouldn't happen, add some assertions
|
||||
Some(id) => id,
|
||||
None => {
|
||||
let id = tcx.interpret_interner.borrow_mut().reserve();
|
||||
tcx.interpret_interner.borrow_mut().cache(def_id, id);
|
||||
id
|
||||
},
|
||||
};
|
||||
let ty = tcx.type_of(def_id);
|
||||
let layout = (tcx, key.param_env).layout_of(ty).unwrap();
|
||||
let ptr = MemoryPointer::new(id, 0);
|
||||
return Ok(tcx.mk_const(ty::Const {
|
||||
val: ConstVal::Value(Value::ByRef(ptr.into(), layout.align)),
|
||||
ty,
|
||||
}))
|
||||
}
|
||||
|
||||
if let Some(id) = tcx.hir.as_local_node_id(def_id) {
|
||||
let tables = tcx.typeck_tables_of(def_id);
|
||||
|
||||
// Do match-check before building MIR
|
||||
if tcx.check_match(def_id).is_err() {
|
||||
return Err(ConstEvalErr {
|
||||
span,
|
||||
kind: CheckMatchError,
|
||||
span,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -392,22 +455,25 @@ pub fn const_eval_provider<'a, 'tcx>(
|
|||
// Do not continue into miri if typeck errors occurred; it will fail horribly
|
||||
if tables.tainted_by_errors {
|
||||
return Err(ConstEvalErr {
|
||||
kind: TypeckError,
|
||||
span,
|
||||
kind: TypeckError
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
match ::interpret::eval_body(tcx, cid, key.param_env) {
|
||||
Ok((miri_value, _, miri_ty)) => Ok(tcx.mk_const(ty::Const {
|
||||
let (res, ecx) = eval_body_and_ecx(tcx, cid, None, key.param_env);
|
||||
res.map(|(miri_value, _, miri_ty)| {
|
||||
tcx.mk_const(ty::Const {
|
||||
val: ConstVal::Value(miri_value),
|
||||
ty: miri_ty,
|
||||
})),
|
||||
Err(err) => {
|
||||
Err(ConstEvalErr {
|
||||
span,
|
||||
kind: err.into()
|
||||
})
|
||||
})
|
||||
}).map_err(|mut err| {
|
||||
if tcx.is_static(def_id).is_some() {
|
||||
ecx.report(&mut err, true);
|
||||
}
|
||||
}
|
||||
ConstEvalErr {
|
||||
kind: err.into(),
|
||||
span,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::fmt::Write;
|
|||
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::hir::map::definitions::DefPathData;
|
||||
use rustc::middle::const_val::ConstVal;
|
||||
use rustc::middle::const_val::{ConstVal, ErrKind};
|
||||
use rustc::mir;
|
||||
use rustc::traits::Reveal;
|
||||
use rustc::ty::layout::{self, Size, Align, HasDataLayout, LayoutOf, TyLayout};
|
||||
|
@ -21,7 +21,7 @@ use super::{Place, PlaceExtra, Memory,
|
|||
HasMemory, MemoryKind, operator,
|
||||
Machine};
|
||||
|
||||
pub struct EvalContext<'a, 'tcx: 'a, M: Machine<'tcx>> {
|
||||
pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
|
||||
/// Stores the `Machine` instance.
|
||||
pub machine: M,
|
||||
|
||||
|
@ -32,10 +32,10 @@ pub struct EvalContext<'a, 'tcx: 'a, M: Machine<'tcx>> {
|
|||
pub param_env: ty::ParamEnv<'tcx>,
|
||||
|
||||
/// The virtual memory system.
|
||||
pub memory: Memory<'a, 'tcx, M>,
|
||||
pub memory: Memory<'a, 'mir, 'tcx, M>,
|
||||
|
||||
/// The virtual call stack.
|
||||
pub(crate) stack: Vec<Frame<'tcx>>,
|
||||
pub(crate) stack: Vec<Frame<'mir, 'tcx>>,
|
||||
|
||||
/// The maximum number of stack frames allowed
|
||||
pub(crate) stack_limit: usize,
|
||||
|
@ -47,12 +47,12 @@ pub struct EvalContext<'a, 'tcx: 'a, M: Machine<'tcx>> {
|
|||
}
|
||||
|
||||
/// A stack frame.
|
||||
pub struct Frame<'tcx> {
|
||||
pub struct Frame<'mir, 'tcx: 'mir> {
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Function and callsite information
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// The MIR for the function called on this frame.
|
||||
pub mir: &'tcx mir::Mir<'tcx>,
|
||||
pub mir: &'mir mir::Mir<'tcx>,
|
||||
|
||||
/// The def_id and substs of the current function
|
||||
pub instance: ty::Instance<'tcx>,
|
||||
|
@ -131,6 +131,15 @@ pub struct ValTy<'tcx> {
|
|||
pub ty: Ty<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> ValTy<'tcx> {
|
||||
pub fn from(val: &ty::Const<'tcx>) -> Option<Self> {
|
||||
match val.val {
|
||||
ConstVal::Value(value) => Some(ValTy { value, ty: val.ty }),
|
||||
ConstVal::Unevaluated { .. } => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ::std::ops::Deref for ValTy<'tcx> {
|
||||
type Target = Value;
|
||||
fn deref(&self) -> &Value {
|
||||
|
@ -138,37 +147,37 @@ impl<'tcx> ::std::ops::Deref for ValTy<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, M: Machine<'tcx>> HasDataLayout for &'a EvalContext<'a, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for &'a EvalContext<'a, 'mir, 'tcx, M> {
|
||||
#[inline]
|
||||
fn data_layout(&self) -> &layout::TargetDataLayout {
|
||||
&self.tcx.data_layout
|
||||
}
|
||||
}
|
||||
|
||||
impl<'c, 'b, 'a, 'tcx, M: Machine<'tcx>> HasDataLayout
|
||||
for &'c &'b mut EvalContext<'a, 'tcx, M> {
|
||||
impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout
|
||||
for &'c &'b mut EvalContext<'a, 'mir, 'tcx, M> {
|
||||
#[inline]
|
||||
fn data_layout(&self) -> &layout::TargetDataLayout {
|
||||
&self.tcx.data_layout
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, M: Machine<'tcx>> layout::HasTyCtxt<'tcx> for &'a EvalContext<'a, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> layout::HasTyCtxt<'tcx> for &'a EvalContext<'a, 'mir, 'tcx, M> {
|
||||
#[inline]
|
||||
fn tcx<'b>(&'b self) -> TyCtxt<'b, 'tcx, 'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
}
|
||||
|
||||
impl<'c, 'b, 'a, 'tcx, M: Machine<'tcx>> layout::HasTyCtxt<'tcx>
|
||||
for &'c &'b mut EvalContext<'a, 'tcx, M> {
|
||||
impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> layout::HasTyCtxt<'tcx>
|
||||
for &'c &'b mut EvalContext<'a, 'mir, 'tcx, M> {
|
||||
#[inline]
|
||||
fn tcx<'d>(&'d self) -> TyCtxt<'d, 'tcx, 'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, M: Machine<'tcx>> LayoutOf<Ty<'tcx>> for &'a EvalContext<'a, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> LayoutOf<Ty<'tcx>> for &'a EvalContext<'a, 'mir, 'tcx, M> {
|
||||
type TyLayout = EvalResult<'tcx, TyLayout<'tcx>>;
|
||||
|
||||
fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout {
|
||||
|
@ -177,8 +186,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> LayoutOf<Ty<'tcx>> for &'a EvalContext<'a, 'tcx
|
|||
}
|
||||
}
|
||||
|
||||
impl<'c, 'b, 'a, 'tcx, M: Machine<'tcx>> LayoutOf<Ty<'tcx>>
|
||||
for &'c &'b mut EvalContext<'a, 'tcx, M> {
|
||||
impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> LayoutOf<Ty<'tcx>>
|
||||
for &'c &'b mut EvalContext<'a, 'mir, 'tcx, M> {
|
||||
type TyLayout = EvalResult<'tcx, TyLayout<'tcx>>;
|
||||
|
||||
#[inline]
|
||||
|
@ -187,7 +196,7 @@ impl<'c, 'b, 'a, 'tcx, M: Machine<'tcx>> LayoutOf<Ty<'tcx>>
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
pub fn new(
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
|
@ -214,15 +223,15 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
|||
self.memory.allocate(size, layout.align, Some(MemoryKind::Stack))
|
||||
}
|
||||
|
||||
pub fn memory(&self) -> &Memory<'a, 'tcx, M> {
|
||||
pub fn memory(&self) -> &Memory<'a, 'mir, 'tcx, M> {
|
||||
&self.memory
|
||||
}
|
||||
|
||||
pub fn memory_mut(&mut self) -> &mut Memory<'a, 'tcx, M> {
|
||||
pub fn memory_mut(&mut self) -> &mut Memory<'a, 'mir, 'tcx, M> {
|
||||
&mut self.memory
|
||||
}
|
||||
|
||||
pub fn stack(&self) -> &[Frame<'tcx>] {
|
||||
pub fn stack(&self) -> &[Frame<'mir, 'tcx>] {
|
||||
&self.stack
|
||||
}
|
||||
|
||||
|
@ -240,14 +249,14 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
|||
))
|
||||
}
|
||||
|
||||
pub(super) fn const_to_value(&mut self, const_val: &ConstVal<'tcx>, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> {
|
||||
pub(super) fn const_to_value(&self, const_val: &ConstVal<'tcx>, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> {
|
||||
match *const_val {
|
||||
ConstVal::Unevaluated(def_id, substs) => {
|
||||
let instance = self.resolve(def_id, substs)?;
|
||||
Ok(self.read_global_as_value(GlobalId {
|
||||
self.read_global_as_value(GlobalId {
|
||||
instance,
|
||||
promoted: None,
|
||||
}, self.layout_of(ty)?))
|
||||
}, ty)
|
||||
}
|
||||
ConstVal::Value(val) => Ok(val),
|
||||
}
|
||||
|
@ -380,14 +389,14 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
|||
&mut self,
|
||||
instance: ty::Instance<'tcx>,
|
||||
span: codemap::Span,
|
||||
mir: &'tcx mir::Mir<'tcx>,
|
||||
mir: &'mir mir::Mir<'tcx>,
|
||||
return_place: Place,
|
||||
return_to_block: StackPopCleanup,
|
||||
) -> EvalResult<'tcx> {
|
||||
::log_settings::settings().indentation += 1;
|
||||
|
||||
/// Return the set of locals that have a storage annotation anywhere
|
||||
fn collect_storage_annotations<'tcx>(mir: &'tcx mir::Mir<'tcx>) -> HashSet<mir::Local> {
|
||||
fn collect_storage_annotations<'mir, 'tcx>(mir: &'mir mir::Mir<'tcx>) -> HashSet<mir::Local> {
|
||||
use rustc::mir::StatementKind::*;
|
||||
|
||||
let mut set = HashSet::new();
|
||||
|
@ -819,7 +828,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
|||
self.read_global_as_value(GlobalId {
|
||||
instance: self.frame().instance,
|
||||
promoted: Some(index),
|
||||
}, self.layout_of(ty)?)
|
||||
}, ty)?
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -931,9 +940,28 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_global_as_value(&self, gid: GlobalId, layout: TyLayout) -> Value {
|
||||
let alloc = self.tcx.interpret_interner.borrow().get_cached(gid).expect("global not cached");
|
||||
Value::ByRef(MemoryPointer::new(alloc, 0).into(), layout.align)
|
||||
pub fn read_global_as_value(&self, gid: GlobalId<'tcx>, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> {
|
||||
if gid.promoted.is_none() {
|
||||
let cached = self
|
||||
.tcx
|
||||
.interpret_interner
|
||||
.borrow()
|
||||
.get_cached(gid.instance.def_id());
|
||||
if let Some(alloc_id) = cached {
|
||||
let layout = self.layout_of(ty)?;
|
||||
let ptr = MemoryPointer::new(alloc_id, 0);
|
||||
return Ok(Value::ByRef(ptr.into(), layout.align))
|
||||
}
|
||||
}
|
||||
let cv = match self.tcx.const_eval(self.param_env.and(gid)) {
|
||||
Ok(val) => val,
|
||||
Err(err) => match err.kind {
|
||||
ErrKind::Miri(miri) => return Err(miri),
|
||||
ErrKind::TypeckError => return err!(TypeckError),
|
||||
other => bug!("const eval returned {:?}", other),
|
||||
},
|
||||
};
|
||||
self.const_to_value(&cv.val, ty)
|
||||
}
|
||||
|
||||
pub fn force_allocation(&mut self, place: Place) -> EvalResult<'tcx, Place> {
|
||||
|
@ -1326,15 +1354,15 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
|||
Ok(Some(Value::ByVal(val)))
|
||||
}
|
||||
|
||||
pub fn frame(&self) -> &Frame<'tcx> {
|
||||
pub fn frame(&self) -> &Frame<'mir, 'tcx> {
|
||||
self.stack.last().expect("no call frames exist")
|
||||
}
|
||||
|
||||
pub fn frame_mut(&mut self) -> &mut Frame<'tcx> {
|
||||
pub fn frame_mut(&mut self) -> &mut Frame<'mir, 'tcx> {
|
||||
self.stack.last_mut().expect("no call frames exist")
|
||||
}
|
||||
|
||||
pub(super) fn mir(&self) -> &'tcx mir::Mir<'tcx> {
|
||||
pub(super) fn mir(&self) -> &'mir mir::Mir<'tcx> {
|
||||
self.frame().mir
|
||||
}
|
||||
|
||||
|
@ -1544,7 +1572,10 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn report(&self, e: &mut EvalError) {
|
||||
pub fn report(&self, e: &mut EvalError, as_err: bool) {
|
||||
if let EvalErrorKind::TypeckError = e.kind {
|
||||
return;
|
||||
}
|
||||
if let Some(ref mut backtrace) = e.backtrace {
|
||||
let mut trace_text = "\n\nAn error occurred in miri:\n".to_string();
|
||||
backtrace.resolve();
|
||||
|
@ -1582,8 +1613,34 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
|||
} else {
|
||||
block.terminator().source_info.span
|
||||
};
|
||||
let mut err = self.tcx.sess.struct_span_err(span, &e.to_string());
|
||||
let node_id = self
|
||||
.stack()
|
||||
.iter()
|
||||
.rev()
|
||||
.filter_map(|frame| self.tcx.hir.as_local_node_id(frame.instance.def_id()))
|
||||
.next()
|
||||
.expect("some part of a failing const eval must be local");
|
||||
let mut err = if as_err {
|
||||
::rustc::middle::const_val::struct_error(self.tcx, span, "constant evaluation error")
|
||||
} else {
|
||||
self.tcx.struct_span_lint_node(
|
||||
::rustc::lint::builtin::CONST_ERR,
|
||||
node_id,
|
||||
span,
|
||||
"constant evaluation error",
|
||||
)
|
||||
};
|
||||
err.span_label(span, e.to_string());
|
||||
let mut last_span = None;
|
||||
for &Frame { instance, span, .. } in self.stack().iter().rev() {
|
||||
// make sure we don't emit frames that are duplicates of the previous
|
||||
if let Some(last) = last_span {
|
||||
if last == span {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
last_span = Some(span);
|
||||
}
|
||||
if self.tcx.def_key(instance.def_id()).disambiguated_data.data ==
|
||||
DefPathData::ClosureExpr
|
||||
{
|
||||
|
@ -1599,7 +1656,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Frame<'tcx> {
|
||||
impl<'mir, 'tcx> Frame<'mir, 'tcx> {
|
||||
pub fn get_local(&self, local: mir::Local) -> EvalResult<'tcx, Value> {
|
||||
// Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0.
|
||||
self.locals[local.index() - 1].ok_or(EvalErrorKind::DeadLocal.into())
|
||||
|
|
|
@ -12,7 +12,7 @@ use syntax::ast::Mutability;
|
|||
|
||||
/// Methods of this trait signifies a point where CTFE evaluation would fail
|
||||
/// and some use case dependent behaviour can instead be applied
|
||||
pub trait Machine<'tcx>: Sized {
|
||||
pub trait Machine<'mir, 'tcx>: Sized {
|
||||
/// Additional data that can be accessed via the Memory
|
||||
type MemoryData;
|
||||
|
||||
|
@ -26,7 +26,7 @@ pub trait Machine<'tcx>: Sized {
|
|||
///
|
||||
/// Returns Ok(false) if a new stack frame was pushed
|
||||
fn eval_fn_call<'a>(
|
||||
ecx: &mut EvalContext<'a, 'tcx, Self>,
|
||||
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
instance: ty::Instance<'tcx>,
|
||||
destination: Option<(Place, mir::BasicBlock)>,
|
||||
args: &[ValTy<'tcx>],
|
||||
|
@ -36,7 +36,7 @@ pub trait Machine<'tcx>: Sized {
|
|||
|
||||
/// directly process an intrinsic without pushing a stack frame.
|
||||
fn call_intrinsic<'a>(
|
||||
ecx: &mut EvalContext<'a, 'tcx, Self>,
|
||||
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
instance: ty::Instance<'tcx>,
|
||||
args: &[ValTy<'tcx>],
|
||||
dest: Place,
|
||||
|
@ -51,7 +51,7 @@ pub trait Machine<'tcx>: Sized {
|
|||
///
|
||||
/// Returns a (value, overflowed) pair if the operation succeeded
|
||||
fn try_ptr_op<'a>(
|
||||
ecx: &EvalContext<'a, 'tcx, Self>,
|
||||
ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
bin_op: mir::BinOp,
|
||||
left: PrimVal,
|
||||
left_ty: Ty<'tcx>,
|
||||
|
@ -60,26 +60,30 @@ pub trait Machine<'tcx>: Sized {
|
|||
) -> EvalResult<'tcx, Option<(PrimVal, bool)>>;
|
||||
|
||||
/// Called when trying to mark machine defined `MemoryKinds` as static
|
||||
fn mark_static_initialized(m: Self::MemoryKinds) -> EvalResult<'tcx>;
|
||||
fn mark_static_initialized<'a>(
|
||||
_mem: &mut Memory<'a, 'mir, 'tcx, Self>,
|
||||
_id: AllocId,
|
||||
_mutability: Mutability,
|
||||
) -> EvalResult<'tcx, bool>;
|
||||
|
||||
/// Heap allocations via the `box` keyword
|
||||
///
|
||||
/// Returns a pointer to the allocated memory
|
||||
fn box_alloc<'a>(
|
||||
ecx: &mut EvalContext<'a, 'tcx, Self>,
|
||||
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
ty: Ty<'tcx>,
|
||||
dest: Place,
|
||||
) -> EvalResult<'tcx>;
|
||||
|
||||
/// Called when trying to access a global declared with a `linkage` attribute
|
||||
fn global_item_with_linkage<'a>(
|
||||
ecx: &mut EvalContext<'a, 'tcx, Self>,
|
||||
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
instance: ty::Instance<'tcx>,
|
||||
mutability: Mutability,
|
||||
) -> EvalResult<'tcx>;
|
||||
|
||||
fn check_locks<'a>(
|
||||
_mem: &Memory<'a, 'tcx, Self>,
|
||||
_mem: &Memory<'a, 'mir, 'tcx, Self>,
|
||||
_ptr: MemoryPointer,
|
||||
_size: u64,
|
||||
_access: AccessKind,
|
||||
|
@ -88,12 +92,12 @@ pub trait Machine<'tcx>: Sized {
|
|||
}
|
||||
|
||||
fn add_lock<'a>(
|
||||
_mem: &mut Memory<'a, 'tcx, Self>,
|
||||
_mem: &mut Memory<'a, 'mir, 'tcx, Self>,
|
||||
_id: AllocId,
|
||||
) {}
|
||||
|
||||
fn free_lock<'a>(
|
||||
_mem: &mut Memory<'a, 'tcx, Self>,
|
||||
_mem: &mut Memory<'a, 'mir, 'tcx, Self>,
|
||||
_id: AllocId,
|
||||
_len: u64,
|
||||
) -> EvalResult<'tcx> {
|
||||
|
@ -101,14 +105,14 @@ pub trait Machine<'tcx>: Sized {
|
|||
}
|
||||
|
||||
fn end_region<'a>(
|
||||
_ecx: &mut EvalContext<'a, 'tcx, Self>,
|
||||
_ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
_reg: Option<::rustc::middle::region::Scope>,
|
||||
) -> EvalResult<'tcx> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validation_op<'a>(
|
||||
_ecx: &mut EvalContext<'a, 'tcx, Self>,
|
||||
_ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
_op: ::rustc::mir::ValidationOp,
|
||||
_operand: &::rustc::mir::ValidationOperand<'tcx, ::rustc::mir::Place<'tcx>>,
|
||||
) -> EvalResult<'tcx> {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian, BigEndian};
|
||||
use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque};
|
||||
use std::{ptr, mem, io};
|
||||
use std::{ptr, io};
|
||||
|
||||
use rustc::ty::{Instance, TyCtxt};
|
||||
use rustc::ty::layout::{self, Align, TargetDataLayout};
|
||||
|
@ -19,8 +19,6 @@ use super::{EvalContext, Machine};
|
|||
pub enum MemoryKind<T> {
|
||||
/// Error if deallocated except during a stack pop
|
||||
Stack,
|
||||
/// A mutable Static. All the others are interned in the tcx
|
||||
MutableStatic, // FIXME: move me into the machine, rustc const eval doesn't need them
|
||||
/// Additional memory kinds a machine wishes to distinguish from the builtin ones
|
||||
Machine(T),
|
||||
}
|
||||
|
@ -29,7 +27,7 @@ pub enum MemoryKind<T> {
|
|||
// Top-level interpreter memory
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub struct Memory<'a, 'tcx: 'a, M: Machine<'tcx>> {
|
||||
pub struct Memory<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
|
||||
/// Additional data required by the Machine
|
||||
pub data: M::MemoryData,
|
||||
|
||||
|
@ -56,7 +54,7 @@ pub struct Memory<'a, 'tcx: 'a, M: Machine<'tcx>> {
|
|||
pub tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
||||
pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, max_memory: u64, data: M::MemoryData) -> Self {
|
||||
Memory {
|
||||
data,
|
||||
|
@ -107,6 +105,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
|||
relocations: BTreeMap::new(),
|
||||
undef_mask: UndefMask::new(size),
|
||||
align,
|
||||
mutable: false,
|
||||
};
|
||||
let id = self.tcx.interpret_interner.borrow_mut().reserve();
|
||||
M::add_lock(self, id);
|
||||
|
@ -119,7 +118,6 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
|||
None => {
|
||||
self.uninitialized_statics.insert(id, alloc);
|
||||
},
|
||||
Some(MemoryKind::MutableStatic) => bug!("don't allocate mutable statics directly")
|
||||
}
|
||||
Ok(MemoryPointer::new(id, 0))
|
||||
}
|
||||
|
@ -164,10 +162,6 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
|||
|
||||
pub fn deallocate_local(&mut self, ptr: MemoryPointer) -> EvalResult<'tcx> {
|
||||
match self.alloc_kind.get(&ptr.alloc_id).cloned() {
|
||||
// for a constant like `const FOO: &i32 = &1;` the local containing
|
||||
// the `1` is referred to by the global. We transitively marked everything
|
||||
// the global refers to as static itself, so we don't free it here
|
||||
Some(MemoryKind::MutableStatic) => Ok(()),
|
||||
Some(MemoryKind::Stack) => self.deallocate(ptr, None, MemoryKind::Stack),
|
||||
// Happens if the memory was interned into immutable memory
|
||||
None => Ok(()),
|
||||
|
@ -292,7 +286,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
|||
}
|
||||
|
||||
/// Allocation accessors
|
||||
impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
||||
pub fn get(&self, id: AllocId) -> EvalResult<'tcx, &Allocation> {
|
||||
// normal alloc?
|
||||
match self.alloc_map.get(&id) {
|
||||
|
@ -376,7 +370,6 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
|||
Some(a) => (a, match self.alloc_kind[&id] {
|
||||
MemoryKind::Stack => " (stack)".to_owned(),
|
||||
MemoryKind::Machine(m) => format!(" ({:?})", m),
|
||||
MemoryKind::MutableStatic => " (static mut)".to_owned(),
|
||||
}),
|
||||
// uninitialized static alloc?
|
||||
None => match self.uninitialized_statics.get(&id) {
|
||||
|
@ -388,15 +381,15 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
|||
Some(a) => (a, "(immutable)".to_owned()),
|
||||
None => if let Some(func) = int.get_fn(id) {
|
||||
trace!("{} {}", msg, func);
|
||||
continue;
|
||||
continue;
|
||||
} else {
|
||||
trace!("{} (deallocated)", msg);
|
||||
continue;
|
||||
trace!("{} (deallocated)", msg);
|
||||
continue;
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
for i in 0..(alloc.bytes.len() as u64) {
|
||||
if let Some(&target_id) = alloc.relocations.get(&i) {
|
||||
|
@ -441,14 +434,9 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
|||
|
||||
pub fn leak_report(&self) -> usize {
|
||||
trace!("### LEAK REPORT ###");
|
||||
let kinds = &self.alloc_kind;
|
||||
let leaks: Vec<_> = self.alloc_map
|
||||
.keys()
|
||||
.filter_map(|key| if kinds[key] != MemoryKind::MutableStatic {
|
||||
Some(*key)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.cloned()
|
||||
.collect();
|
||||
let n = leaks.len();
|
||||
self.dump_allocs(leaks);
|
||||
|
@ -457,7 +445,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
|||
}
|
||||
|
||||
/// Byte accessors
|
||||
impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
||||
fn get_bytes_unchecked(
|
||||
&self,
|
||||
ptr: MemoryPointer,
|
||||
|
@ -521,7 +509,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
|||
}
|
||||
|
||||
/// Reading and writing
|
||||
impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
||||
/// mark an allocation pointed to by a static as static and initialized
|
||||
fn mark_inner_allocation_initialized(
|
||||
&mut self,
|
||||
|
@ -529,10 +517,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
|||
mutability: Mutability,
|
||||
) -> EvalResult<'tcx> {
|
||||
match self.alloc_kind.get(&alloc) {
|
||||
// do not go into immutable statics
|
||||
None |
|
||||
// or mutable statics
|
||||
Some(&MemoryKind::MutableStatic) => Ok(()),
|
||||
// do not go into statics
|
||||
None => Ok(()),
|
||||
// just locals and machine allocs
|
||||
Some(_) => self.mark_static_initalized(alloc, mutability),
|
||||
}
|
||||
|
@ -549,60 +535,27 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
|||
alloc_id,
|
||||
mutability
|
||||
);
|
||||
if mutability == Mutability::Immutable {
|
||||
let alloc = self.alloc_map.remove(&alloc_id);
|
||||
let kind = self.alloc_kind.remove(&alloc_id);
|
||||
assert_ne!(kind, Some(MemoryKind::MutableStatic));
|
||||
let uninit = self.uninitialized_statics.remove(&alloc_id);
|
||||
if let Some(alloc) = alloc.or(uninit) {
|
||||
let alloc = self.tcx.intern_const_alloc(alloc);
|
||||
self.tcx.interpret_interner.borrow_mut().intern_at_reserved(alloc_id, alloc);
|
||||
// recurse into inner allocations
|
||||
for &alloc in alloc.relocations.values() {
|
||||
self.mark_inner_allocation_initialized(alloc, mutability)?;
|
||||
}
|
||||
// The machine handled it
|
||||
if M::mark_static_initialized(self, alloc_id, mutability)? {
|
||||
return Ok(())
|
||||
}
|
||||
let alloc = self.alloc_map.remove(&alloc_id);
|
||||
match self.alloc_kind.remove(&alloc_id) {
|
||||
None => {},
|
||||
Some(MemoryKind::Machine(_)) => bug!("machine didn't handle machine alloc"),
|
||||
Some(MemoryKind::Stack) => {},
|
||||
}
|
||||
let uninit = self.uninitialized_statics.remove(&alloc_id);
|
||||
if let Some(mut alloc) = alloc.or(uninit) {
|
||||
// ensure llvm knows not to put this into immutable memroy
|
||||
alloc.mutable = mutability == Mutability::Mutable;
|
||||
let alloc = self.tcx.intern_const_alloc(alloc);
|
||||
self.tcx.interpret_interner.borrow_mut().intern_at_reserved(alloc_id, alloc);
|
||||
// recurse into inner allocations
|
||||
for &alloc in alloc.relocations.values() {
|
||||
self.mark_inner_allocation_initialized(alloc, mutability)?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
// We are marking the static as initialized, so move it out of the uninit map
|
||||
if let Some(uninit) = self.uninitialized_statics.remove(&alloc_id) {
|
||||
self.alloc_map.insert(alloc_id, uninit);
|
||||
}
|
||||
// do not use `self.get_mut(alloc_id)` here, because we might have already marked a
|
||||
// sub-element or have circular pointers (e.g. `Rc`-cycles)
|
||||
let relocations = match self.alloc_map.get_mut(&alloc_id) {
|
||||
Some(&mut Allocation {
|
||||
ref mut relocations,
|
||||
..
|
||||
}) => {
|
||||
match self.alloc_kind.get(&alloc_id) {
|
||||
// const eval results can refer to "locals".
|
||||
// E.g. `const Foo: &u32 = &1;` refers to the temp local that stores the `1`
|
||||
None |
|
||||
Some(&MemoryKind::Stack) => {},
|
||||
Some(&MemoryKind::Machine(m)) => M::mark_static_initialized(m)?,
|
||||
Some(&MemoryKind::MutableStatic) => {
|
||||
trace!("mark_static_initalized: skipping already initialized static referred to by static currently being initialized");
|
||||
return Ok(());
|
||||
},
|
||||
}
|
||||
// overwrite or insert
|
||||
self.alloc_kind.insert(alloc_id, MemoryKind::MutableStatic);
|
||||
// take out the relocations vector to free the borrow on self, so we can call
|
||||
// mark recursively
|
||||
mem::replace(relocations, Default::default())
|
||||
}
|
||||
None => return err!(DanglingPointerDeref),
|
||||
};
|
||||
// recurse into inner allocations
|
||||
for &alloc in relocations.values() {
|
||||
self.mark_inner_allocation_initialized(alloc, mutability)?;
|
||||
}
|
||||
// put back the relocations
|
||||
self.alloc_map
|
||||
.get_mut(&alloc_id)
|
||||
.expect("checked above")
|
||||
.relocations = relocations;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -829,7 +782,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
|||
}
|
||||
|
||||
/// Relocations
|
||||
impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
||||
fn relocations(
|
||||
&self,
|
||||
ptr: MemoryPointer,
|
||||
|
@ -883,7 +836,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
|||
}
|
||||
|
||||
/// Undefined bytes
|
||||
impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
||||
// FIXME(solson): This is a very naive, slow version.
|
||||
fn copy_undef_mask(
|
||||
&mut self,
|
||||
|
@ -944,7 +897,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
|||
// Methods to access integers in the target endianness
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
fn write_target_uint(
|
||||
pub fn write_target_uint(
|
||||
endianness: layout::Endian,
|
||||
mut target: &mut [u8],
|
||||
data: u128,
|
||||
|
@ -955,7 +908,8 @@ fn write_target_uint(
|
|||
layout::Endian::Big => target.write_uint128::<BigEndian>(data, len),
|
||||
}
|
||||
}
|
||||
fn write_target_int(
|
||||
|
||||
pub fn write_target_int(
|
||||
endianness: layout::Endian,
|
||||
mut target: &mut [u8],
|
||||
data: i128,
|
||||
|
@ -967,14 +921,14 @@ fn write_target_int(
|
|||
}
|
||||
}
|
||||
|
||||
fn read_target_uint(endianness: layout::Endian, mut source: &[u8]) -> Result<u128, io::Error> {
|
||||
pub fn read_target_uint(endianness: layout::Endian, mut source: &[u8]) -> Result<u128, io::Error> {
|
||||
match endianness {
|
||||
layout::Endian::Little => source.read_uint128::<LittleEndian>(source.len()),
|
||||
layout::Endian::Big => source.read_uint128::<BigEndian>(source.len()),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_target_int(endianness: layout::Endian, mut source: &[u8]) -> Result<i128, io::Error> {
|
||||
pub fn read_target_int(endianness: layout::Endian, mut source: &[u8]) -> Result<i128, io::Error> {
|
||||
match endianness {
|
||||
layout::Endian::Little => source.read_int128::<LittleEndian>(source.len()),
|
||||
layout::Endian::Big => source.read_int128::<BigEndian>(source.len()),
|
||||
|
@ -985,9 +939,9 @@ fn read_target_int(endianness: layout::Endian, mut source: &[u8]) -> Result<i128
|
|||
// Unaligned accesses
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub trait HasMemory<'a, 'tcx: 'a, M: Machine<'tcx>> {
|
||||
fn memory_mut(&mut self) -> &mut Memory<'a, 'tcx, M>;
|
||||
fn memory(&self) -> &Memory<'a, 'tcx, M>;
|
||||
pub trait HasMemory<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
|
||||
fn memory_mut(&mut self) -> &mut Memory<'a, 'mir, 'tcx, M>;
|
||||
fn memory(&self) -> &Memory<'a, 'mir, 'tcx, M>;
|
||||
|
||||
/// Convert the value into a pointer (or a pointer-sized integer). If the value is a ByRef,
|
||||
/// this may have to perform a load.
|
||||
|
@ -1051,31 +1005,31 @@ pub trait HasMemory<'a, 'tcx: 'a, M: Machine<'tcx>> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, M: Machine<'tcx>> HasMemory<'a, 'tcx, M> for Memory<'a, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasMemory<'a, 'mir, 'tcx, M> for Memory<'a, 'mir, 'tcx, M> {
|
||||
#[inline]
|
||||
fn memory_mut(&mut self) -> &mut Memory<'a, 'tcx, M> {
|
||||
fn memory_mut(&mut self) -> &mut Memory<'a, 'mir, 'tcx, M> {
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn memory(&self) -> &Memory<'a, 'tcx, M> {
|
||||
fn memory(&self) -> &Memory<'a, 'mir, 'tcx, M> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, M: Machine<'tcx>> HasMemory<'a, 'tcx, M> for EvalContext<'a, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasMemory<'a, 'mir, 'tcx, M> for EvalContext<'a, 'mir, 'tcx, M> {
|
||||
#[inline]
|
||||
fn memory_mut(&mut self) -> &mut Memory<'a, 'tcx, M> {
|
||||
fn memory_mut(&mut self) -> &mut Memory<'a, 'mir, 'tcx, M> {
|
||||
&mut self.memory
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn memory(&self) -> &Memory<'a, 'tcx, M> {
|
||||
fn memory(&self) -> &Memory<'a, 'mir, 'tcx, M> {
|
||||
&self.memory
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, M: Machine<'tcx>> layout::HasDataLayout for &'a Memory<'a, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> layout::HasDataLayout for &'a Memory<'a, 'mir, 'tcx, M> {
|
||||
#[inline]
|
||||
fn data_layout(&self) -> &TargetDataLayout {
|
||||
&self.tcx.data_layout
|
||||
|
|
|
@ -18,6 +18,17 @@ pub use self::place::{Place, PlaceExtra};
|
|||
|
||||
pub use self::memory::{Memory, MemoryKind, HasMemory};
|
||||
|
||||
pub use self::const_eval::{eval_body_as_integer, eval_body, CompileTimeEvaluator, const_eval_provider, const_val_field, const_discr, check_body};
|
||||
pub use self::const_eval::{
|
||||
eval_body_with_mir,
|
||||
mk_borrowck_eval_cx,
|
||||
eval_body,
|
||||
CompileTimeEvaluator,
|
||||
const_eval_provider,
|
||||
const_val_field,
|
||||
const_discr,
|
||||
};
|
||||
|
||||
pub use self::machine::Machine;
|
||||
|
||||
pub use self::operator::unary_op;
|
||||
pub use self::memory::{write_target_uint, write_target_int, read_target_uint, read_target_int};
|
||||
|
|
|
@ -8,7 +8,7 @@ use super::{EvalContext, Place, Machine, ValTy};
|
|||
|
||||
use rustc::mir::interpret::{EvalResult, PrimVal, PrimValKind, Value, bytes_to_f32, bytes_to_f64};
|
||||
|
||||
impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
fn binop_with_overflow(
|
||||
&mut self,
|
||||
op: mir::BinOp,
|
||||
|
@ -56,6 +56,24 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
|||
}
|
||||
|
||||
macro_rules! overflow {
|
||||
(overflowing_div, $l:expr, $r:expr) => ({
|
||||
let (val, overflowed) = if $r == 0 {
|
||||
($l, true)
|
||||
} else {
|
||||
$l.overflowing_div($r)
|
||||
};
|
||||
let primval = PrimVal::Bytes(val as u128);
|
||||
Ok((primval, overflowed))
|
||||
});
|
||||
(overflowing_rem, $l:expr, $r:expr) => ({
|
||||
let (val, overflowed) = if $r == 0 {
|
||||
($l, true)
|
||||
} else {
|
||||
$l.overflowing_rem($r)
|
||||
};
|
||||
let primval = PrimVal::Bytes(val as u128);
|
||||
Ok((primval, overflowed))
|
||||
});
|
||||
($op:ident, $l:expr, $r:expr) => ({
|
||||
let (val, overflowed) = $l.$op($r);
|
||||
let primval = PrimVal::Bytes(val as u128);
|
||||
|
@ -105,7 +123,7 @@ macro_rules! int_shift {
|
|||
})
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
/// Returns the result of the specified operation and whether it overflowed.
|
||||
pub fn binary_op(
|
||||
&self,
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
use rustc::mir;
|
||||
use rustc::ty::{self, Ty};
|
||||
use rustc::ty::layout::{self, Align, LayoutOf, TyLayout};
|
||||
use rustc::traits;
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
|
||||
use rustc::mir::interpret::{GlobalId, Value, PrimVal, EvalResult, Pointer, MemoryPointer};
|
||||
use super::{EvalContext, Machine, ValTy};
|
||||
use interpret::memory::HasMemory;
|
||||
use rustc::middle::const_val::ErrKind;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Place {
|
||||
|
@ -90,7 +92,7 @@ impl<'tcx> Place {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
/// Reads a value from the place without going through the intermediate step of obtaining
|
||||
/// a `miri::Place`
|
||||
pub fn try_read_place(
|
||||
|
@ -106,10 +108,10 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
|||
// Directly reading a static will always succeed
|
||||
Static(ref static_) => {
|
||||
let instance = ty::Instance::mono(self.tcx, static_.def_id);
|
||||
Ok(Some(self.read_global_as_value(GlobalId {
|
||||
self.read_global_as_value(GlobalId {
|
||||
instance,
|
||||
promoted: None,
|
||||
}, self.layout_of(self.place_ty(place))?)))
|
||||
}, self.place_ty(place)).map(Some)
|
||||
}
|
||||
Projection(ref proj) => self.try_read_place_projection(proj),
|
||||
}
|
||||
|
@ -199,17 +201,44 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
|||
},
|
||||
|
||||
Static(ref static_) => {
|
||||
let instance = ty::Instance::mono(self.tcx, static_.def_id);
|
||||
let gid = GlobalId {
|
||||
instance,
|
||||
promoted: None,
|
||||
};
|
||||
let alloc = self
|
||||
.tcx
|
||||
.interpret_interner
|
||||
.borrow()
|
||||
.get_cached(static_.def_id);
|
||||
let layout = self.layout_of(self.place_ty(mir_place))?;
|
||||
let alloc = self.tcx.interpret_interner.borrow().get_cached(gid).expect("uncached global");
|
||||
Place::Ptr {
|
||||
ptr: MemoryPointer::new(alloc, 0).into(),
|
||||
align: layout.align,
|
||||
extra: PlaceExtra::None,
|
||||
if let Some(alloc) = alloc {
|
||||
Place::Ptr {
|
||||
ptr: MemoryPointer::new(alloc, 0).into(),
|
||||
align: layout.align,
|
||||
extra: PlaceExtra::None,
|
||||
}
|
||||
} else {
|
||||
let instance = ty::Instance::mono(self.tcx, static_.def_id);
|
||||
let cid = GlobalId {
|
||||
instance,
|
||||
promoted: None
|
||||
};
|
||||
let param_env = ty::ParamEnv::empty(traits::Reveal::All);
|
||||
// ensure the static is computed
|
||||
if let Err(err) = self.tcx.const_eval(param_env.and(cid)) {
|
||||
match err.kind {
|
||||
ErrKind::Miri(miri) => return Err(miri),
|
||||
ErrKind::TypeckError => return err!(TypeckError),
|
||||
other => bug!("const eval returned {:?}", other),
|
||||
}
|
||||
};
|
||||
let alloc = self
|
||||
.tcx
|
||||
.interpret_interner
|
||||
.borrow()
|
||||
.get_cached(static_.def_id)
|
||||
.expect("uncached static");
|
||||
Place::Ptr {
|
||||
ptr: MemoryPointer::new(alloc, 0).into(),
|
||||
align: layout.align,
|
||||
extra: PlaceExtra::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,21 +2,12 @@
|
|||
//!
|
||||
//! The main entry point is the `step` method.
|
||||
|
||||
use rustc::hir;
|
||||
use rustc::mir::visit::{Visitor, PlaceContext};
|
||||
use rustc::mir;
|
||||
use rustc::ty::{self, Instance};
|
||||
use rustc::ty::layout::LayoutOf;
|
||||
use rustc::middle::const_val::ConstVal;
|
||||
use rustc::mir::interpret::GlobalId;
|
||||
|
||||
use rustc::mir::interpret::{EvalResult, EvalErrorKind};
|
||||
use super::{EvalContext, StackPopCleanup, Place, Machine};
|
||||
use rustc::mir::interpret::EvalResult;
|
||||
use super::{EvalContext, Machine};
|
||||
|
||||
use syntax::codemap::Span;
|
||||
use syntax::ast::Mutability;
|
||||
|
||||
impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
pub fn inc_step_counter_and_check_limit(&mut self, n: u64) -> EvalResult<'tcx> {
|
||||
self.steps_remaining = self.steps_remaining.saturating_sub(n);
|
||||
if self.steps_remaining > 0 {
|
||||
|
@ -41,52 +32,14 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
|||
let old_frames = self.cur_frame();
|
||||
|
||||
if let Some(stmt) = basic_block.statements.get(stmt_id) {
|
||||
let mut new = Ok(false);
|
||||
ConstantExtractor {
|
||||
span: stmt.source_info.span,
|
||||
instance: self.frame().instance,
|
||||
ecx: self,
|
||||
mir,
|
||||
new_constant: &mut new,
|
||||
}.visit_statement(
|
||||
block,
|
||||
stmt,
|
||||
mir::Location {
|
||||
block,
|
||||
statement_index: stmt_id,
|
||||
},
|
||||
);
|
||||
// if ConstantExtractor added a new frame, we don't execute anything here
|
||||
// but await the next call to step
|
||||
if !new? {
|
||||
assert_eq!(old_frames, self.cur_frame());
|
||||
self.statement(stmt)?;
|
||||
}
|
||||
assert_eq!(old_frames, self.cur_frame());
|
||||
self.statement(stmt)?;
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
let terminator = basic_block.terminator();
|
||||
let mut new = Ok(false);
|
||||
ConstantExtractor {
|
||||
span: terminator.source_info.span,
|
||||
instance: self.frame().instance,
|
||||
ecx: self,
|
||||
mir,
|
||||
new_constant: &mut new,
|
||||
}.visit_terminator(
|
||||
block,
|
||||
terminator,
|
||||
mir::Location {
|
||||
block,
|
||||
statement_index: stmt_id,
|
||||
},
|
||||
);
|
||||
// if ConstantExtractor added a new frame, we don't execute anything here
|
||||
// but await the next call to step
|
||||
if !new? {
|
||||
assert_eq!(old_frames, self.cur_frame());
|
||||
self.terminator(terminator)?;
|
||||
}
|
||||
assert_eq!(old_frames, self.cur_frame());
|
||||
self.terminator(terminator)?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
|
@ -152,184 +105,4 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// returns `true` if a stackframe was pushed
|
||||
fn global_item(
|
||||
&mut self,
|
||||
instance: Instance<'tcx>,
|
||||
span: Span,
|
||||
mutability: Mutability,
|
||||
) -> EvalResult<'tcx, bool> {
|
||||
debug!("global_item: {:?}", instance);
|
||||
let cid = GlobalId {
|
||||
instance,
|
||||
promoted: None,
|
||||
};
|
||||
if self.tcx.interpret_interner.borrow().get_cached(cid).is_some() {
|
||||
return Ok(false);
|
||||
}
|
||||
if self.tcx.has_attr(instance.def_id(), "linkage") {
|
||||
M::global_item_with_linkage(self, cid.instance, mutability)?;
|
||||
return Ok(false);
|
||||
}
|
||||
let instance_ty = instance.ty(self.tcx);
|
||||
let layout = self.layout_of(instance_ty)?;
|
||||
assert!(!layout.is_unsized());
|
||||
let ptr = self.memory.allocate(
|
||||
layout.size.bytes(),
|
||||
layout.align,
|
||||
None,
|
||||
)?;
|
||||
self.tcx.interpret_interner.borrow_mut().cache(cid, ptr.alloc_id);
|
||||
let internally_mutable = !layout.ty.is_freeze(self.tcx, self.param_env, span);
|
||||
let mutability = if mutability == Mutability::Mutable || internally_mutable {
|
||||
Mutability::Mutable
|
||||
} else {
|
||||
Mutability::Immutable
|
||||
};
|
||||
let cleanup = StackPopCleanup::MarkStatic(mutability);
|
||||
let name = ty::tls::with(|tcx| tcx.item_path_str(instance.def_id()));
|
||||
trace!("pushing stack frame for global: {}", name);
|
||||
let mir = self.load_mir(instance.def)?;
|
||||
self.push_stack_frame(
|
||||
instance,
|
||||
span,
|
||||
mir,
|
||||
Place::from_ptr(ptr, layout.align),
|
||||
cleanup,
|
||||
)?;
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b, M: Machine<'tcx> + 'a> {
|
||||
span: Span,
|
||||
ecx: &'a mut EvalContext<'b, 'tcx, M>,
|
||||
mir: &'tcx mir::Mir<'tcx>,
|
||||
instance: ty::Instance<'tcx>,
|
||||
// Whether a stackframe for a new constant has been pushed
|
||||
new_constant: &'a mut EvalResult<'tcx, bool>,
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'tcx, M: Machine<'tcx>> ConstantExtractor<'a, 'b, 'tcx, M> {
|
||||
fn try<F: FnOnce(&mut Self) -> EvalResult<'tcx, bool>>(&mut self, f: F) {
|
||||
match *self.new_constant {
|
||||
// already computed a constant, don't do more than one per iteration
|
||||
Ok(true) => {},
|
||||
// no constants computed yet
|
||||
Ok(false) => *self.new_constant = f(self),
|
||||
// error happened, abort the visitor traversing
|
||||
Err(_) => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'tcx, M: Machine<'tcx>> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx, M> {
|
||||
fn visit_constant(&mut self, constant: &mir::Constant<'tcx>, location: mir::Location) {
|
||||
self.super_constant(constant, location);
|
||||
self.try(|this| {
|
||||
match constant.literal {
|
||||
// already computed by rustc
|
||||
mir::Literal::Value { value: &ty::Const { val: ConstVal::Unevaluated(def_id, substs), .. } } => {
|
||||
debug!("global_item: {:?}, {:#?}", def_id, substs);
|
||||
let substs = this.ecx.tcx.trans_apply_param_substs(this.instance.substs, &substs);
|
||||
debug!("global_item_new_substs: {:#?}", substs);
|
||||
debug!("global_item_param_env: {:#?}", this.ecx.param_env);
|
||||
let instance = Instance::resolve(
|
||||
this.ecx.tcx,
|
||||
this.ecx.param_env,
|
||||
def_id,
|
||||
substs,
|
||||
).ok_or(EvalErrorKind::TypeckError)?; // turn error prop into a panic to expose associated type in const issue
|
||||
this.ecx.global_item(
|
||||
instance,
|
||||
constant.span,
|
||||
Mutability::Immutable,
|
||||
)
|
||||
}
|
||||
mir::Literal::Value { .. } => Ok(false),
|
||||
mir::Literal::Promoted { index } => {
|
||||
let cid = GlobalId {
|
||||
instance: this.instance,
|
||||
promoted: Some(index),
|
||||
};
|
||||
if this.ecx.tcx.interpret_interner.borrow().get_cached(cid).is_some() {
|
||||
return Ok(false);
|
||||
}
|
||||
let mir = &this.mir.promoted[index];
|
||||
let ty = this.ecx.monomorphize(mir.return_ty(), this.instance.substs);
|
||||
let layout = this.ecx.layout_of(ty)?;
|
||||
assert!(!layout.is_unsized());
|
||||
let ptr = this.ecx.memory.allocate(
|
||||
layout.size.bytes(),
|
||||
layout.align,
|
||||
None,
|
||||
)?;
|
||||
this.ecx.tcx.interpret_interner.borrow_mut().cache(cid, ptr.alloc_id);
|
||||
trace!("pushing stack frame for {:?}", index);
|
||||
this.ecx.push_stack_frame(
|
||||
this.instance,
|
||||
constant.span,
|
||||
mir,
|
||||
Place::from_ptr(ptr, layout.align),
|
||||
StackPopCleanup::MarkStatic(Mutability::Immutable),
|
||||
)?;
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn visit_place(
|
||||
&mut self,
|
||||
place: &mir::Place<'tcx>,
|
||||
context: PlaceContext<'tcx>,
|
||||
location: mir::Location,
|
||||
) {
|
||||
self.super_place(place, context, location);
|
||||
self.try(|this| {
|
||||
if let mir::Place::Static(ref static_) = *place {
|
||||
let def_id = static_.def_id;
|
||||
let span = this.span;
|
||||
if let Some(node_item) = this.ecx.tcx.hir.get_if_local(def_id) {
|
||||
if let hir::map::Node::NodeItem(&hir::Item { ref node, .. }) = node_item {
|
||||
if let hir::ItemStatic(_, m, _) = *node {
|
||||
let instance = Instance::mono(this.ecx.tcx, def_id);
|
||||
this.ecx.global_item(
|
||||
instance,
|
||||
span,
|
||||
if m == hir::MutMutable {
|
||||
Mutability::Mutable
|
||||
} else {
|
||||
Mutability::Immutable
|
||||
},
|
||||
)
|
||||
} else {
|
||||
bug!("static def id doesn't point to static");
|
||||
}
|
||||
} else {
|
||||
bug!("static def id doesn't point to item");
|
||||
}
|
||||
} else {
|
||||
let def = this.ecx.tcx.describe_def(def_id).expect("static not found");
|
||||
if let hir::def::Def::Static(_, mutable) = def {
|
||||
let instance = Instance::mono(this.ecx.tcx, def_id);
|
||||
this.ecx.global_item(
|
||||
instance,
|
||||
span,
|
||||
if mutable {
|
||||
Mutability::Mutable
|
||||
} else {
|
||||
Mutability::Immutable
|
||||
},
|
||||
)
|
||||
} else {
|
||||
bug!("static found but isn't a static: {:?}", def);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use syntax::codemap::Span;
|
|||
use rustc::mir::interpret::{EvalResult, PrimVal, Value};
|
||||
use interpret::{Machine, ValTy, EvalContext, Place, PlaceExtra};
|
||||
|
||||
impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
pub(crate) fn drop_place(
|
||||
&mut self,
|
||||
place: Place,
|
||||
|
|
|
@ -13,7 +13,7 @@ use interpret::memory::HasMemory;
|
|||
|
||||
mod drop;
|
||||
|
||||
impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
pub fn goto_block(&mut self, target: mir::BasicBlock) {
|
||||
self.frame_mut().block = target;
|
||||
self.frame_mut().stmt = 0;
|
||||
|
|
|
@ -6,7 +6,7 @@ use rustc::mir::interpret::{PrimVal, Value, MemoryPointer, EvalResult};
|
|||
use super::{EvalContext, eval_context,
|
||||
Machine};
|
||||
|
||||
impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
/// Creates a dynamic vtable for the given type and vtable origin. This is used only for
|
||||
/// objects.
|
||||
///
|
||||
|
@ -54,7 +54,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
|||
|
||||
self.memory.mark_static_initalized(
|
||||
vtable.alloc_id,
|
||||
Mutability::Mutable,
|
||||
Mutability::Immutable,
|
||||
)?;
|
||||
|
||||
Ok(vtable)
|
||||
|
|
|
@ -194,6 +194,7 @@ use rustc::hir::itemlikevisit::ItemLikeVisitor;
|
|||
use rustc::hir::map as hir_map;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::middle::const_val::ConstVal;
|
||||
use rustc::mir::interpret::{Value, PrimVal, AllocId, Pointer};
|
||||
use rustc::middle::lang_items::{ExchangeMallocFnLangItem, StartFnLangItem};
|
||||
use rustc::traits;
|
||||
use rustc::ty::subst::{Substs, Kind};
|
||||
|
@ -568,14 +569,26 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
|
|||
fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, location: Location) {
|
||||
debug!("visiting const {:?} @ {:?}", *constant, location);
|
||||
|
||||
if let ConstVal::Unevaluated(def_id, substs) = constant.val {
|
||||
let substs = self.tcx.trans_apply_param_substs(self.param_substs,
|
||||
&substs);
|
||||
let instance = ty::Instance::resolve(self.tcx,
|
||||
ty::ParamEnv::empty(traits::Reveal::All),
|
||||
def_id,
|
||||
substs).unwrap();
|
||||
collect_neighbours(self.tcx, instance, true, self.output);
|
||||
match constant.val {
|
||||
ConstVal::Unevaluated(def_id, substs) => {
|
||||
let substs = self.tcx.trans_apply_param_substs(self.param_substs,
|
||||
&substs);
|
||||
let instance = ty::Instance::resolve(self.tcx,
|
||||
ty::ParamEnv::empty(traits::Reveal::All),
|
||||
def_id,
|
||||
substs).unwrap();
|
||||
collect_neighbours(self.tcx, instance, true, self.output);
|
||||
},
|
||||
ConstVal::Value(Value::ByValPair(PrimVal::Ptr(a), PrimVal::Ptr(b))) => {
|
||||
collect_miri(self.tcx, a.alloc_id, self.output);
|
||||
collect_miri(self.tcx, b.alloc_id, self.output);
|
||||
}
|
||||
ConstVal::Value(Value::ByValPair(_, PrimVal::Ptr(ptr))) |
|
||||
ConstVal::Value(Value::ByValPair(PrimVal::Ptr(ptr), _)) |
|
||||
ConstVal::Value(Value::ByVal(PrimVal::Ptr(ptr))) |
|
||||
ConstVal::Value(Value::ByRef(Pointer { primval: PrimVal::Ptr(ptr) }, _)) =>
|
||||
collect_miri(self.tcx, ptr.alloc_id, self.output),
|
||||
_ => {},
|
||||
}
|
||||
|
||||
self.super_const(constant);
|
||||
|
@ -1098,6 +1111,28 @@ fn create_mono_items_for_default_impls<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|||
}
|
||||
}
|
||||
|
||||
/// Scan the miri alloc in order to find function calls, closures, and drop-glue
|
||||
fn collect_miri<'a, 'tcx>(
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
alloc_id: AllocId,
|
||||
output: &mut Vec<MonoItem<'tcx>>,
|
||||
) {
|
||||
let interpret_interner = tcx.interpret_interner.borrow();
|
||||
if let Some(alloc) = interpret_interner.get_alloc(alloc_id) {
|
||||
trace!("collecting {:?} with {:#?}", alloc_id, alloc);
|
||||
for &inner in alloc.relocations.values() {
|
||||
collect_miri(tcx, inner, output);
|
||||
}
|
||||
} else if let Some(fn_instance) = interpret_interner.get_fn(alloc_id) {
|
||||
if should_monomorphize_locally(tcx, &fn_instance) {
|
||||
trace!("collecting {:?} with {:#?}", alloc_id, fn_instance);
|
||||
output.push(create_fn_mono_item(fn_instance));
|
||||
}
|
||||
} else {
|
||||
bug!("alloc id without corresponding allocation: {}", alloc_id);
|
||||
}
|
||||
}
|
||||
|
||||
/// Scan the MIR in order to find function calls, closures, and drop-glue
|
||||
fn collect_neighbours<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
instance: Instance<'tcx>,
|
||||
|
|
|
@ -213,7 +213,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
|
|||
// locals are safe
|
||||
}
|
||||
&Place::Static(box Static { def_id, ty: _ }) => {
|
||||
if self.tcx.is_static_mut(def_id) {
|
||||
if self.tcx.is_static(def_id) == Some(hir::Mutability::MutMutable) {
|
||||
self.require_unsafe("use of mutable static");
|
||||
} else if self.tcx.is_foreign_item(def_id) {
|
||||
let source_info = self.source_info;
|
||||
|
|
|
@ -11,44 +11,52 @@
|
|||
//! Performs various peephole optimizations.
|
||||
|
||||
use rustc::mir::{Constant, Literal, Location, Place, Mir, Operand, ProjectionElem, Rvalue, Local};
|
||||
use rustc::mir::visit::{MutVisitor, Visitor};
|
||||
use rustc::ty::{TyCtxt, TypeVariants};
|
||||
use rustc::mir::{NullOp, StatementKind, Statement, BasicBlock};
|
||||
use rustc::mir::{SourceInfo, ARGUMENT_VISIBILITY_SCOPE, TerminatorKind};
|
||||
use rustc::mir::visit::{MutVisitor, Visitor, TyContext};
|
||||
use rustc::middle::const_val::ConstVal;
|
||||
use rustc::ty::{TyCtxt, TypeVariants, self, Instance};
|
||||
use rustc::mir::interpret::{Value, PrimVal, GlobalId};
|
||||
use interpret::{eval_body_with_mir, eval_body, mk_borrowck_eval_cx, unary_op, ValTy};
|
||||
use rustc::util::nodemap::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
use std::mem;
|
||||
use std::collections::VecDeque;
|
||||
use transform::{MirPass, MirSource};
|
||||
use syntax::codemap::{Span, DUMMY_SP};
|
||||
use rustc_data_structures::control_flow_graph::ControlFlowGraph;
|
||||
use rustc::ty::subst::Substs;
|
||||
|
||||
pub struct InstCombine;
|
||||
|
||||
impl MirPass for InstCombine {
|
||||
fn run_pass<'a, 'tcx>(&self,
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
_: MirSource,
|
||||
source: MirSource,
|
||||
mir: &mut Mir<'tcx>) {
|
||||
// We only run when optimizing MIR (at any level).
|
||||
if tcx.sess.opts.debugging_opts.mir_opt_level == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// First, find optimization opportunities. This is done in a pre-pass to keep the MIR
|
||||
// read-only so that we can do global analyses on the MIR in the process (e.g.
|
||||
// `Place::ty()`).
|
||||
let optimizations = {
|
||||
let mut optimization_finder = OptimizationFinder::new(mir, tcx);
|
||||
let mut optimization_finder = OptimizationFinder::new(mir, tcx, source);
|
||||
optimization_finder.visit_mir(mir);
|
||||
optimization_finder.optimizations
|
||||
};
|
||||
|
||||
// Then carry out those optimizations.
|
||||
MutVisitor::visit_mir(&mut InstCombineVisitor { optimizations }, mir);
|
||||
MutVisitor::visit_mir(&mut InstCombineVisitor { optimizations, tcx }, mir);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InstCombineVisitor<'tcx> {
|
||||
type Const<'tcx> = (Value, ty::Ty<'tcx>, Span);
|
||||
|
||||
pub struct InstCombineVisitor<'a, 'tcx: 'a> {
|
||||
optimizations: OptimizationList<'tcx>,
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> MutVisitor<'tcx> for InstCombineVisitor<'tcx> {
|
||||
impl<'a, 'tcx> MutVisitor<'tcx> for InstCombineVisitor<'a, 'tcx> {
|
||||
fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) {
|
||||
if self.optimizations.and_stars.remove(&location) {
|
||||
debug!("Replacing `&*`: {:?}", rvalue);
|
||||
|
@ -67,28 +75,460 @@ impl<'tcx> MutVisitor<'tcx> for InstCombineVisitor<'tcx> {
|
|||
*rvalue = Rvalue::Use(Operand::Constant(box constant));
|
||||
}
|
||||
|
||||
if let Some((value, ty, span)) = self.optimizations.const_prop.remove(&location) {
|
||||
let value = self.tcx.mk_const(ty::Const {
|
||||
val: ConstVal::Value(value),
|
||||
ty,
|
||||
});
|
||||
debug!("Replacing `{:?}` with {:?}", rvalue, value);
|
||||
let constant = Constant {
|
||||
ty,
|
||||
literal: Literal::Value { value },
|
||||
span,
|
||||
};
|
||||
*rvalue = Rvalue::Use(Operand::Constant(box constant));
|
||||
}
|
||||
|
||||
self.super_rvalue(rvalue, location)
|
||||
}
|
||||
|
||||
fn visit_constant(
|
||||
&mut self,
|
||||
constant: &mut Constant<'tcx>,
|
||||
location: Location,
|
||||
) {
|
||||
self.super_constant(constant, location);
|
||||
if let Some(&(val, ty, _)) = self.optimizations.constants.get(constant) {
|
||||
constant.literal = Literal::Value {
|
||||
value: self.tcx.mk_const(ty::Const {
|
||||
val: ConstVal::Value(val),
|
||||
ty,
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_operand(
|
||||
&mut self,
|
||||
operand: &mut Operand<'tcx>,
|
||||
location: Location,
|
||||
) {
|
||||
self.super_operand(operand, location);
|
||||
let new = match operand {
|
||||
Operand::Move(Place::Local(local)) |
|
||||
Operand::Copy(Place::Local(local)) => {
|
||||
trace!("trying to read {:?}", local);
|
||||
self.optimizations.places.get(&local).cloned()
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
if let Some((value, ty, span)) = new {
|
||||
let value = self.tcx.mk_const(ty::Const {
|
||||
val: ConstVal::Value(value),
|
||||
ty,
|
||||
});
|
||||
debug!("Replacing `{:?}` with {:?}", operand, value);
|
||||
let constant = Constant {
|
||||
ty,
|
||||
literal: Literal::Value { value },
|
||||
span,
|
||||
};
|
||||
*operand = Operand::Constant(box constant);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_terminator_kind(
|
||||
&mut self,
|
||||
block: BasicBlock,
|
||||
kind: &mut TerminatorKind<'tcx>,
|
||||
location: Location,
|
||||
) {
|
||||
match kind {
|
||||
TerminatorKind::SwitchInt { discr: value, .. } |
|
||||
TerminatorKind::Yield { value, .. } |
|
||||
TerminatorKind::Assert { cond: value, .. } => {
|
||||
if let Some((new, ty, span)) = self.optimizations.const_prop.remove(&location) {
|
||||
let new = self.tcx.mk_const(ty::Const {
|
||||
val: ConstVal::Value(new),
|
||||
ty,
|
||||
});
|
||||
debug!("Replacing `{:?}` with {:?}", value, new);
|
||||
let constant = Constant {
|
||||
ty,
|
||||
literal: Literal::Value { value: new },
|
||||
span,
|
||||
};
|
||||
*value = Operand::Constant(box constant);
|
||||
}
|
||||
}
|
||||
// FIXME: do this optimization for function calls
|
||||
_ => {},
|
||||
}
|
||||
self.super_terminator_kind(block, kind, location)
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds optimization opportunities on the MIR.
|
||||
struct OptimizationFinder<'b, 'a, 'tcx:'a+'b> {
|
||||
mir: &'b Mir<'tcx>,
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
source: MirSource,
|
||||
optimizations: OptimizationList<'tcx>,
|
||||
}
|
||||
|
||||
impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> {
|
||||
fn new(mir: &'b Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> OptimizationFinder<'b, 'a, 'tcx> {
|
||||
fn new(
|
||||
mir: &'b Mir<'tcx>,
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
source: MirSource,
|
||||
) -> OptimizationFinder<'b, 'a, 'tcx> {
|
||||
OptimizationFinder {
|
||||
mir,
|
||||
tcx,
|
||||
source,
|
||||
optimizations: OptimizationList::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_constant(&mut self, c: &Constant<'tcx>, span: Span) -> Option<Const<'tcx>> {
|
||||
if let Some(&val) = self.optimizations.constants.get(c) {
|
||||
return Some(val);
|
||||
}
|
||||
match c.literal {
|
||||
Literal::Value { value } => match value.val {
|
||||
ConstVal::Value(v) => Some((v, value.ty, span)),
|
||||
ConstVal::Unevaluated(did, substs) => {
|
||||
let param_env = self.tcx.param_env(self.source.def_id);
|
||||
let span = self.tcx.def_span(did);
|
||||
let instance = Instance::resolve(
|
||||
self.tcx,
|
||||
param_env,
|
||||
did,
|
||||
substs,
|
||||
)?;
|
||||
let cid = GlobalId {
|
||||
instance,
|
||||
promoted: None,
|
||||
};
|
||||
let (value, _, ty) = eval_body(self.tcx, cid, param_env)?;
|
||||
let val = (value, ty, span);
|
||||
trace!("evaluated {:?} to {:?}", c, val);
|
||||
self.optimizations.constants.insert(c.clone(), val);
|
||||
Some(val)
|
||||
},
|
||||
},
|
||||
// evaluate the promoted and replace the constant with the evaluated result
|
||||
Literal::Promoted { index } => {
|
||||
let generics = self.tcx.generics_of(self.source.def_id);
|
||||
if generics.parent_types as usize + generics.types.len() > 0 {
|
||||
// FIXME: can't handle code with generics
|
||||
return None;
|
||||
}
|
||||
let substs = Substs::identity_for_item(self.tcx, self.source.def_id);
|
||||
let instance = Instance::new(self.source.def_id, substs);
|
||||
let cid = GlobalId {
|
||||
instance,
|
||||
promoted: Some(index),
|
||||
};
|
||||
let span = self.tcx.def_span(self.source.def_id);
|
||||
let param_env = self.tcx.param_env(self.source.def_id);
|
||||
let (value, _, ty) = eval_body_with_mir(self.tcx, cid, self.mir, param_env)?;
|
||||
let val = (value, ty, span);
|
||||
trace!("evaluated {:?} to {:?}", c, val);
|
||||
self.optimizations.constants.insert(c.clone(), val);
|
||||
Some(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_operand(&mut self, op: &Operand<'tcx>, span: Span) -> Option<Const<'tcx>> {
|
||||
match *op {
|
||||
Operand::Constant(ref c) => self.eval_constant(c, span),
|
||||
Operand::Move(ref place) | Operand::Copy(ref place) => match *place {
|
||||
Place::Local(loc) => self.optimizations.places.get(&loc).cloned(),
|
||||
// FIXME(oli-obk): field and index projections
|
||||
Place::Projection(_) => None,
|
||||
_ => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn simplify_operand(&mut self, op: &Operand<'tcx>, span: Span) -> Option<Const<'tcx>> {
|
||||
match *op {
|
||||
Operand::Constant(ref c) => match c.literal {
|
||||
Literal::Value { .. } => None,
|
||||
_ => self.eval_operand(op, span),
|
||||
},
|
||||
_ => self.eval_operand(op, span),
|
||||
}
|
||||
}
|
||||
|
||||
fn const_prop(
|
||||
&mut self,
|
||||
rvalue: &Rvalue<'tcx>,
|
||||
place_ty: ty::Ty<'tcx>,
|
||||
span: Span,
|
||||
) -> Option<Const<'tcx>> {
|
||||
match *rvalue {
|
||||
Rvalue::Use(ref op) => self.simplify_operand(op, span),
|
||||
Rvalue::Repeat(..) |
|
||||
Rvalue::Ref(..) |
|
||||
Rvalue::Cast(..) |
|
||||
Rvalue::Aggregate(..) |
|
||||
Rvalue::NullaryOp(NullOp::Box, _) |
|
||||
Rvalue::Discriminant(..) => None,
|
||||
// FIXME(oli-obk): evaluate static/constant slice lengths
|
||||
Rvalue::Len(_) => None,
|
||||
Rvalue::NullaryOp(NullOp::SizeOf, ty) => {
|
||||
let param_env = self.tcx.param_env(self.source.def_id);
|
||||
type_size_of(self.tcx, param_env, ty).map(|n| (
|
||||
Value::ByVal(PrimVal::Bytes(n as u128)),
|
||||
self.tcx.types.usize,
|
||||
span,
|
||||
))
|
||||
}
|
||||
Rvalue::UnaryOp(op, ref arg) => {
|
||||
let def_id = if self.tcx.is_closure(self.source.def_id) {
|
||||
self.tcx.closure_base_def_id(self.source.def_id)
|
||||
} else {
|
||||
self.source.def_id
|
||||
};
|
||||
let generics = self.tcx.generics_of(def_id);
|
||||
if generics.parent_types as usize + generics.types.len() > 0 {
|
||||
// FIXME: can't handle code with generics
|
||||
return None;
|
||||
}
|
||||
let substs = Substs::identity_for_item(self.tcx, self.source.def_id);
|
||||
let instance = Instance::new(self.source.def_id, substs);
|
||||
let ecx = mk_borrowck_eval_cx(self.tcx, instance, self.mir).unwrap();
|
||||
|
||||
let val = self.eval_operand(arg, span)?;
|
||||
let prim = ecx.value_to_primval(ValTy { value: val.0, ty: val.1 }).ok()?;
|
||||
let kind = ecx.ty_to_primval_kind(val.1).ok()?;
|
||||
match unary_op(op, prim, kind) {
|
||||
Ok(val) => Some((Value::ByVal(val), place_ty, span)),
|
||||
Err(mut err) => {
|
||||
ecx.report(&mut err, false);
|
||||
None
|
||||
},
|
||||
}
|
||||
}
|
||||
Rvalue::CheckedBinaryOp(op, ref left, ref right) |
|
||||
Rvalue::BinaryOp(op, ref left, ref right) => {
|
||||
trace!("rvalue binop {:?} for {:?} and {:?}", op, left, right);
|
||||
let left = self.eval_operand(left, span)?;
|
||||
let right = self.eval_operand(right, span)?;
|
||||
let def_id = if self.tcx.is_closure(self.source.def_id) {
|
||||
self.tcx.closure_base_def_id(self.source.def_id)
|
||||
} else {
|
||||
self.source.def_id
|
||||
};
|
||||
let generics = self.tcx.generics_of(def_id);
|
||||
let has_generics = generics.parent_types as usize + generics.types.len() > 0;
|
||||
if has_generics {
|
||||
// FIXME: can't handle code with generics
|
||||
return None;
|
||||
}
|
||||
let substs = Substs::identity_for_item(self.tcx, self.source.def_id);
|
||||
let instance = Instance::new(self.source.def_id, substs);
|
||||
let ecx = mk_borrowck_eval_cx(self.tcx, instance, self.mir).unwrap();
|
||||
|
||||
let l = ecx.value_to_primval(ValTy { value: left.0, ty: left.1 }).ok()?;
|
||||
let r = ecx.value_to_primval(ValTy { value: right.0, ty: right.1 }).ok()?;
|
||||
trace!("const evaluating {:?} for {:?} and {:?}", op, left, right);
|
||||
match ecx.binary_op(op, l, left.1, r, right.1) {
|
||||
Ok((val, overflow)) => {
|
||||
let val = if let Rvalue::CheckedBinaryOp(..) = *rvalue {
|
||||
Value::ByValPair(
|
||||
val,
|
||||
PrimVal::from_bool(overflow),
|
||||
)
|
||||
} else {
|
||||
if overflow {
|
||||
use rustc::mir::interpret::EvalError;
|
||||
use rustc::mir::interpret::EvalErrorKind;
|
||||
let mut err = EvalError {
|
||||
kind: EvalErrorKind::OverflowingMath,
|
||||
backtrace: None,
|
||||
};
|
||||
ecx.report(&mut err, false);
|
||||
return None;
|
||||
}
|
||||
Value::ByVal(val)
|
||||
};
|
||||
Some((val, place_ty, span))
|
||||
},
|
||||
Err(mut err) => {
|
||||
ecx.report(&mut err, false);
|
||||
None
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn type_size_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
ty: ty::Ty<'tcx>) -> Option<u64> {
|
||||
use rustc::ty::layout::LayoutOf;
|
||||
(tcx, param_env).layout_of(ty).ok().map(|layout| layout.size.bytes())
|
||||
}
|
||||
|
||||
struct ConstPropVisitor {
|
||||
local: Local,
|
||||
can_const_prop: bool,
|
||||
// false at the beginning, once set, there are not allowed to be any more assignments
|
||||
found_assignment: bool,
|
||||
}
|
||||
|
||||
impl ConstPropVisitor {
|
||||
/// returns true if `local` can be propagated
|
||||
fn check<'tcx>(local: Local, mir: &Mir<'tcx>) -> bool {
|
||||
let mut cpv = ConstPropVisitor {
|
||||
local,
|
||||
can_const_prop: true,
|
||||
found_assignment: false,
|
||||
};
|
||||
cpv.visit_mir(mir);
|
||||
cpv.can_const_prop
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for ConstPropVisitor {
|
||||
fn visit_statement(
|
||||
&mut self,
|
||||
block: BasicBlock,
|
||||
statement: &Statement<'tcx>,
|
||||
location: Location,
|
||||
) {
|
||||
self.super_statement(block, statement, location);
|
||||
match statement.kind {
|
||||
StatementKind::SetDiscriminant { place: Place::Local(local), .. } |
|
||||
StatementKind::Assign(Place::Local(local), _) => {
|
||||
if local == self.local {
|
||||
if self.found_assignment {
|
||||
self.can_const_prop = false;
|
||||
} else {
|
||||
self.found_assignment = true
|
||||
}
|
||||
}
|
||||
},
|
||||
StatementKind::InlineAsm { ref outputs, .. } => {
|
||||
for place in outputs {
|
||||
if let Place::Local(local) = *place {
|
||||
if local == self.local {
|
||||
if self.found_assignment {
|
||||
self.can_const_prop = false;
|
||||
} else {
|
||||
self.found_assignment = true
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
|
||||
self.super_rvalue(rvalue, location);
|
||||
if let Rvalue::Ref(_, _, Place::Local(local)) = *rvalue {
|
||||
if local == self.local {
|
||||
self.can_const_prop = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b, 'a, 'tcx> Visitor<'tcx> for OptimizationFinder<'b, 'a, 'tcx> {
|
||||
// override to visit basic blocks in execution order
|
||||
fn super_mir(&mut self, mir: &Mir<'tcx>) {
|
||||
let mut seen = FxHashSet::default();
|
||||
seen.insert(mir.start_node());
|
||||
let mut sorted = Vec::new();
|
||||
let mut next = VecDeque::new();
|
||||
sorted.push(mir.start_node());
|
||||
next.push_back(mir.start_node());
|
||||
while let Some(current) = next.pop_front() {
|
||||
for successor in mir.successors(current) {
|
||||
trace!("checking successor of {:?}: {:?}", current, successor);
|
||||
trace!("{:?}, {:?}", sorted, next);
|
||||
if seen.contains(&successor) {
|
||||
for &pending in &next {
|
||||
// not a back-edge, just a branch merging back into a single execution
|
||||
if pending == successor {
|
||||
// move to the back of the queue
|
||||
let i = sorted.iter().position(|&b| b == successor).unwrap();
|
||||
sorted.remove(i);
|
||||
sorted.push(successor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
seen.insert(successor);
|
||||
sorted.push(successor);
|
||||
next.push_back(successor);
|
||||
}
|
||||
}
|
||||
}
|
||||
trace!("checking basic blocks: {:?}", sorted);
|
||||
for bb in sorted {
|
||||
self.visit_basic_block_data(bb, &mir[bb]);
|
||||
}
|
||||
|
||||
for scope in &mir.visibility_scopes {
|
||||
self.visit_visibility_scope_data(scope);
|
||||
}
|
||||
|
||||
self.visit_ty(&mir.return_ty(), TyContext::ReturnTy(SourceInfo {
|
||||
span: mir.span,
|
||||
scope: ARGUMENT_VISIBILITY_SCOPE,
|
||||
}));
|
||||
|
||||
for local in mir.local_decls.indices() {
|
||||
self.visit_local_decl(local, &mir.local_decls[local]);
|
||||
}
|
||||
|
||||
self.visit_span(&mir.span);
|
||||
}
|
||||
|
||||
fn visit_constant(
|
||||
&mut self,
|
||||
constant: &Constant<'tcx>,
|
||||
location: Location,
|
||||
) {
|
||||
trace!("visit_constant: {:?}", constant);
|
||||
self.super_constant(constant, location);
|
||||
self.eval_constant(constant, DUMMY_SP);
|
||||
}
|
||||
|
||||
fn visit_statement(
|
||||
&mut self,
|
||||
block: BasicBlock,
|
||||
statement: &Statement<'tcx>,
|
||||
location: Location,
|
||||
) {
|
||||
trace!("visit_statement: {:?}", statement);
|
||||
if let StatementKind::Assign(ref place, ref rval) = statement.kind {
|
||||
let place_ty = place
|
||||
.ty(&self.mir.local_decls, self.tcx)
|
||||
.to_ty(self.tcx);
|
||||
let span = self.mir.source_info(location).span;
|
||||
if let Some(value) = self.const_prop(rval, place_ty, span) {
|
||||
self.optimizations.const_prop.insert(location, value);
|
||||
if let Place::Local(local) = *place {
|
||||
if !self.mir.local_decls[local].is_user_variable
|
||||
&& ConstPropVisitor::check(local, self.mir) {
|
||||
trace!("storing {:?} to {:?}", value, local);
|
||||
assert!(self.optimizations.places.insert(local, value).is_none());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.super_statement(block, statement, location);
|
||||
}
|
||||
|
||||
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
|
||||
if let Rvalue::Ref(_, _, Place::Projection(ref projection)) = *rvalue {
|
||||
if let ProjectionElem::Deref = projection.elem {
|
||||
|
@ -111,10 +551,33 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for OptimizationFinder<'b, 'a, 'tcx> {
|
|||
|
||||
self.super_rvalue(rvalue, location)
|
||||
}
|
||||
|
||||
fn visit_terminator_kind(
|
||||
&mut self,
|
||||
_block: BasicBlock,
|
||||
kind: &TerminatorKind<'tcx>,
|
||||
location: Location,
|
||||
) {
|
||||
let span = self.mir.source_info(location).span;
|
||||
match kind {
|
||||
TerminatorKind::SwitchInt { discr: value, .. } |
|
||||
TerminatorKind::Yield { value, .. } |
|
||||
TerminatorKind::Assert { cond: value, .. } => {
|
||||
if let Some(value) = self.simplify_operand(value, span) {
|
||||
self.optimizations.const_prop.insert(location, value);
|
||||
}
|
||||
}
|
||||
// FIXME: do this optimization for function calls
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct OptimizationList<'tcx> {
|
||||
and_stars: FxHashSet<Location>,
|
||||
arrays_lengths: FxHashMap<Location, Constant<'tcx>>,
|
||||
const_prop: FxHashMap<Location, Const<'tcx>>,
|
||||
places: FxHashMap<Local, Const<'tcx>>,
|
||||
constants: FxHashMap<Constant<'tcx>, Const<'tcx>>,
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ use llvm::{SetUnnamedAddr};
|
|||
use llvm::{ValueRef, True};
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::hir::map as hir_map;
|
||||
use rustc::middle::const_val::ConstEvalErr;
|
||||
use debuginfo;
|
||||
use base;
|
||||
use monomorphize::MonoItem;
|
||||
|
@ -247,12 +246,15 @@ pub fn get_static(cx: &CodegenCx, def_id: DefId) -> ValueRef {
|
|||
pub fn trans_static<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
|
||||
def_id: DefId,
|
||||
is_mutable: bool,
|
||||
attrs: &[ast::Attribute])
|
||||
-> Result<ValueRef, ConstEvalErr<'tcx>> {
|
||||
attrs: &[ast::Attribute]) {
|
||||
unsafe {
|
||||
let g = get_static(cx, def_id);
|
||||
|
||||
let v = ::mir::trans_static_initializer(cx, def_id)?;
|
||||
let v = match ::mir::trans_static_initializer(cx, def_id) {
|
||||
Ok(v) => v,
|
||||
// Error has already been reported
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
// boolean SSA values are i1, but they have to be stored in i8 slots,
|
||||
// otherwise some LLVM optimization passes don't work as expected
|
||||
|
@ -316,7 +318,5 @@ pub fn trans_static<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
|
|||
let cast = llvm::LLVMConstPointerCast(g, Type::i8p(cx).to_ref());
|
||||
cx.used_statics.borrow_mut().push(cast);
|
||||
}
|
||||
|
||||
Ok(g)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
use llvm::{self, ValueRef, BasicBlockRef};
|
||||
use rustc::middle::lang_items;
|
||||
use rustc::middle::const_val::{ConstEvalErr, ErrKind};
|
||||
use rustc::ty::{self, TypeFoldable};
|
||||
use rustc::ty::layout::{self, LayoutOf};
|
||||
use rustc::traits;
|
||||
|
@ -19,7 +18,7 @@ use abi::{Abi, FnType, ArgType, PassMode};
|
|||
use base;
|
||||
use callee;
|
||||
use builder::Builder;
|
||||
use common::{self, C_bool, C_str_slice, C_struct, C_u32, C_undef};
|
||||
use common::{self, C_bool, C_str_slice, C_struct, C_u32, C_uint_big, C_undef};
|
||||
use consts;
|
||||
use meth;
|
||||
use monomorphize;
|
||||
|
@ -30,7 +29,6 @@ use syntax::symbol::Symbol;
|
|||
use syntax_pos::Pos;
|
||||
|
||||
use super::{FunctionCx, LocalRef};
|
||||
use super::constant::Const;
|
||||
use super::place::PlaceRef;
|
||||
use super::operand::OperandRef;
|
||||
use super::operand::OperandValue::{Pair, Ref, Immediate};
|
||||
|
@ -206,10 +204,11 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> {
|
|||
let (otherwise, targets) = targets.split_last().unwrap();
|
||||
let switch = bx.switch(discr.immediate(),
|
||||
llblock(self, *otherwise), values.len());
|
||||
let switch_llty = bcx.ccx.layout_of(switch_ty).immediate_llvm_type(bcx.ccx);
|
||||
for (&value, target) in values.iter().zip(targets) {
|
||||
let val = Const::from_bytes(bx.cx, value, switch_ty);
|
||||
let llval = C_uint_big(switch_llty, value);
|
||||
let llbb = llblock(self, *target);
|
||||
bx.add_case(switch, val.llval, llbb)
|
||||
bx.add_case(switch, llval, llbb)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -359,10 +358,10 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> {
|
|||
|
||||
let const_err = common::const_to_opt_u128(len, false)
|
||||
.and_then(|len| common::const_to_opt_u128(index, false)
|
||||
.map(|index| ErrKind::IndexOutOfBounds {
|
||||
len: len as u64,
|
||||
index: index as u64
|
||||
}));
|
||||
.map(|index| format!(
|
||||
"index out of bounds: the len is {} but the index is {}",
|
||||
len, index,
|
||||
)));
|
||||
|
||||
let file_line_col = C_struct(bx.cx, &[filename, line, col], false);
|
||||
let file_line_col = consts::addr_of(bx.cx,
|
||||
|
@ -385,7 +384,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> {
|
|||
"panic_loc");
|
||||
(lang_items::PanicFnLangItem,
|
||||
vec![msg_file_line_col],
|
||||
Some(ErrKind::Math(err.clone())))
|
||||
Some(err.description().to_owned()))
|
||||
}
|
||||
mir::AssertMessage::GeneratorResumedAfterReturn |
|
||||
mir::AssertMessage::GeneratorResumedAfterPanic => {
|
||||
|
@ -413,10 +412,11 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> {
|
|||
// is also constant, then we can produce a warning.
|
||||
if const_cond == Some(!expected) {
|
||||
if let Some(err) = const_err {
|
||||
let err = ConstEvalErr{ span: span, kind: err };
|
||||
let mut diag = bx.tcx().sess.struct_span_warn(
|
||||
span, "this expression will panic at run-time");
|
||||
err.note(bx.tcx(), span, "expression", &mut diag);
|
||||
span, &format!(
|
||||
"this expression will panic at run-time with {:?}",
|
||||
err,
|
||||
));
|
||||
diag.emit();
|
||||
}
|
||||
}
|
||||
|
@ -530,10 +530,13 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> {
|
|||
span_bug!(span, "shuffle indices must be constant");
|
||||
}
|
||||
mir::Operand::Constant(ref constant) => {
|
||||
let val = self.trans_constant(&bx, constant);
|
||||
let (llval, ty) = self.remove_me_shuffle_indices(
|
||||
&bx,
|
||||
constant,
|
||||
);
|
||||
return OperandRef {
|
||||
val: Immediate(val.llval),
|
||||
layout: bx.cx.layout_of(val.ty)
|
||||
val: Immediate(llval),
|
||||
layout: bx.cx.layout_of(ty)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -44,6 +44,8 @@ use self::operand::{OperandRef, OperandValue};
|
|||
|
||||
/// Master context for translating MIR.
|
||||
pub struct FunctionCx<'a, 'tcx:'a> {
|
||||
instance: Instance<'tcx>,
|
||||
|
||||
mir: &'a mir::Mir<'tcx>,
|
||||
|
||||
debug_context: debuginfo::FunctionDebugContext,
|
||||
|
@ -227,6 +229,7 @@ pub fn trans_mir<'a, 'tcx: 'a>(
|
|||
let (landing_pads, funclets) = create_funclets(mir, &bx, &cleanup_kinds, &block_bxs);
|
||||
|
||||
let mut fx = FunctionCx {
|
||||
instance,
|
||||
mir,
|
||||
llfn,
|
||||
fn_ty,
|
||||
|
|
|
@ -9,12 +9,14 @@
|
|||
// except according to those terms.
|
||||
|
||||
use llvm::ValueRef;
|
||||
use rustc::ty::layout::{self, Align, LayoutOf, TyLayout};
|
||||
use rustc::middle::const_val::ConstEvalErr;
|
||||
use rustc::mir;
|
||||
use rustc::mir::interpret::Value as MiriValue;
|
||||
use rustc::ty::layout::{self, Align, LayoutOf, TyLayout};
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
|
||||
use base;
|
||||
use common::{self, CodegenCx, C_undef, C_usize};
|
||||
use common::{self, CodegenCx, C_null, C_undef, C_usize};
|
||||
use builder::Builder;
|
||||
use value::Value;
|
||||
use type_of::LayoutLlvmExt;
|
||||
|
@ -24,6 +26,7 @@ use std::fmt;
|
|||
use std::ptr;
|
||||
|
||||
use super::{FunctionCx, LocalRef};
|
||||
use super::constant::{primval_to_llvm};
|
||||
use super::place::PlaceRef;
|
||||
|
||||
/// The representation of a Rust value. The enum variant is in fact
|
||||
|
@ -89,6 +92,70 @@ impl<'a, 'tcx> OperandRef<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn from_const(bcx: &Builder<'a, 'tcx>,
|
||||
miri_val: MiriValue,
|
||||
ty: ty::Ty<'tcx>)
|
||||
-> Result<OperandRef<'tcx>, ConstEvalErr<'tcx>> {
|
||||
let layout = bcx.ccx.layout_of(ty);
|
||||
|
||||
if layout.is_zst() {
|
||||
return Ok(OperandRef::new_zst(bcx.ccx, layout));
|
||||
}
|
||||
|
||||
let val = match miri_val {
|
||||
MiriValue::ByVal(x) => {
|
||||
let scalar = match layout.abi {
|
||||
layout::Abi::Scalar(ref x) => x,
|
||||
_ => bug!("from_const: invalid ByVal layout: {:#?}", layout)
|
||||
};
|
||||
let llval = primval_to_llvm(
|
||||
bcx.ccx,
|
||||
x,
|
||||
scalar,
|
||||
layout.immediate_llvm_type(bcx.ccx),
|
||||
);
|
||||
OperandValue::Immediate(llval)
|
||||
},
|
||||
MiriValue::ByValPair(a, b) => {
|
||||
let (a_scalar, b_scalar) = match layout.abi {
|
||||
layout::Abi::ScalarPair(ref a, ref b) => (a, b),
|
||||
_ => bug!("from_const: invalid ByValPair layout: {:#?}", layout)
|
||||
};
|
||||
let a_llval = primval_to_llvm(
|
||||
bcx.ccx,
|
||||
a,
|
||||
a_scalar,
|
||||
layout.scalar_pair_element_llvm_type(bcx.ccx, 0),
|
||||
);
|
||||
let b_llval = primval_to_llvm(
|
||||
bcx.ccx,
|
||||
b,
|
||||
b_scalar,
|
||||
layout.scalar_pair_element_llvm_type(bcx.ccx, 1),
|
||||
);
|
||||
OperandValue::Pair(a_llval, b_llval)
|
||||
},
|
||||
MiriValue::ByRef(ptr, align) => {
|
||||
let scalar = layout::Scalar {
|
||||
value: layout::Primitive::Pointer,
|
||||
valid_range: 0..=!0
|
||||
};
|
||||
let ptr = primval_to_llvm(
|
||||
bcx.ccx,
|
||||
ptr.into_inner_primval(),
|
||||
&scalar,
|
||||
layout.llvm_type(bcx.ccx).ptr_to(),
|
||||
);
|
||||
return Ok(PlaceRef::new_sized(ptr, layout, align).load(bcx));
|
||||
},
|
||||
};
|
||||
|
||||
Ok(OperandRef {
|
||||
val,
|
||||
layout
|
||||
})
|
||||
}
|
||||
|
||||
/// Asserts that this operand refers to a scalar and returns
|
||||
/// a reference to its value.
|
||||
pub fn immediate(self) -> ValueRef {
|
||||
|
@ -327,14 +394,19 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> {
|
|||
}
|
||||
|
||||
mir::Operand::Constant(ref constant) => {
|
||||
let val = self.trans_constant(&bx, constant);
|
||||
let operand = val.to_operand(bx.cx);
|
||||
if let OperandValue::Ref(ptr, align) = operand.val {
|
||||
// If this is a OperandValue::Ref to an immediate constant, load it.
|
||||
PlaceRef::new_sized(ptr, operand.layout, align).load(bx)
|
||||
} else {
|
||||
operand
|
||||
}
|
||||
let ty = self.monomorphize(&constant.ty);
|
||||
self.mir_constant_to_miri_value(bx, constant)
|
||||
.and_then(|c| OperandRef::from_const(bx, c, ty))
|
||||
.unwrap_or_else(|err| {
|
||||
err.report(bx.tcx(), constant.span, "const operand");
|
||||
// We've errored, so we don't have to produce working code.
|
||||
let layout = bx.cx.layout_of(ty);
|
||||
PlaceRef::new_sized(
|
||||
C_null(layout.llvm_type(bx.cx).ptr_to()),
|
||||
layout,
|
||||
layout.align,
|
||||
).load(bx)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,12 +58,7 @@ pub trait MonoItemExt<'a, 'tcx>: fmt::Debug + BaseMonoItemExt<'a, 'tcx> {
|
|||
};
|
||||
let attrs = tcx.get_attrs(def_id);
|
||||
|
||||
match consts::trans_static(&cx, def_id, is_mutable, &attrs) {
|
||||
Ok(_) => { /* Cool, everything's alright. */ },
|
||||
Err(err) => {
|
||||
err.report(tcx, tcx.def_span(def_id), "static");
|
||||
}
|
||||
};
|
||||
consts::trans_static(&cx, def_id, is_mutable, &attrs);
|
||||
}
|
||||
MonoItem::GlobalAsm(node_id) => {
|
||||
let item = cx.tcx.hir.expect_item(node_id);
|
||||
|
|
|
@ -4013,7 +4013,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
let count = tcx.const_eval(param_env.and(global_id));
|
||||
|
||||
if let Err(ref err) = count {
|
||||
err.report(tcx, tcx.def_span(count_def_id), "constant expression");
|
||||
err.report(tcx, tcx.def_span(count_def_id), "constant expression");
|
||||
}
|
||||
|
||||
let uty = match expected {
|
||||
|
|
|
@ -535,7 +535,7 @@ fn convert_enum_variant_types<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|||
// so we need to report the real error
|
||||
if let Err(ref err) = result {
|
||||
err.report(tcx, variant.span, "enum discriminant");
|
||||
}
|
||||
}
|
||||
|
||||
match result {
|
||||
Ok(&ty::Const {
|
||||
|
|
|
@ -2500,7 +2500,11 @@ impl Clean<Type> for hir::Ty {
|
|||
let def_id = cx.tcx.hir.body_owner_def_id(n);
|
||||
let param_env = cx.tcx.param_env(def_id);
|
||||
let substs = Substs::identity_for_item(cx.tcx, def_id);
|
||||
let n = cx.tcx.const_eval(param_env.and((def_id, substs))).unwrap_or_else(|_| {
|
||||
let cid = GlobalId {
|
||||
instance: ty::Instance::new(def_id, substs),
|
||||
promoted: None
|
||||
};
|
||||
let n = cx.tcx.const_eval(param_env.and(cid)).unwrap_or_else(|_| {
|
||||
cx.tcx.mk_const(ty::Const {
|
||||
val: ConstVal::Unevaluated(def_id, substs),
|
||||
ty: cx.tcx.types.usize
|
||||
|
@ -2633,7 +2637,11 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
|
|||
let mut n = cx.tcx.lift(&n).unwrap();
|
||||
if let ConstVal::Unevaluated(def_id, substs) = n.val {
|
||||
let param_env = cx.tcx.param_env(def_id);
|
||||
if let Ok(new_n) = cx.tcx.const_eval(param_env.and((def_id, substs))) {
|
||||
let cid = GlobalId {
|
||||
instance: ty::Instance::new(def_id, substs),
|
||||
promoted: None
|
||||
};
|
||||
if let Ok(new_n) = cx.tcx.const_eval(param_env.and(cid)) {
|
||||
n = new_n;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -15,4 +15,5 @@ fn f(x: usize) -> usize {
|
|||
fn main() {
|
||||
let _ = [0; f(2)];
|
||||
//~^ ERROR calls in constants are limited to constant functions
|
||||
//~| E0080
|
||||
}
|
||||
|
|
|
@ -11,14 +11,15 @@
|
|||
#![feature(const_indexing)]
|
||||
#![deny(const_err)]
|
||||
|
||||
pub const A: i8 = -std::i8::MIN; //~ ERROR attempt to negate with overflow
|
||||
pub const B: u8 = 200u8 + 200u8; //~ ERROR attempt to add with overflow
|
||||
pub const C: u8 = 200u8 * 4; //~ ERROR attempt to multiply with overflow
|
||||
pub const D: u8 = 42u8 - (42u8 + 1); //~ ERROR attempt to subtract with overflow
|
||||
pub const A: i8 = -std::i8::MIN; //~ ERROR E0080
|
||||
//~^ ERROR attempt to negate with overflow
|
||||
//~| ERROR const_err
|
||||
pub const B: u8 = 200u8 + 200u8; //~ ERROR E0080
|
||||
pub const C: u8 = 200u8 * 4; //~ ERROR E0080
|
||||
pub const D: u8 = 42u8 - (42u8 + 1); //~ ERROR E0080
|
||||
pub const E: u8 = [5u8][1];
|
||||
//~^ ERROR index out of bounds: the len is 1 but the index is 1
|
||||
//~^ ERROR E0080
|
||||
|
||||
fn main() {
|
||||
let _e = [6u8][1];
|
||||
//~^ ERROR index out of bounds: the len is 1 but the index is 1
|
||||
}
|
||||
|
|
|
@ -11,9 +11,14 @@
|
|||
#![deny(const_err)]
|
||||
|
||||
pub const A: i8 = -std::i8::MIN; //~ ERROR attempt to negate with overflow
|
||||
//~^ ERROR E0080
|
||||
//~| ERROR const_err
|
||||
pub const B: i8 = A;
|
||||
//~^ ERROR E0080
|
||||
pub const C: u8 = A as u8;
|
||||
//~^ ERROR E0080
|
||||
pub const D: i8 = 50 - A;
|
||||
//~^ ERROR E0080
|
||||
|
||||
fn main() {
|
||||
}
|
||||
|
|
|
@ -26,26 +26,5 @@ const FOO: u8 = [5u8][1];
|
|||
//~| index out of bounds: the len is 1 but the index is 1
|
||||
|
||||
fn main() {
|
||||
let a = -std::i8::MIN;
|
||||
//~^ WARN this expression will panic at run-time
|
||||
//~| attempt to negate with overflow
|
||||
let b = 200u8 + 200u8 + 200u8;
|
||||
//~^ WARN this expression will panic at run-time
|
||||
//~^^ WARN this expression will panic at run-time
|
||||
//~| attempt to add with overflow
|
||||
let c = 200u8 * 4;
|
||||
//~^ WARN this expression will panic at run-time
|
||||
//~| attempt to multiply with overflow
|
||||
let d = 42u8 - (42u8 + 1);
|
||||
//~^ WARN this expression will panic at run-time
|
||||
//~| attempt to subtract with overflow
|
||||
let _e = [5u8][1];
|
||||
//~^ WARN this expression will panic at run-time
|
||||
//~| index out of bounds: the len is 1 but the index is 1
|
||||
black_box(a);
|
||||
black_box(b);
|
||||
black_box(c);
|
||||
black_box(d);
|
||||
|
||||
black_box((FOO, FOO));
|
||||
}
|
||||
|
|
|
@ -18,13 +18,11 @@ fn black_box<T>(_: T) {
|
|||
|
||||
fn main() {
|
||||
let a = -std::i8::MIN;
|
||||
//~^ ERROR attempt to negate with overflow
|
||||
//~^ ERROR const_err
|
||||
//~| ERROR const_err
|
||||
let b = 200u8 + 200u8 + 200u8;
|
||||
//~^ ERROR attempt to add with overflow
|
||||
let c = 200u8 * 4;
|
||||
//~^ ERROR attempt to multiply with overflow
|
||||
let d = 42u8 - (42u8 + 1);
|
||||
//~^ ERROR attempt to subtract with overflow
|
||||
let _e = [5u8][1];
|
||||
black_box(a);
|
||||
black_box(b);
|
||||
|
|
28
src/test/compile-fail/const-err3.rs
Normal file
28
src/test/compile-fail/const-err3.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
// Copyright 2012 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.
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#![deny(const_err)]
|
||||
|
||||
fn black_box<T>(_: T) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let b = 200u8 + 200u8 + 200u8;
|
||||
//~^ ERROR const_err
|
||||
//~| ERROR const_err
|
||||
let c = 200u8 * 4;
|
||||
let d = 42u8 - (42u8 + 1);
|
||||
let _e = [5u8][1];
|
||||
black_box(b);
|
||||
black_box(c);
|
||||
black_box(d);
|
||||
}
|
|
@ -1,154 +0,0 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
#![allow(unused_imports)]
|
||||
|
||||
// Note: the relevant lint pass here runs before some of the constant
|
||||
// evaluation below (e.g. that performed by trans and llvm), so if you
|
||||
// change this warn to a deny, then the compiler will exit before
|
||||
// those errors are detected.
|
||||
|
||||
#![warn(const_err)]
|
||||
|
||||
use std::fmt;
|
||||
use std::{i8, i16, i32, i64, isize};
|
||||
use std::{u8, u16, u32, u64, usize};
|
||||
|
||||
const VALS_I8: (i8, i8, i8, i8) =
|
||||
(-i8::MIN,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to negate with overflow
|
||||
i8::MIN - 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to subtract with overflow
|
||||
i8::MAX + 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to add with overflow
|
||||
i8::MIN * 2,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to multiply with overflow
|
||||
);
|
||||
|
||||
const VALS_I16: (i16, i16, i16, i16) =
|
||||
(-i16::MIN,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to negate with overflow
|
||||
i16::MIN - 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to subtract with overflow
|
||||
i16::MAX + 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to add with overflow
|
||||
i16::MIN * 2,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to multiply with overflow
|
||||
);
|
||||
|
||||
const VALS_I32: (i32, i32, i32, i32) =
|
||||
(-i32::MIN,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to negate with overflow
|
||||
i32::MIN - 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to subtract with overflow
|
||||
i32::MAX + 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to add with overflow
|
||||
i32::MIN * 2,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to multiply with overflow
|
||||
);
|
||||
|
||||
const VALS_I64: (i64, i64, i64, i64) =
|
||||
(-i64::MIN,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to negate with overflow
|
||||
i64::MIN - 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to subtract with overflow
|
||||
i64::MAX + 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to add with overflow
|
||||
i64::MAX * 2,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to multiply with overflow
|
||||
);
|
||||
|
||||
const VALS_U8: (u8, u8, u8, u8) =
|
||||
( //~ WARN constant evaluation error: attempt to subtract with overflow
|
||||
-(u8::MIN as i8) as u8,
|
||||
u8::MIN - 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to subtract with overflow
|
||||
u8::MAX + 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to add with overflow
|
||||
u8::MAX * 2,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to multiply with overflow
|
||||
);
|
||||
|
||||
const VALS_U16: (u16, u16, u16, u16) =
|
||||
( //~ WARN constant evaluation error: attempt to subtract with overflow
|
||||
-(u16::MIN as i16) as u16,
|
||||
u16::MIN - 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to subtract with overflow
|
||||
u16::MAX + 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to add with overflow
|
||||
u16::MAX * 2,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to multiply with overflow
|
||||
);
|
||||
|
||||
const VALS_U32: (u32, u32, u32, u32) =
|
||||
( //~ WARN constant evaluation error: attempt to subtract with overflow
|
||||
-(u32::MIN as i32) as u32,
|
||||
u32::MIN - 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to subtract with overflow
|
||||
u32::MAX + 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to add with overflow
|
||||
u32::MAX * 2,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to multiply with overflow
|
||||
);
|
||||
|
||||
const VALS_U64: (u64, u64, u64, u64) =
|
||||
( //~ WARN constant evaluation error: attempt to subtract with overflow
|
||||
-(u64::MIN as i64) as u64,
|
||||
u64::MIN - 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to subtract with overflow
|
||||
u64::MAX + 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to add with overflow
|
||||
u64::MAX * 2,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to multiply with overflow
|
||||
);
|
||||
|
||||
fn main() {
|
||||
foo(VALS_I8);
|
||||
foo(VALS_I16);
|
||||
foo(VALS_I32);
|
||||
foo(VALS_I64);
|
||||
|
||||
foo(VALS_U8);
|
||||
foo(VALS_U16);
|
||||
foo(VALS_U32);
|
||||
foo(VALS_U64);
|
||||
}
|
||||
|
||||
fn foo<T:fmt::Debug>(x: T) {
|
||||
println!("{:?}", x);
|
||||
}
|
91
src/test/compile-fail/const-eval-overflow2.rs
Normal file
91
src/test/compile-fail/const-eval-overflow2.rs
Normal file
|
@ -0,0 +1,91 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
#![allow(unused_imports)]
|
||||
|
||||
// Note: the relevant lint pass here runs before some of the constant
|
||||
// evaluation below (e.g. that performed by trans and llvm), so if you
|
||||
// change this warn to a deny, then the compiler will exit before
|
||||
// those errors are detected.
|
||||
|
||||
#![deny(const_err)]
|
||||
|
||||
use std::fmt;
|
||||
use std::{i8, i16, i32, i64, isize};
|
||||
use std::{u8, u16, u32, u64, usize};
|
||||
|
||||
const VALS_I8: (i8,) =
|
||||
(
|
||||
i8::MIN - 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to subtract with overflow
|
||||
);
|
||||
|
||||
const VALS_I16: (i16,) =
|
||||
(
|
||||
i16::MIN - 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to subtract with overflow
|
||||
);
|
||||
|
||||
const VALS_I32: (i32,) =
|
||||
(
|
||||
i32::MIN - 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to subtract with overflow
|
||||
);
|
||||
|
||||
const VALS_I64: (i64,) =
|
||||
(
|
||||
i64::MIN - 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to subtract with overflow
|
||||
);
|
||||
|
||||
const VALS_U8: (u8,) =
|
||||
(
|
||||
u8::MIN - 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to subtract with overflow
|
||||
);
|
||||
|
||||
const VALS_U16: (u16,) = (
|
||||
u16::MIN - 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to subtract with overflow
|
||||
);
|
||||
|
||||
const VALS_U32: (u32,) = (
|
||||
u32::MIN - 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to subtract with overflow
|
||||
);
|
||||
|
||||
const VALS_U64: (u64,) =
|
||||
(
|
||||
u64::MIN - 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to subtract with overflow
|
||||
);
|
||||
|
||||
fn main() {
|
||||
foo(VALS_I8);
|
||||
foo(VALS_I16);
|
||||
foo(VALS_I32);
|
||||
foo(VALS_I64);
|
||||
|
||||
foo(VALS_U8);
|
||||
foo(VALS_U16);
|
||||
foo(VALS_U32);
|
||||
foo(VALS_U64);
|
||||
}
|
||||
|
||||
fn foo<T>(_: T) {
|
||||
}
|
91
src/test/compile-fail/const-eval-overflow2b.rs
Normal file
91
src/test/compile-fail/const-eval-overflow2b.rs
Normal file
|
@ -0,0 +1,91 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
#![allow(unused_imports)]
|
||||
|
||||
// Note: the relevant lint pass here runs before some of the constant
|
||||
// evaluation below (e.g. that performed by trans and llvm), so if you
|
||||
// change this warn to a deny, then the compiler will exit before
|
||||
// those errors are detected.
|
||||
|
||||
#![deny(const_err)]
|
||||
|
||||
use std::fmt;
|
||||
use std::{i8, i16, i32, i64, isize};
|
||||
use std::{u8, u16, u32, u64, usize};
|
||||
|
||||
const VALS_I8: (i8,) =
|
||||
(
|
||||
i8::MAX + 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to add with overflow
|
||||
);
|
||||
|
||||
const VALS_I16: (i16,) =
|
||||
(
|
||||
i16::MAX + 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to add with overflow
|
||||
);
|
||||
|
||||
const VALS_I32: (i32,) =
|
||||
(
|
||||
i32::MAX + 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to add with overflow
|
||||
);
|
||||
|
||||
const VALS_I64: (i64,) =
|
||||
(
|
||||
i64::MAX + 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to add with overflow
|
||||
);
|
||||
|
||||
const VALS_U8: (u8,) =
|
||||
(
|
||||
u8::MAX + 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to add with overflow
|
||||
);
|
||||
|
||||
const VALS_U16: (u16,) = (
|
||||
u16::MAX + 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to add with overflow
|
||||
);
|
||||
|
||||
const VALS_U32: (u32,) = (
|
||||
u32::MAX + 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to add with overflow
|
||||
);
|
||||
|
||||
const VALS_U64: (u64,) =
|
||||
(
|
||||
u64::MAX + 1,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to add with overflow
|
||||
);
|
||||
|
||||
fn main() {
|
||||
foo(VALS_I8);
|
||||
foo(VALS_I16);
|
||||
foo(VALS_I32);
|
||||
foo(VALS_I64);
|
||||
|
||||
foo(VALS_U8);
|
||||
foo(VALS_U16);
|
||||
foo(VALS_U32);
|
||||
foo(VALS_U64);
|
||||
}
|
||||
|
||||
fn foo<T>(_: T) {
|
||||
}
|
91
src/test/compile-fail/const-eval-overflow2c.rs
Normal file
91
src/test/compile-fail/const-eval-overflow2c.rs
Normal file
|
@ -0,0 +1,91 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
#![allow(unused_imports)]
|
||||
|
||||
// Note: the relevant lint pass here runs before some of the constant
|
||||
// evaluation below (e.g. that performed by trans and llvm), so if you
|
||||
// change this warn to a deny, then the compiler will exit before
|
||||
// those errors are detected.
|
||||
|
||||
#![deny(const_err)]
|
||||
|
||||
use std::fmt;
|
||||
use std::{i8, i16, i32, i64, isize};
|
||||
use std::{u8, u16, u32, u64, usize};
|
||||
|
||||
const VALS_I8: (i8,) =
|
||||
(
|
||||
i8::MIN * 2,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to multiply with overflow
|
||||
);
|
||||
|
||||
const VALS_I16: (i16,) =
|
||||
(
|
||||
i16::MIN * 2,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to multiply with overflow
|
||||
);
|
||||
|
||||
const VALS_I32: (i32,) =
|
||||
(
|
||||
i32::MIN * 2,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to multiply with overflow
|
||||
);
|
||||
|
||||
const VALS_I64: (i64,) =
|
||||
(
|
||||
i64::MIN * 2,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to multiply with overflow
|
||||
);
|
||||
|
||||
const VALS_U8: (u8,) =
|
||||
(
|
||||
u8::MAX * 2,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to multiply with overflow
|
||||
);
|
||||
|
||||
const VALS_U16: (u16,) = (
|
||||
u16::MAX * 2,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to multiply with overflow
|
||||
);
|
||||
|
||||
const VALS_U32: (u32,) = (
|
||||
u32::MAX * 2,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to multiply with overflow
|
||||
);
|
||||
|
||||
const VALS_U64: (u64,) =
|
||||
(
|
||||
u64::MAX * 2,
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to multiply with overflow
|
||||
);
|
||||
|
||||
fn main() {
|
||||
foo(VALS_I8);
|
||||
foo(VALS_I16);
|
||||
foo(VALS_I32);
|
||||
foo(VALS_I64);
|
||||
|
||||
foo(VALS_U8);
|
||||
foo(VALS_U16);
|
||||
foo(VALS_U32);
|
||||
foo(VALS_U64);
|
||||
}
|
||||
|
||||
fn foo<T>(_: T) {
|
||||
}
|
|
@ -16,6 +16,7 @@ const X: usize = 42 && 39;
|
|||
//~| ERROR mismatched types
|
||||
//~| expected usize, found bool
|
||||
const ARR: [i32; X] = [99; 34];
|
||||
//~^ ERROR constant evaluation error
|
||||
|
||||
const X1: usize = 42 || 39;
|
||||
//~^ ERROR mismatched types
|
||||
|
@ -25,6 +26,7 @@ const X1: usize = 42 || 39;
|
|||
//~| ERROR mismatched types
|
||||
//~| expected usize, found bool
|
||||
const ARR1: [i32; X1] = [99; 47];
|
||||
//~^ ERROR constant evaluation error
|
||||
|
||||
const X2: usize = -42 || -39;
|
||||
//~^ ERROR mismatched types
|
||||
|
@ -34,6 +36,7 @@ const X2: usize = -42 || -39;
|
|||
//~| ERROR mismatched types
|
||||
//~| expected usize, found bool
|
||||
const ARR2: [i32; X2] = [99; 18446744073709551607];
|
||||
//~^ ERROR constant evaluation error
|
||||
|
||||
const X3: usize = -42 && -39;
|
||||
//~^ ERROR mismatched types
|
||||
|
@ -43,36 +46,43 @@ const X3: usize = -42 && -39;
|
|||
//~| ERROR mismatched types
|
||||
//~| expected usize, found bool
|
||||
const ARR3: [i32; X3] = [99; 6];
|
||||
//~^ ERROR constant evaluation error
|
||||
|
||||
const Y: usize = 42.0 == 42.0;
|
||||
//~^ ERROR mismatched types
|
||||
//~| expected usize, found bool
|
||||
const ARRR: [i32; Y] = [99; 1];
|
||||
//~^ ERROR constant evaluation error
|
||||
|
||||
const Y1: usize = 42.0 >= 42.0;
|
||||
//~^ ERROR mismatched types
|
||||
//~| expected usize, found bool
|
||||
const ARRR1: [i32; Y1] = [99; 1];
|
||||
//~^ ERROR constant evaluation error
|
||||
|
||||
const Y2: usize = 42.0 <= 42.0;
|
||||
//~^ ERROR mismatched types
|
||||
//~| expected usize, found bool
|
||||
const ARRR2: [i32; Y2] = [99; 1];
|
||||
//~^ ERROR constant evaluation error
|
||||
|
||||
const Y3: usize = 42.0 > 42.0;
|
||||
//~^ ERROR mismatched types
|
||||
//~| expected usize, found bool
|
||||
const ARRR3: [i32; Y3] = [99; 0];
|
||||
//~^ ERROR constant evaluation error
|
||||
|
||||
const Y4: usize = 42.0 < 42.0;
|
||||
//~^ ERROR mismatched types
|
||||
//~| expected usize, found bool
|
||||
const ARRR4: [i32; Y4] = [99; 0];
|
||||
//~^ ERROR constant evaluation error
|
||||
|
||||
const Y5: usize = 42.0 != 42.0;
|
||||
//~^ ERROR mismatched types
|
||||
//~| expected usize, found bool
|
||||
const ARRR5: [i32; Y5] = [99; 0];
|
||||
//~^ ERROR constant evaluation error
|
||||
|
||||
fn main() {
|
||||
let _ = ARR;
|
||||
|
|
|
@ -16,6 +16,6 @@ const TWO: usize = 2;
|
|||
|
||||
fn main() {
|
||||
let a: [i8; ONE - TWO] = unimplemented!();
|
||||
//~^ ERROR constant evaluation error [E0080]
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| attempt to subtract with overflow
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#[deny(const_err)]
|
||||
|
||||
const FOO: &'static[u32] = &[1, 2, 3];
|
||||
const BAR: u32 = FOO[5];
|
||||
//~^ ERROR constant evaluation error [E0080]
|
||||
|
|
|
@ -14,6 +14,7 @@ const TUP: (usize,) = 5usize << 64;
|
|||
//~^ ERROR mismatched types
|
||||
//~| expected tuple, found usize
|
||||
const ARR: [i32; TUP.0] = [];
|
||||
//~^ ERROR constant evaluation error
|
||||
|
||||
fn main() {
|
||||
}
|
||||
|
|
|
@ -8,12 +8,15 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
enum test {
|
||||
div_zero = 1/0, //~ ERROR E0080
|
||||
//~| attempt to divide by zero
|
||||
rem_zero = 1%0,
|
||||
//~^ ERROR E0080
|
||||
//~| attempt to calculate the remainder with a divisor of zero
|
||||
enum Test {
|
||||
DivZero = 1/0,
|
||||
//~^ attempt to divide by zero
|
||||
//~| ERROR constant evaluation error
|
||||
//~| WARN constant evaluation error
|
||||
RemZero = 1%0,
|
||||
//~^ attempt to calculate the remainder with a divisor of zero
|
||||
//~| ERROR constant evaluation error
|
||||
//~| WARN constant evaluation error
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
// Copyright 2017 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.
|
||||
|
||||
#![feature(i128_type)]
|
||||
#![allow(const_err)] // this test is only about hard errors
|
||||
|
||||
use std::{f32, f64};
|
||||
|
||||
// Forces evaluation of constants, triggering hard error
|
||||
fn force<T>(_: T) {}
|
||||
|
||||
fn main() {
|
||||
{ const X: u16 = -1. as u16; force(X); } //~ ERROR constant evaluation error
|
||||
{ const X: u128 = -100. as u128; force(X); } //~ ERROR constant evaluation error
|
||||
|
||||
{ const X: i8 = f32::NAN as i8; force(X); } //~ ERROR constant evaluation error
|
||||
{ const X: i32 = f32::NAN as i32; force(X); } //~ ERROR constant evaluation error
|
||||
{ const X: u64 = f32::NAN as u64; force(X); } //~ ERROR constant evaluation error
|
||||
{ const X: u128 = f32::NAN as u128; force(X); } //~ ERROR constant evaluation error
|
||||
|
||||
{ const X: i8 = f32::INFINITY as i8; force(X); } //~ ERROR constant evaluation error
|
||||
{ const X: u32 = f32::INFINITY as u32; force(X); } //~ ERROR constant evaluation error
|
||||
{ const X: i128 = f32::INFINITY as i128; force(X); } //~ ERROR constant evaluation error
|
||||
{ const X: u128 = f32::INFINITY as u128; force(X); } //~ ERROR constant evaluation error
|
||||
|
||||
{ const X: u8 = f32::NEG_INFINITY as u8; force(X); } //~ ERROR constant evaluation error
|
||||
{ const X: u16 = f32::NEG_INFINITY as u16; force(X); } //~ ERROR constant evaluation error
|
||||
{ const X: i64 = f32::NEG_INFINITY as i64; force(X); } //~ ERROR constant evaluation error
|
||||
{ const X: i128 = f32::NEG_INFINITY as i128; force(X); } //~ ERROR constant evaluation error
|
||||
|
||||
{ const X: i8 = f64::NAN as i8; force(X); } //~ ERROR constant evaluation error
|
||||
{ const X: i32 = f64::NAN as i32; force(X); } //~ ERROR constant evaluation error
|
||||
{ const X: u64 = f64::NAN as u64; force(X); } //~ ERROR constant evaluation error
|
||||
{ const X: u128 = f64::NAN as u128; force(X); } //~ ERROR constant evaluation error
|
||||
|
||||
{ const X: i8 = f64::INFINITY as i8; force(X); } //~ ERROR constant evaluation error
|
||||
{ const X: u32 = f64::INFINITY as u32; force(X); } //~ ERROR constant evaluation error
|
||||
{ const X: i128 = f64::INFINITY as i128; force(X); } //~ ERROR constant evaluation error
|
||||
{ const X: u128 = f64::INFINITY as u128; force(X); } //~ ERROR constant evaluation error
|
||||
|
||||
{ const X: u8 = f64::NEG_INFINITY as u8; force(X); } //~ ERROR constant evaluation error
|
||||
{ const X: u16 = f64::NEG_INFINITY as u16; force(X); } //~ ERROR constant evaluation error
|
||||
{ const X: i64 = f64::NEG_INFINITY as i64; force(X); } //~ ERROR constant evaluation error
|
||||
{ const X: i128 = f64::NEG_INFINITY as i128; force(X); } //~ ERROR constant evaluation error
|
||||
|
||||
{ const X: u8 = 256. as u8; force(X); } //~ ERROR constant evaluation error
|
||||
{ const X: i8 = -129. as i8; force(X); } //~ ERROR constant evaluation error
|
||||
{ const X: i8 = 128. as i8; force(X); } //~ ERROR constant evaluation error
|
||||
{ const X: i32 = 2147483648. as i32; force(X); } //~ ERROR constant evaluation error
|
||||
{ const X: i32 = -2147483904. as i32; force(X); } //~ ERROR constant evaluation error
|
||||
{ const X: u32 = 4294967296. as u32; force(X); } //~ ERROR constant evaluation error
|
||||
{ const X: u128 = 1e40 as u128; force(X); } //~ ERROR constant evaluation error
|
||||
{ const X: i128 = 1e40 as i128; force(X); } //~ ERROR constant evaluation error
|
||||
}
|
|
@ -14,8 +14,7 @@ fn main() {
|
|||
|
||||
match i {
|
||||
0...index => println!("winner"),
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| non-constant path in constant expression
|
||||
//~^ ERROR runtime values cannot be referenced in patterns
|
||||
_ => println!("hello"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,33 +18,33 @@
|
|||
fn main() {
|
||||
let x = 42.0;
|
||||
match x {
|
||||
5.0 => {}, //~ ERROR floating-point cannot be used
|
||||
5.0 => {}, //~ ERROR floating-point types cannot be used in patterns
|
||||
//~| WARNING hard error
|
||||
5.0f32 => {}, //~ ERROR floating-point cannot be used
|
||||
5.0f32 => {}, //~ ERROR floating-point types cannot be used in patterns
|
||||
//~| WARNING hard error
|
||||
-5.0 => {}, //~ ERROR floating-point cannot be used
|
||||
-5.0 => {}, //~ ERROR floating-point types cannot be used in patterns
|
||||
//~| WARNING hard error
|
||||
1.0 .. 33.0 => {}, //~ ERROR floating-point cannot be used
|
||||
1.0 .. 33.0 => {}, //~ ERROR floating-point types cannot be used in patterns
|
||||
//~| WARNING hard error
|
||||
//~| ERROR floating-point cannot be used
|
||||
//~| ERROR floating-point types cannot be used in patterns
|
||||
//~| WARNING hard error
|
||||
39.0 ... 70.0 => {}, //~ ERROR floating-point cannot be used
|
||||
39.0 ... 70.0 => {}, //~ ERROR floating-point types cannot be used in patterns
|
||||
//~| WARNING hard error
|
||||
//~| ERROR floating-point cannot be used
|
||||
//~| ERROR floating-point types cannot be used in patterns
|
||||
//~| WARNING hard error
|
||||
_ => {},
|
||||
};
|
||||
let y = 5.0;
|
||||
// Same for tuples
|
||||
match (x, 5) {
|
||||
(3.14, 1) => {}, //~ ERROR floating-point cannot be used
|
||||
(3.14, 1) => {}, //~ ERROR floating-point types cannot be used
|
||||
//~| WARNING hard error
|
||||
_ => {},
|
||||
}
|
||||
// Or structs
|
||||
struct Foo { x: f32 };
|
||||
match (Foo { x }) {
|
||||
Foo { x: 2.0 } => {}, //~ ERROR floating-point cannot be used
|
||||
Foo { x: 2.0 } => {}, //~ ERROR floating-point types cannot be used
|
||||
//~| WARNING hard error
|
||||
_ => {},
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ enum Bar<A, B> {
|
|||
}
|
||||
|
||||
impl<A: Foo, B: Foo> Foo for Bar<A, B> {
|
||||
const AMT: usize = [A::AMT][(A::AMT > B::AMT) as usize]; //~ ERROR constant evaluation
|
||||
const AMT: usize = [A::AMT][(A::AMT > B::AMT) as usize]; //~ E0080
|
||||
}
|
||||
|
||||
impl Foo for u8 {
|
||||
|
@ -30,5 +30,5 @@ impl Foo for u16 {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
println!("{}", <Bar<u16, u8> as Foo>::AMT);
|
||||
println!("{}", <Bar<u16, u8> as Foo>::AMT); //~ E0080
|
||||
}
|
||||
|
|
|
@ -12,18 +12,21 @@
|
|||
|
||||
#![feature(slice_patterns)]
|
||||
#![allow(unused)]
|
||||
#![deny(illegal_floating_point_literal_pattern)]
|
||||
|
||||
use std::f64::NAN;
|
||||
|
||||
fn main() {
|
||||
let x = NAN;
|
||||
match x {
|
||||
NAN => {}, //~ ERROR floating point constants cannot be used
|
||||
NAN => {}, //~ ERROR floating-point types cannot be used
|
||||
//~^ WARN this was previously accepted by the compiler but is being phased out
|
||||
_ => {},
|
||||
};
|
||||
|
||||
match [x, 1.0] {
|
||||
[NAN, _] => {}, //~ ERROR floating point constants cannot be used
|
||||
[NAN, _] => {}, //~ ERROR floating-point types cannot be used
|
||||
//~^ WARN this was previously accepted by the compiler but is being phased out
|
||||
_ => {},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -16,42 +16,62 @@ use std::thread;
|
|||
fn main() {
|
||||
assert!(thread::spawn(move|| { isize::MIN / -1; }).join().is_err());
|
||||
//~^ ERROR attempt to divide with overflow
|
||||
//~| ERROR constant evaluation error
|
||||
assert!(thread::spawn(move|| { i8::MIN / -1; }).join().is_err());
|
||||
//~^ ERROR attempt to divide with overflow
|
||||
//~| ERROR constant evaluation error
|
||||
assert!(thread::spawn(move|| { i16::MIN / -1; }).join().is_err());
|
||||
//~^ ERROR attempt to divide with overflow
|
||||
//~| ERROR constant evaluation error
|
||||
assert!(thread::spawn(move|| { i32::MIN / -1; }).join().is_err());
|
||||
//~^ ERROR attempt to divide with overflow
|
||||
//~| ERROR constant evaluation error
|
||||
assert!(thread::spawn(move|| { i64::MIN / -1; }).join().is_err());
|
||||
//~^ ERROR attempt to divide with overflow
|
||||
//~| ERROR constant evaluation error
|
||||
assert!(thread::spawn(move|| { 1isize / 0; }).join().is_err());
|
||||
//~^ ERROR attempt to divide by zero
|
||||
//~| ERROR constant evaluation error
|
||||
assert!(thread::spawn(move|| { 1i8 / 0; }).join().is_err());
|
||||
//~^ ERROR attempt to divide by zero
|
||||
//~| ERROR constant evaluation error
|
||||
assert!(thread::spawn(move|| { 1i16 / 0; }).join().is_err());
|
||||
//~^ ERROR attempt to divide by zero
|
||||
//~| ERROR constant evaluation error
|
||||
assert!(thread::spawn(move|| { 1i32 / 0; }).join().is_err());
|
||||
//~^ ERROR attempt to divide by zero
|
||||
//~| ERROR constant evaluation error
|
||||
assert!(thread::spawn(move|| { 1i64 / 0; }).join().is_err());
|
||||
//~^ ERROR attempt to divide by zero
|
||||
//~| ERROR constant evaluation error
|
||||
assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err());
|
||||
//~^ ERROR attempt to calculate the remainder with overflow
|
||||
//~| ERROR constant evaluation error
|
||||
assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err());
|
||||
//~^ ERROR attempt to calculate the remainder with overflow
|
||||
//~| ERROR constant evaluation error
|
||||
assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err());
|
||||
//~^ ERROR attempt to calculate the remainder with overflow
|
||||
//~| ERROR constant evaluation error
|
||||
assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err());
|
||||
//~^ ERROR attempt to calculate the remainder with overflow
|
||||
//~| ERROR constant evaluation error
|
||||
assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err());
|
||||
//~^ ERROR attempt to calculate the remainder with overflow
|
||||
//~| ERROR constant evaluation error
|
||||
assert!(thread::spawn(move|| { 1isize % 0; }).join().is_err());
|
||||
//~^ ERROR attempt to calculate the remainder with a divisor of zero
|
||||
//~| ERROR constant evaluation error
|
||||
assert!(thread::spawn(move|| { 1i8 % 0; }).join().is_err());
|
||||
//~^ ERROR attempt to calculate the remainder with a divisor of zero
|
||||
//~| ERROR constant evaluation error
|
||||
assert!(thread::spawn(move|| { 1i16 % 0; }).join().is_err());
|
||||
//~^ ERROR attempt to calculate the remainder with a divisor of zero
|
||||
//~| ERROR constant evaluation error
|
||||
assert!(thread::spawn(move|| { 1i32 % 0; }).join().is_err());
|
||||
//~^ ERROR attempt to calculate the remainder with a divisor of zero
|
||||
//~| ERROR constant evaluation error
|
||||
assert!(thread::spawn(move|| { 1i64 % 0; }).join().is_err());
|
||||
//~^ ERROR attempt to calculate the remainder with a divisor of zero
|
||||
//~| ERROR constant evaluation error
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
#![deny(exceeding_bitshifts)]
|
||||
#![allow(unused_variables)]
|
||||
#![allow(dead_code)]
|
||||
#![allow(dead_code, const_err)]
|
||||
#![feature(const_indexing)]
|
||||
|
||||
fn main() {
|
||||
|
@ -54,21 +54,5 @@ fn main() {
|
|||
|
||||
let n = 1u8 << -8; //~ ERROR: bitshift exceeds the type's number of bits
|
||||
|
||||
|
||||
let n = 1u8 << (4+3);
|
||||
let n = 1u8 << (4+4); //~ ERROR: bitshift exceeds the type's number of bits
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
const BITS: usize = 32;
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
const BITS: usize = 64;
|
||||
|
||||
let n = 1_isize << BITS; //~ ERROR: bitshift exceeds the type's number of bits
|
||||
let n = 1_usize << BITS; //~ ERROR: bitshift exceeds the type's number of bits
|
||||
|
||||
|
||||
let n = 1i8<<(1isize+-1);
|
||||
|
||||
let n = 1i64 >> [63][0];
|
||||
let n = 1i64 >> [64][0]; //~ ERROR: bitshift exceeds the type's number of bits
|
||||
}
|
||||
|
|
27
src/test/compile-fail/lint-exceeding-bitshifts2.rs
Normal file
27
src/test/compile-fail/lint-exceeding-bitshifts2.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
#![deny(exceeding_bitshifts)]
|
||||
#![allow(unused_variables, const_err)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
fn main() {
|
||||
let n = 1u8 << (4+3);
|
||||
let n = 1u8 << (4+4); //~ ERROR: bitshift exceeds the type's number of bits
|
||||
let n = 1i64 >> [63][0];
|
||||
let n = 1i64 >> [64][0]; // should be linting, needs to wait for const propagation
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
const BITS: usize = 32;
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
const BITS: usize = 64;
|
||||
let n = 1_isize << BITS; //~ ERROR: bitshift exceeds the type's number of bits
|
||||
let n = 1_usize << BITS; //~ ERROR: bitshift exceeds the type's number of bits
|
||||
}
|
|
@ -17,6 +17,7 @@
|
|||
#[rustc_error]
|
||||
fn main() { //~ ERROR: compilation successful
|
||||
let x2: i8 = --128; //~ warn: literal out of range for i8
|
||||
//~^ WARN constant evaluation error
|
||||
|
||||
let x = -3.40282357e+38_f32; //~ warn: literal out of range for f32
|
||||
let x = 3.40282357e+38_f32; //~ warn: literal out of range for f32
|
||||
|
|
|
@ -12,7 +12,6 @@ fn main() {
|
|||
let x = 0;
|
||||
match 1 {
|
||||
0 ... x => {}
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| non-constant path in constant expression
|
||||
//~^ ERROR runtime values cannot be referenced in patterns
|
||||
};
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ fn main() {
|
|||
let x = 0.0;
|
||||
match x {
|
||||
f32::INFINITY => { }
|
||||
//~^ WARNING floating-point cannot be used in patterns
|
||||
//~^ WARNING floating-point types cannot be used in patterns
|
||||
//~| WARNING will become a hard error in a future release
|
||||
_ => { }
|
||||
}
|
||||
|
|
|
@ -9,8 +9,7 @@
|
|||
// except according to those terms.
|
||||
|
||||
const ARR: [usize; 1] = [2];
|
||||
const ARR2: [i32; ARR[0]] = [5, 6]; //~ ERROR E0080
|
||||
//~| unstable
|
||||
const ARR2: [i32; ARR[0]] = [5, 6];
|
||||
|
||||
fn main() {
|
||||
}
|
61
src/test/run-pass/float-int-invalid-const-cast.rs
Normal file
61
src/test/run-pass/float-int-invalid-const-cast.rs
Normal file
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2017 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.
|
||||
|
||||
#![feature(i128_type)]
|
||||
#![deny(const_err)]
|
||||
|
||||
use std::{f32, f64};
|
||||
|
||||
// Forces evaluation of constants, triggering hard error
|
||||
fn force<T>(_: T) {}
|
||||
|
||||
fn main() {
|
||||
{ const X: u16 = -1. as u16; force(X); }
|
||||
{ const X: u128 = -100. as u128; force(X); }
|
||||
|
||||
{ const X: i8 = f32::NAN as i8; force(X); }
|
||||
{ const X: i32 = f32::NAN as i32; force(X); }
|
||||
{ const X: u64 = f32::NAN as u64; force(X); }
|
||||
{ const X: u128 = f32::NAN as u128; force(X); }
|
||||
|
||||
{ const X: i8 = f32::INFINITY as i8; force(X); }
|
||||
{ const X: u32 = f32::INFINITY as u32; force(X); }
|
||||
{ const X: i128 = f32::INFINITY as i128; force(X); }
|
||||
{ const X: u128 = f32::INFINITY as u128; force(X); }
|
||||
|
||||
{ const X: u8 = f32::NEG_INFINITY as u8; force(X); }
|
||||
{ const X: u16 = f32::NEG_INFINITY as u16; force(X); }
|
||||
{ const X: i64 = f32::NEG_INFINITY as i64; force(X); }
|
||||
{ const X: i128 = f32::NEG_INFINITY as i128; force(X); }
|
||||
|
||||
{ const X: i8 = f64::NAN as i8; force(X); }
|
||||
{ const X: i32 = f64::NAN as i32; force(X); }
|
||||
{ const X: u64 = f64::NAN as u64; force(X); }
|
||||
{ const X: u128 = f64::NAN as u128; force(X); }
|
||||
|
||||
{ const X: i8 = f64::INFINITY as i8; force(X); }
|
||||
{ const X: u32 = f64::INFINITY as u32; force(X); }
|
||||
{ const X: i128 = f64::INFINITY as i128; force(X); }
|
||||
{ const X: u128 = f64::INFINITY as u128; force(X); }
|
||||
|
||||
{ const X: u8 = f64::NEG_INFINITY as u8; force(X); }
|
||||
{ const X: u16 = f64::NEG_INFINITY as u16; force(X); }
|
||||
{ const X: i64 = f64::NEG_INFINITY as i64; force(X); }
|
||||
{ const X: i128 = f64::NEG_INFINITY as i128; force(X); }
|
||||
|
||||
{ const X: u8 = 256. as u8; force(X); }
|
||||
{ const X: i8 = -129. as i8; force(X); }
|
||||
{ const X: i8 = 128. as i8; force(X); }
|
||||
{ const X: i32 = 2147483648. as i32; force(X); }
|
||||
{ const X: i32 = -2147483904. as i32; force(X); }
|
||||
{ const X: u32 = 4294967296. as u32; force(X); }
|
||||
{ const X: u128 = 1e40 as u128; force(X); }
|
||||
{ const X: i128 = 1e40 as i128; force(X); }
|
||||
}
|
|
@ -18,4 +18,6 @@ fn main() {
|
|||
const X: u32 = 0-1; //~ ERROR constant evaluation error
|
||||
const Y: u32 = foo(0-1); //~ ERROR constant evaluation error
|
||||
println!("{} {}", X, Y);
|
||||
//~^ ERROR constant evaluation error
|
||||
//~| ERROR constant evaluation error
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
const ONE: usize = 1;
|
||||
const TWO: usize = 2;
|
||||
const LEN: usize = ONE - TWO;
|
||||
//~^ ERROR E0080
|
||||
|
||||
fn main() {
|
||||
let a: [i8; LEN] = unimplemented!();
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
|
||||
fn main() {
|
||||
match 5u32 {
|
||||
match 5u32 { //~ ERROR non-exhaustive patterns
|
||||
1000 ... 5 => {}
|
||||
//~^ ERROR lower range bound must be less than or equal to upper
|
||||
}
|
||||
|
|
|
@ -10,9 +10,10 @@
|
|||
|
||||
enum Enum {
|
||||
X = (1 << 500), //~ ERROR E0080
|
||||
//~| WARNING shift left with overflow
|
||||
//~| shift left with overflow
|
||||
Y = (1 / 0) //~ ERROR E0080
|
||||
//~| WARNING divide by zero
|
||||
//~| const_err
|
||||
//~| divide by zero
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
|
Loading…
Add table
Reference in a new issue