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