Fully use miri in trans

This commit is contained in:
Oliver Schneider 2018-01-16 09:31:48 +01:00
parent b2b101befc
commit b33e4e784e
No known key found for this signature in database
GPG key ID: A69F8D225B3AD7D9
78 changed files with 1935 additions and 2186 deletions

View file

@ -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)
}

View file

@ -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) =>

View file

@ -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,
}
}
}

View file

@ -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),

View file

@ -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")

View file

@ -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"),
})))
}
},

View file

@ -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};

View file

@ -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(

View file

@ -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>,

View file

@ -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;

View file

@ -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
}
}
}

View file

@ -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;

View file

@ -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);
}

View file

@ -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)?;

View file

@ -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

View file

@ -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.

View file

@ -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();
}
}
}

View file

@ -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> {

View file

@ -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
},
}

View file

@ -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(..) |

View file

@ -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 {

View file

@ -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,

View file

@ -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,

View file

@ -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,
}
})
}

View file

@ -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())

View file

@ -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> {

View file

@ -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

View file

@ -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};

View file

@ -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,

View file

@ -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,
}
}
}

View file

@ -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)
}
});
}
}

View file

@ -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,

View file

@ -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;

View file

@ -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)

View file

@ -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>,

View file

@ -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;

View file

@ -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>>,
}

View file

@ -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)
}
}

View file

@ -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

View file

@ -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,

View file

@ -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)
})
}
}
}

View file

@ -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);

View file

@ -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 {

View file

@ -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 {

View file

@ -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;
}
};

View file

@ -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
}

View file

@ -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
}

View file

@ -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() {
}

View file

@ -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));
}

View file

@ -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);

View 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);
}

View file

@ -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);
}

View 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) {
}

View 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) {
}

View 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) {
}

View file

@ -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;

View file

@ -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
}

View file

@ -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]

View file

@ -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() {
}

View file

@ -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() {}

View file

@ -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
}

View file

@ -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"),
}
}

View file

@ -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
_ => {},
}

View file

@ -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
}

View file

@ -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
_ => {},
};
}

View file

@ -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
}

View file

@ -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
}

View 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
}

View file

@ -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

View file

@ -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
};
}

View file

@ -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
_ => { }
}

View file

@ -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() {
}

View 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); }
}

View file

@ -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
}

View file

@ -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!();

View file

@ -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
}

View file

@ -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() {