Nuke the entire ctfe from orbit, it's the only way to be sure

This commit is contained in:
Oliver Schneider 2018-01-16 09:28:27 +01:00
parent 0b9db5716f
commit 28572d2c1f
No known key found for this signature in database
GPG key ID: A69F8D225B3AD7D9
53 changed files with 629 additions and 2066 deletions

View file

@ -338,53 +338,10 @@ for ::middle::const_val::ConstVal<'gcx> {
hcx: &mut StableHashingContext<'gcx>,
hasher: &mut StableHasher<W>) {
use middle::const_val::ConstVal::*;
use middle::const_val::ConstAggregate::*;
mem::discriminant(self).hash_stable(hcx, hasher);
match *self {
Integral(ref value) => {
value.hash_stable(hcx, hasher);
}
Float(ref value) => {
value.hash_stable(hcx, hasher);
}
Str(ref value) => {
value.hash_stable(hcx, hasher);
}
ByteStr(ref value) => {
value.hash_stable(hcx, hasher);
}
Bool(value) => {
value.hash_stable(hcx, hasher);
}
Char(value) => {
value.hash_stable(hcx, hasher);
}
Variant(def_id) => {
def_id.hash_stable(hcx, hasher);
}
Function(def_id, substs) => {
def_id.hash_stable(hcx, hasher);
hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| {
substs.hash_stable(hcx, hasher);
});
}
Aggregate(Struct(ref name_values)) => {
let mut values = name_values.to_vec();
values.sort_unstable_by_key(|&(ref name, _)| name.clone());
values.hash_stable(hcx, hasher);
}
Aggregate(Tuple(ref value)) => {
value.hash_stable(hcx, hasher);
}
Aggregate(Array(ref value)) => {
value.hash_stable(hcx, hasher);
}
Aggregate(Repeat(ref value, times)) => {
value.hash_stable(hcx, hasher);
times.hash_stable(hcx, hasher);
}
Unevaluated(def_id, substs) => {
def_id.hash_stable(hcx, hasher);
substs.hash_stable(hcx, hasher);

View file

@ -20,6 +20,12 @@ use session::Session;
use session::config::Epoch;
use syntax::codemap::Span;
declare_lint! {
pub EXCEEDING_BITSHIFTS,
Deny,
"shift exceeds the type's number of bits"
}
declare_lint! {
pub CONST_ERR,
Warn,
@ -263,6 +269,12 @@ declare_lint! {
Epoch::Epoch2018
}
declare_lint! {
pub ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
Warn,
"floating-point literals cannot be used in patterns"
}
/// Does nothing as a lint pass, but registers some `Lint`s
/// which are used by other parts of the compiler.
#[derive(Copy, Clone)]
@ -271,6 +283,8 @@ pub struct HardwiredLints;
impl LintPass for HardwiredLints {
fn get_lints(&self) -> LintArray {
lint_array!(
ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
EXCEEDING_BITSHIFTS,
UNUSED_IMPORTS,
UNUSED_EXTERN_CRATES,
UNUSED_QUALIFICATIONS,

View file

@ -18,9 +18,7 @@ use mir::interpret::{Value, PrimVal};
use graphviz::IntoCow;
use errors::DiagnosticBuilder;
use serialize::{self, Encodable, Encoder, Decodable, Decoder};
use syntax::symbol::InternedString;
use syntax::ast;
use serialize;
use syntax_pos::Span;
use std::borrow::Cow;
@ -29,17 +27,7 @@ pub type EvalResult<'tcx> = Result<&'tcx ty::Const<'tcx>, ConstEvalErr<'tcx>>;
#[derive(Copy, Clone, Debug, Hash, RustcEncodable, RustcDecodable, Eq, PartialEq)]
pub enum ConstVal<'tcx> {
Integral(ConstInt),
Float(ConstFloat),
Str(InternedString),
ByteStr(ByteArray<'tcx>),
Bool(bool),
Char(char),
Variant(DefId),
Function(DefId, &'tcx Substs<'tcx>),
Aggregate(ConstAggregate<'tcx>),
Unevaluated(DefId, &'tcx Substs<'tcx>),
/// A miri value, currently only produced if --miri is enabled
Value(Value),
}
@ -50,32 +38,9 @@ pub struct ByteArray<'tcx> {
impl<'tcx> serialize::UseSpecializedDecodable for ByteArray<'tcx> {}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum ConstAggregate<'tcx> {
Struct(&'tcx [(ast::Name, &'tcx ty::Const<'tcx>)]),
Tuple(&'tcx [&'tcx ty::Const<'tcx>]),
Array(&'tcx [&'tcx ty::Const<'tcx>]),
Repeat(&'tcx ty::Const<'tcx>, u64),
}
impl<'tcx> Encodable for ConstAggregate<'tcx> {
fn encode<S: Encoder>(&self, _: &mut S) -> Result<(), S::Error> {
bug!("should never encode ConstAggregate::{:?}", self)
}
}
impl<'tcx> Decodable for ConstAggregate<'tcx> {
fn decode<D: Decoder>(_: &mut D) -> Result<Self, D::Error> {
bug!("should never decode ConstAggregate")
}
}
impl<'tcx> ConstVal<'tcx> {
pub fn to_u128(&self) -> Option<u128> {
match *self {
ConstVal::Integral(i) => i.to_u128(),
ConstVal::Bool(b) => Some(b as u128),
ConstVal::Char(ch) => Some(ch as u32 as u128),
ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))) => {
Some(b)
},
@ -93,7 +58,6 @@ impl<'tcx> ConstVal<'tcx> {
}
pub fn unwrap_usize<'a, 'gcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> ConstUsize {
match *self {
ConstVal::Integral(ConstInt::Usize(i)) => i,
ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))) => {
assert_eq!(b as u64 as u128, b);
match ConstUsize::new(b as u64, tcx.sess.target.usize_ty) {

View file

@ -33,7 +33,6 @@ use ty::TypeAndMut;
use util::ppaux;
use std::slice;
use hir::{self, InlineAsm};
use std::ascii;
use std::borrow::{Cow};
use std::cell::Ref;
use std::fmt::{self, Debug, Formatter, Write};
@ -1539,12 +1538,8 @@ impl<'tcx> Operand<'tcx> {
ty,
literal: Literal::Value {
value: tcx.mk_const(ty::Const {
val: if tcx.sess.opts.debugging_opts.miri {
// ZST function type
ConstVal::Value(Value::ByVal(PrimVal::Undef))
} else {
ConstVal::Function(def_id, substs)
},
// ZST function type
val: ConstVal::Value(Value::ByVal(PrimVal::Undef)),
ty
})
},
@ -1877,21 +1872,6 @@ impl<'tcx> Debug for Literal<'tcx> {
fn fmt_const_val<W: Write>(fmt: &mut W, const_val: &ty::Const) -> fmt::Result {
use middle::const_val::ConstVal::*;
match const_val.val {
Float(f) => write!(fmt, "{:?}", f),
Integral(n) => write!(fmt, "{}", n),
Str(s) => write!(fmt, "{:?}", s),
ByteStr(bytes) => {
let escaped: String = bytes.data
.iter()
.flat_map(|&ch| ascii::escape_default(ch).map(|c| c as char))
.collect();
write!(fmt, "b\"{}\"", escaped)
}
Bool(b) => write!(fmt, "{:?}", b),
Char(c) => write!(fmt, "{:?}", c),
Variant(def_id) |
Function(def_id, _) => write!(fmt, "{}", item_path_str(def_id)),
Aggregate(_) => bug!("`ConstVal::{:?}` should not be in MIR", const_val),
Unevaluated(..) => write!(fmt, "{:?}", const_val),
Value(val) => print_miri_value(val, const_val.ty, fmt),
}
@ -1918,7 +1898,7 @@ fn print_miri_value<W: Write>(value: Value, ty: Ty, f: &mut W) -> fmt::Result {
let alloc = tcx
.interpret_interner
.borrow()
.get_alloc(ptr.alloc_id.0)
.get_alloc(ptr.alloc_id)
.expect("miri alloc not found");
assert_eq!(len as usize as u128, len);
let slice = &alloc.bytes[(ptr.offset as usize)..][..(len as usize)];

View file

@ -54,7 +54,7 @@ use rustc_data_structures::stable_hasher::{HashStable, hash_stable_hashmap,
StableHasher, StableHasherResult,
StableVec};
use arena::{TypedArena, DroplessArena};
use rustc_const_math::{ConstInt, ConstUsize};
use rustc_const_math::ConstUsize;
use rustc_data_structures::indexed_vec::IndexVec;
use rustc_data_structures::sync::Lrc;
use std::any::Any;
@ -909,7 +909,7 @@ pub struct InterpretInterner<'tcx> {
/// Reverse map of `alloc_cache`
///
/// Multiple globals may share the same memory
global_cache: FxHashMap<interpret::Pointer, Vec<interpret::GlobalId<'tcx>>>,
global_cache: FxHashMap<interpret::AllocId, Vec<interpret::GlobalId<'tcx>>>,
/// The AllocId to assign to the next new regular allocation.
/// Always incremented, never gets smaller.
@ -959,20 +959,17 @@ impl<'tcx> InterpretInterner<'tcx> {
pub fn cache(
&mut self,
global_id: interpret::GlobalId<'tcx>,
ptr: interpret::AllocId,
alloc_id: interpret::AllocId,
) {
if let interpret::PrimVal::Ptr(ptr) = ptr.primval {
assert!(ptr.offset == 0);
}
self.global_cache.entry(ptr).or_default().push(global_id);
if let Some(old) = self.alloc_cache.insert(global_id, ptr) {
self.global_cache.entry(alloc_id).or_default().push(global_id);
if let Some(old) = self.alloc_cache.insert(global_id, alloc_id) {
bug!("tried to cache {:?}, but was already existing as {:#?}", global_id, old);
}
}
pub fn get_globals(
&self,
ptr: interpret::Pointer,
ptr: interpret::AllocId,
) -> &[interpret::GlobalId<'tcx>] {
match self.global_cache.get(&ptr) {
Some(v) => v,
@ -2099,11 +2096,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
pub fn mk_array_const_usize(self, ty: Ty<'tcx>, n: ConstUsize) -> Ty<'tcx> {
self.mk_ty(TyArray(ty, self.mk_const(ty::Const {
val: if self.sess.opts.debugging_opts.miri {
ConstVal::Value(Value::ByVal(PrimVal::Bytes(n.as_u64().into())))
} else {
ConstVal::Integral(ConstInt::Usize(n))
},
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(n.as_u64().into()))),
ty: self.types.usize
})))
}

View file

@ -19,8 +19,6 @@ use syntax::ast;
use errors::DiagnosticBuilder;
use syntax_pos::Span;
use rustc_const_math::ConstInt;
use hir;
#[derive(Clone, Copy, Debug)]
@ -188,8 +186,6 @@ impl<'a, 'gcx, 'lcx, 'tcx> ty::TyS<'tcx> {
ty::TyForeign(def_id) => format!("extern type `{}`", tcx.item_path_str(def_id)),
ty::TyArray(_, n) => {
match n.val {
ConstVal::Integral(ConstInt::Usize(n)) =>
format!("array of {} elements", n),
ConstVal::Value(Value::ByVal(PrimVal::Bytes(n))) =>
format!("array of {} elements", n),
_ => "array".to_string(),

View file

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use middle::const_val::{ConstVal, ConstAggregate};
use middle::const_val::ConstVal;
use ty::subst::Substs;
use ty::{self, Ty, TypeFlags, TypeFoldable};
@ -218,31 +218,7 @@ impl FlagComputation {
fn add_const(&mut self, constant: &ty::Const) {
self.add_ty(constant.ty);
match constant.val {
ConstVal::Integral(_) |
ConstVal::Float(_) |
ConstVal::Str(_) |
ConstVal::ByteStr(_) |
ConstVal::Bool(_) |
ConstVal::Char(_) |
ConstVal::Value(_) |
ConstVal::Variant(_) => {}
ConstVal::Function(_, substs) => {
self.add_substs(substs);
}
ConstVal::Aggregate(ConstAggregate::Struct(fields)) => {
for &(_, v) in fields {
self.add_const(v);
}
}
ConstVal::Aggregate(ConstAggregate::Tuple(fields)) |
ConstVal::Aggregate(ConstAggregate::Array(fields)) => {
for v in fields {
self.add_const(v);
}
}
ConstVal::Aggregate(ConstAggregate::Repeat(v, _)) => {
self.add_const(v);
}
ConstVal::Value(_) => {}
ConstVal::Unevaluated(_, substs) => {
self.add_flags(TypeFlags::HAS_PROJECTION);
self.add_substs(substs);

View file

@ -1836,9 +1836,6 @@ impl<'a, 'gcx, 'tcx> AdtDef {
if let VariantDiscr::Explicit(expr_did) = v.discr {
let substs = Substs::identity_for_item(tcx.global_tcx(), expr_did);
match tcx.const_eval(param_env.and((expr_did, substs))) {
Ok(&ty::Const { val: ConstVal::Integral(v), .. }) => {
discr = v;
}
Ok(&ty::Const {
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))),
..
@ -1889,10 +1886,6 @@ impl<'a, 'gcx, 'tcx> AdtDef {
ty::VariantDiscr::Explicit(expr_did) => {
let substs = Substs::identity_for_item(tcx.global_tcx(), expr_did);
match tcx.const_eval(param_env.and((expr_did, substs))) {
Ok(&ty::Const { val: ConstVal::Integral(v), .. }) => {
explicit_value = v;
break;
}
Ok(&ty::Const {
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))),
..

View file

@ -483,7 +483,6 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R,
assert_eq!(sz_b.ty, tcx.types.usize);
let to_u64 = |x: &'tcx ty::Const<'tcx>| -> Result<u64, ErrorReported> {
match x.val {
ConstVal::Integral(x) => Ok(x.to_u64().unwrap()),
ConstVal::Value(Value::ByVal(prim)) => Ok(prim.to_u64().unwrap()),
ConstVal::Unevaluated(def_id, substs) => {
// FIXME(eddyb) get the right param_env.
@ -491,9 +490,6 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R,
match tcx.lift_to_global(&substs) {
Some(substs) => {
match tcx.const_eval(param_env.and((def_id, substs))) {
Ok(&ty::Const { val: ConstVal::Integral(x), .. }) => {
return Ok(x.to_u64().unwrap());
}
Ok(&ty::Const {
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))),
..

View file

@ -13,7 +13,7 @@
//! hand, though we've recently added some macros (e.g.,
//! `BraceStructLiftImpl!`) to help with the tedium.
use middle::const_val::{self, ConstVal, ConstAggregate, ConstEvalErr};
use middle::const_val::{self, ConstVal, ConstEvalErr};
use ty::{self, Lift, Ty, TyCtxt};
use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
use rustc_data_structures::accumulate_vec::AccumulateVec;
@ -1410,54 +1410,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::error::TypeError<'tcx> {
impl<'tcx> TypeFoldable<'tcx> for ConstVal<'tcx> {
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
match *self {
ConstVal::Integral(i) => ConstVal::Integral(i),
ConstVal::Float(f) => ConstVal::Float(f),
ConstVal::Str(s) => ConstVal::Str(s),
ConstVal::ByteStr(b) => ConstVal::ByteStr(b),
ConstVal::Bool(b) => ConstVal::Bool(b),
ConstVal::Char(c) => ConstVal::Char(c),
ConstVal::Value(v) => ConstVal::Value(v),
ConstVal::Variant(def_id) => ConstVal::Variant(def_id),
ConstVal::Function(def_id, substs) => {
ConstVal::Function(def_id, substs.fold_with(folder))
}
ConstVal::Aggregate(ConstAggregate::Struct(fields)) => {
let new_fields: Vec<_> = fields.iter().map(|&(name, v)| {
(name, v.fold_with(folder))
}).collect();
let fields = if new_fields == fields {
fields
} else {
folder.tcx().alloc_name_const_slice(&new_fields)
};
ConstVal::Aggregate(ConstAggregate::Struct(fields))
}
ConstVal::Aggregate(ConstAggregate::Tuple(fields)) => {
let new_fields: Vec<_> = fields.iter().map(|v| {
v.fold_with(folder)
}).collect();
let fields = if new_fields == fields {
fields
} else {
folder.tcx().alloc_const_slice(&new_fields)
};
ConstVal::Aggregate(ConstAggregate::Tuple(fields))
}
ConstVal::Aggregate(ConstAggregate::Array(fields)) => {
let new_fields: Vec<_> = fields.iter().map(|v| {
v.fold_with(folder)
}).collect();
let fields = if new_fields == fields {
fields
} else {
folder.tcx().alloc_const_slice(&new_fields)
};
ConstVal::Aggregate(ConstAggregate::Array(fields))
}
ConstVal::Aggregate(ConstAggregate::Repeat(v, count)) => {
let v = v.fold_with(folder);
ConstVal::Aggregate(ConstAggregate::Repeat(v, count))
}
ConstVal::Unevaluated(def_id, substs) => {
ConstVal::Unevaluated(def_id, substs.fold_with(folder))
}
@ -1466,25 +1419,7 @@ impl<'tcx> TypeFoldable<'tcx> for ConstVal<'tcx> {
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
match *self {
ConstVal::Integral(_) |
ConstVal::Float(_) |
ConstVal::Str(_) |
ConstVal::ByteStr(_) |
ConstVal::Bool(_) |
ConstVal::Char(_) |
ConstVal::Value(_) |
ConstVal::Variant(_) => false,
ConstVal::Function(_, substs) => substs.visit_with(visitor),
ConstVal::Aggregate(ConstAggregate::Struct(fields)) => {
fields.iter().any(|&(_, v)| v.visit_with(visitor))
}
ConstVal::Aggregate(ConstAggregate::Tuple(fields)) |
ConstVal::Aggregate(ConstAggregate::Array(fields)) => {
fields.iter().any(|v| v.visit_with(visitor))
}
ConstVal::Aggregate(ConstAggregate::Repeat(v, _)) => {
v.visit_with(visitor)
}
ConstVal::Value(_) => false,
ConstVal::Unevaluated(_, substs) => substs.visit_with(visitor),
}
}

View file

@ -765,7 +765,6 @@ impl<'a, 'gcx, 'tcx, W> TypeVisitor<'tcx> for TypeIdHasher<'a, 'gcx, 'tcx, W>
TyArray(_, n) => {
self.hash_discriminant_u8(&n.val);
match n.val {
ConstVal::Integral(x) => self.hash(x.to_u64().unwrap()),
ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))) => self.hash(b),
ConstVal::Unevaluated(def_id, _) => self.def_id(def_id),
_ => bug!("arrays should not have {:?} as length", n)

View file

@ -11,7 +11,7 @@
//! An iterator over the type substructure.
//! WARNING: this does not keep track of the region depth.
use middle::const_val::{ConstVal, ConstAggregate};
use middle::const_val::ConstVal;
use ty::{self, Ty};
use rustc_data_structures::small_vec::SmallVec;
use rustc_data_structures::accumulate_vec::IntoIter as AccIntoIter;
@ -140,31 +140,7 @@ fn push_subtypes<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent_ty: Ty<'tcx>) {
fn push_const<'tcx>(stack: &mut TypeWalkerStack<'tcx>, constant: &'tcx ty::Const<'tcx>) {
match constant.val {
ConstVal::Integral(_) |
ConstVal::Float(_) |
ConstVal::Str(_) |
ConstVal::ByteStr(_) |
ConstVal::Bool(_) |
ConstVal::Char(_) |
ConstVal::Value(_) |
ConstVal::Variant(_) => {}
ConstVal::Function(_, substs) => {
stack.extend(substs.types().rev());
}
ConstVal::Aggregate(ConstAggregate::Struct(fields)) => {
for &(_, v) in fields.iter().rev() {
push_const(stack, v);
}
}
ConstVal::Aggregate(ConstAggregate::Tuple(fields)) |
ConstVal::Aggregate(ConstAggregate::Array(fields)) => {
for v in fields.iter().rev() {
push_const(stack, v);
}
}
ConstVal::Aggregate(ConstAggregate::Repeat(v, _)) => {
push_const(stack, v);
}
ConstVal::Value(_) => {}
ConstVal::Unevaluated(_, substs) => {
stack.extend(substs.types().rev());
}

View file

@ -9,7 +9,7 @@
// except according to those terms.
use hir::def_id::DefId;
use middle::const_val::{ConstVal, ConstAggregate};
use middle::const_val::ConstVal;
use infer::InferCtxt;
use ty::subst::Substs;
use traits;
@ -217,29 +217,7 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
fn compute_const(&mut self, constant: &'tcx ty::Const<'tcx>) {
self.require_sized(constant.ty, traits::ConstSized);
match constant.val {
ConstVal::Integral(_) |
ConstVal::Float(_) |
ConstVal::Str(_) |
ConstVal::ByteStr(_) |
ConstVal::Bool(_) |
ConstVal::Char(_) |
ConstVal::Variant(_) |
ConstVal::Value(_) |
ConstVal::Function(..) => {}
ConstVal::Aggregate(ConstAggregate::Struct(fields)) => {
for &(_, v) in fields {
self.compute_const(v);
}
}
ConstVal::Aggregate(ConstAggregate::Tuple(fields)) |
ConstVal::Aggregate(ConstAggregate::Array(fields)) => {
for v in fields {
self.compute_const(v);
}
}
ConstVal::Aggregate(ConstAggregate::Repeat(v, _)) => {
self.compute_const(v);
}
ConstVal::Value(_) => {}
ConstVal::Unevaluated(def_id, substs) => {
let obligations = self.nominal_obligations(def_id, substs);
self.out.extend(obligations);

View file

@ -27,7 +27,6 @@ use std::cell::Cell;
use std::fmt;
use std::usize;
use rustc_const_math::ConstInt;
use rustc_data_structures::indexed_vec::Idx;
use syntax::abi::Abi;
use syntax::ast::CRATE_NODE_ID;
@ -1166,9 +1165,6 @@ define_print! {
TyArray(ty, sz) => {
print!(f, cx, write("["), print(ty), write("; "))?;
match sz.val {
ConstVal::Integral(ConstInt::Usize(sz)) => {
write!(f, "{}", sz)?;
}
ConstVal::Value(Value::ByVal(PrimVal::Bytes(sz))) => {
write!(f, "{}", sz)?;
}

View file

@ -1088,6 +1088,13 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(trans: &TransCrate,
stability::check_unused_or_stable_features(tcx)
});
time(time_passes,
"MIR linting",
|| for def_id in tcx.body_owners() {
mir::const_eval::check::check(tcx, def_id)
});
time(time_passes, "lint checking", || lint::check_crate(tcx));
return Ok(f(tcx, analysis, rx, tcx.sess.compile_status()));

View file

@ -682,78 +682,6 @@ impl EarlyLintPass for DeprecatedAttr {
}
}
declare_lint! {
pub ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
Warn,
"floating-point literals cannot be used in patterns"
}
/// Checks for floating point literals in patterns.
#[derive(Clone)]
pub struct IllegalFloatLiteralPattern;
impl LintPass for IllegalFloatLiteralPattern {
fn get_lints(&self) -> LintArray {
lint_array!(ILLEGAL_FLOATING_POINT_LITERAL_PATTERN)
}
}
fn fl_lit_check_expr(cx: &EarlyContext, expr: &ast::Expr) {
use self::ast::{ExprKind, LitKind};
match expr.node {
ExprKind::Lit(ref l) => {
match l.node {
LitKind::FloatUnsuffixed(..) |
LitKind::Float(..) => {
cx.span_lint(ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
l.span,
"floating-point literals cannot be used in patterns");
},
_ => (),
}
}
// These may occur in patterns
// and can maybe contain float literals
ExprKind::Unary(_, ref f) => fl_lit_check_expr(cx, f),
// Other kinds of exprs can't occur in patterns so we don't have to check them
// (ast_validation will emit an error if they occur)
_ => (),
}
}
impl EarlyLintPass for IllegalFloatLiteralPattern {
fn check_pat(&mut self, cx: &EarlyContext, pat: &ast::Pat) {
use self::ast::PatKind;
pat.walk(&mut |p| {
match p.node {
// Wildcard patterns and paths are uninteresting for the lint
PatKind::Wild |
PatKind::Path(..) => (),
// The walk logic recurses inside these
PatKind::Ident(..) |
PatKind::Struct(..) |
PatKind::Tuple(..) |
PatKind::TupleStruct(..) |
PatKind::Ref(..) |
PatKind::Box(..) |
PatKind::Paren(..) |
PatKind::Slice(..) => (),
// Extract the expressions and check them
PatKind::Lit(ref e) => fl_lit_check_expr(cx, e),
PatKind::Range(ref st, ref en, _) => {
fl_lit_check_expr(cx, st);
fl_lit_check_expr(cx, en);
},
PatKind::Mac(_) => bug!("lint must run post-expansion"),
}
true
});
}
}
declare_lint! {
pub UNUSED_DOC_COMMENT,
Warn,

View file

@ -43,7 +43,6 @@ extern crate rustc_mir;
extern crate syntax_pos;
use rustc::lint;
use rustc::middle;
use rustc::session;
use rustc::util;
@ -107,7 +106,6 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
UnusedParens,
UnusedImportBraces,
AnonymousParameters,
IllegalFloatLiteralPattern,
UnusedDocComment,
);

View file

@ -14,9 +14,6 @@ use rustc::hir::map as hir_map;
use rustc::ty::subst::Substs;
use rustc::ty::{self, AdtKind, Ty, TyCtxt};
use rustc::ty::layout::{self, LayoutOf};
use middle::const_val::ConstVal;
use rustc_mir::const_eval::ConstContext;
use rustc::mir::interpret::{Value, PrimVal};
use util::nodemap::FxHashSet;
use lint::{LateContext, LintContext, LintArray};
use lint::{LintPass, LateLintPass};
@ -43,12 +40,6 @@ declare_lint! {
"literal out of range for its type"
}
declare_lint! {
EXCEEDING_BITSHIFTS,
Deny,
"shift exceeds the type's number of bits"
}
declare_lint! {
VARIANT_SIZE_DIFFERENCES,
Allow,
@ -70,8 +61,7 @@ impl TypeLimits {
impl LintPass for TypeLimits {
fn get_lints(&self) -> LintArray {
lint_array!(UNUSED_COMPARISONS,
OVERFLOWING_LITERALS,
EXCEEDING_BITSHIFTS)
OVERFLOWING_LITERALS)
}
}
@ -90,59 +80,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits {
e.span,
"comparison is useless due to type limits");
}
if binop.node.is_shift() {
let opt_ty_bits = match cx.tables.node_id_to_type(l.hir_id).sty {
ty::TyInt(t) => Some(int_ty_bits(t, cx.sess().target.isize_ty)),
ty::TyUint(t) => Some(uint_ty_bits(t, cx.sess().target.usize_ty)),
_ => None,
};
if let Some(bits) = opt_ty_bits {
let exceeding = if let hir::ExprLit(ref lit) = r.node {
if let ast::LitKind::Int(shift, _) = lit.node {
shift as u64 >= bits
} else {
false
}
} else {
// HACK(eddyb) This might be quite inefficient.
// This would be better left to MIR constant propagation,
// perhaps even at trans time (like is the case already
// when the value being shifted is *also* constant).
let parent_item = cx.tcx.hir.get_parent(e.id);
let parent_def_id = cx.tcx.hir.local_def_id(parent_item);
let substs = Substs::identity_for_item(cx.tcx, parent_def_id);
let const_cx = ConstContext::new(cx.tcx,
cx.param_env.and(substs),
cx.tables);
match const_cx.eval(&r) {
Ok(&ty::Const { val: ConstVal::Integral(i), .. }) => {
i.is_negative() ||
i.to_u64()
.map(|i| i >= bits)
.unwrap_or(true)
}
Ok(&ty::Const {
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))),
ty,
}) => {
if ty.is_signed() {
(b as i128) < 0
} else {
b >= bits as u128
}
}
_ => false,
}
};
if exceeding {
cx.span_lint(EXCEEDING_BITSHIFTS,
e.span,
"bitshift exceeds the type's number of bits");
}
};
}
}
hir::ExprLit(ref lit) => {
match cx.tables.node_id_to_type(e.hir_id).sty {
@ -301,28 +238,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits {
}
}
fn int_ty_bits(int_ty: ast::IntTy, isize_ty: ast::IntTy) -> u64 {
match int_ty {
ast::IntTy::Isize => int_ty_bits(isize_ty, isize_ty),
ast::IntTy::I8 => 8,
ast::IntTy::I16 => 16 as u64,
ast::IntTy::I32 => 32,
ast::IntTy::I64 => 64,
ast::IntTy::I128 => 128,
}
}
fn uint_ty_bits(uint_ty: ast::UintTy, usize_ty: ast::UintTy) -> u64 {
match uint_ty {
ast::UintTy::Usize => uint_ty_bits(usize_ty, usize_ty),
ast::UintTy::U8 => 8,
ast::UintTy::U16 => 16,
ast::UintTy::U32 => 32,
ast::UintTy::U64 => 64,
ast::UintTy::U128 => 128,
}
}
fn check_limits(cx: &LateContext,
binop: hir::BinOp,
l: &hir::Expr,

View file

@ -281,8 +281,7 @@ impl<'a, 'tcx> SpecializedDecoder<interpret::AllocId> for DecodeContext<'a, 'tcx
let pos = self.position();
match usize::decode(self)? {
::std::usize::MAX => {
let id = interpret_interner().reserve();
let alloc_id = interpret::AllocId(id);
let alloc_id = interpret_interner().reserve();
trace!("creating alloc id {:?} at {}", alloc_id, pos);
// insert early to allow recursive allocs
self.interpret_alloc_cache.insert(pos, alloc_id);
@ -290,18 +289,12 @@ impl<'a, 'tcx> SpecializedDecoder<interpret::AllocId> for DecodeContext<'a, 'tcx
let allocation = interpret::Allocation::decode(self)?;
trace!("decoded alloc {:?} {:#?}", alloc_id, allocation);
let allocation = self.tcx.unwrap().intern_const_alloc(allocation);
interpret_interner().intern_at_reserved(id, allocation);
interpret_interner().intern_at_reserved(alloc_id, allocation);
let num = usize::decode(self)?;
let ptr = interpret::Pointer {
primval: interpret::PrimVal::Ptr(interpret::MemoryPointer {
alloc_id,
offset: 0,
}),
};
for _ in 0..num {
let glob = interpret::GlobalId::decode(self)?;
interpret_interner().cache(glob, ptr);
interpret_interner().cache(glob, alloc_id);
}
Ok(alloc_id)
@ -310,7 +303,7 @@ impl<'a, 'tcx> SpecializedDecoder<interpret::AllocId> for DecodeContext<'a, 'tcx
trace!("creating fn alloc id at {}", pos);
let instance = ty::Instance::decode(self)?;
trace!("decoded fn alloc instance: {:?}", instance);
let id = interpret::AllocId(interpret_interner().create_fn_alloc(instance));
let id = interpret_interner().create_fn_alloc(instance);
trace!("created fn alloc id: {:?}", id);
self.interpret_alloc_cache.insert(pos, id);
Ok(id)

View file

@ -199,26 +199,21 @@ impl<'a, 'tcx> SpecializedEncoder<interpret::AllocId> for EncodeContext<'a, 'tcx
// point to itself.
self.interpret_alloc_shorthands.insert(*alloc_id, start);
let interpret_interner = self.tcx.interpret_interner.borrow();
if let Some(alloc) = interpret_interner.get_alloc(alloc_id.0) {
if let Some(alloc) = interpret_interner.get_alloc(*alloc_id) {
trace!("encoding {:?} with {:#?}", alloc_id, alloc);
usize::max_value().encode(self)?;
alloc.encode(self)?;
let globals = interpret_interner.get_globals(interpret::Pointer {
primval: interpret::PrimVal::Ptr(interpret::MemoryPointer {
alloc_id: *alloc_id,
offset: 0,
}),
});
let globals = interpret_interner.get_globals(*alloc_id);
globals.len().encode(self)?;
for glob in globals {
glob.encode(self)?;
}
} else if let Some(fn_instance) = interpret_interner.get_fn(alloc_id.0) {
} else if let Some(fn_instance) = interpret_interner.get_fn(*alloc_id) {
trace!("encoding {:?} with {:#?}", alloc_id, fn_instance);
(usize::max_value() - 1).encode(self)?;
fn_instance.encode(self)?;
} else {
bug!("alloc id without corresponding allocation: {}", alloc_id.0);
bug!("alloc id without corresponding allocation: {}", alloc_id);
}
Ok(())
}

View file

@ -260,12 +260,11 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
// would be lost if we just look at the normalized
// value.
let did = match value.val {
ConstVal::Function(def_id, ..) => Some(def_id),
ConstVal::Value(Value::ByVal(PrimVal::Ptr(p))) => {
self.tcx()
.interpret_interner
.borrow()
.get_fn(p.alloc_id.0)
.get_fn(p.alloc_id)
.map(|instance| instance.def_id())
},
ConstVal::Value(Value::ByVal(PrimVal::Undef)) => {
@ -1044,11 +1043,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
},
..
}) => match val {
ConstVal::Function(def_id, _) => {
Some(def_id) == self.tcx().lang_items().box_free_fn()
},
ConstVal::Value(Value::ByVal(PrimVal::Ptr(p))) => {
let inst = self.tcx().interpret_interner.borrow().get_fn(p.alloc_id.0);
let inst = self.tcx().interpret_interner.borrow().get_fn(p.alloc_id);
inst.map_or(false, |inst| {
Some(inst.def_id()) == self.tcx().lang_items().box_free_fn()
})

View file

@ -204,11 +204,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
ty: this.hir.tcx().types.u32,
literal: Literal::Value {
value: this.hir.tcx().mk_const(ty::Const {
val: if this.hir.tcx().sess.opts.debugging_opts.miri {
ConstVal::Value(Value::ByVal(PrimVal::Bytes(0)))
} else {
ConstVal::Integral(ConstInt::U32(0))
},
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(0))),
ty: this.hir.tcx().types.u32
}),
},
@ -406,11 +402,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
Literal::Value {
value: self.hir.tcx().mk_const(ty::Const {
val: if self.hir.tcx().sess.opts.debugging_opts.miri {
ConstVal::Value(Value::ByVal(PrimVal::Bytes(val.to_u128_unchecked())))
} else {
ConstVal::Integral(val)
},
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(val.to_u128_unchecked()))),
ty
})
}
@ -448,13 +440,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
Literal::Value {
value: self.hir.tcx().mk_const(ty::Const {
val: if self.hir.tcx().sess.opts.debugging_opts.miri {
ConstVal::Value(Value::ByVal(PrimVal::Bytes(
val.to_u128_unchecked()
)))
} else {
ConstVal::Integral(val)
},
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(
val.to_u128_unchecked()
))),
ty
})
}

View file

@ -299,7 +299,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
let mut val = Operand::Copy(place.clone());
let bytes = match value.val {
ConstVal::ByteStr(bytes) => Some(bytes.data),
ConstVal::Value(Value::ByVal(PrimVal::Ptr(p))) => {
let is_array_ptr = ty
.builtin_deref(true, ty::NoPreference)
@ -310,7 +309,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
.tcx()
.interpret_interner
.borrow()
.get_alloc(p.alloc_id.0)
.get_alloc(p.alloc_id)
.map(|alloc| &alloc.bytes[..])
} else {
None

View file

@ -13,13 +13,11 @@
use build::Builder;
use rustc_const_math::{ConstInt, ConstUsize, ConstIsize};
use rustc::middle::const_val::ConstVal;
use rustc::ty::{self, Ty};
use rustc::mir::interpret::{Value, PrimVal};
use rustc::mir::*;
use syntax::ast;
use syntax_pos::{Span, DUMMY_SP};
impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
@ -63,61 +61,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
ty::TyChar => {
Literal::Value {
value: self.hir.tcx().mk_const(ty::Const {
val: if self.hir.tcx().sess.opts.debugging_opts.miri {
ConstVal::Value(Value::ByVal(PrimVal::Bytes(0)))
} else {
ConstVal::Char('\0')
},
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(0))),
ty
})
}
}
ty::TyUint(ity) => {
let val = match ity {
ast::UintTy::U8 => ConstInt::U8(0),
ast::UintTy::U16 => ConstInt::U16(0),
ast::UintTy::U32 => ConstInt::U32(0),
ast::UintTy::U64 => ConstInt::U64(0),
ast::UintTy::U128 => ConstInt::U128(0),
ast::UintTy::Usize => {
let uint_ty = self.hir.tcx().sess.target.usize_ty;
let val = ConstUsize::new(0, uint_ty).unwrap();
ConstInt::Usize(val)
}
};
ty::TyUint(_) |
ty::TyInt(_) => {
Literal::Value {
value: self.hir.tcx().mk_const(ty::Const {
val: if self.hir.tcx().sess.opts.debugging_opts.miri {
ConstVal::Value(Value::ByVal(PrimVal::Bytes(0)))
} else {
ConstVal::Integral(val)
},
ty
})
}
}
ty::TyInt(ity) => {
let val = match ity {
ast::IntTy::I8 => ConstInt::I8(0),
ast::IntTy::I16 => ConstInt::I16(0),
ast::IntTy::I32 => ConstInt::I32(0),
ast::IntTy::I64 => ConstInt::I64(0),
ast::IntTy::I128 => ConstInt::I128(0),
ast::IntTy::Isize => {
let int_ty = self.hir.tcx().sess.target.isize_ty;
let val = ConstIsize::new(0, int_ty).unwrap();
ConstInt::Isize(val)
}
};
Literal::Value {
value: self.hir.tcx().mk_const(ty::Const {
val: if self.hir.tcx().sess.opts.debugging_opts.miri {
ConstVal::Value(Value::ByVal(PrimVal::Bytes(0)))
} else {
ConstVal::Integral(val)
},
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(0))),
ty
})
}

View file

@ -15,8 +15,6 @@ use self::WitnessPreference::*;
use rustc::middle::const_val::ConstVal;
use const_eval::eval::{compare_const_vals};
use rustc_const_math::ConstInt;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::indexed_vec::Idx;
@ -182,20 +180,6 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
let tcx = self.tcx;
self.byte_array_map.entry(pat).or_insert_with(|| {
match pat.kind {
box PatternKind::Constant {
value: &ty::Const { val: ConstVal::ByteStr(b), .. }
} => {
b.data.iter().map(|&b| &*pattern_arena.alloc(Pattern {
ty: tcx.types.u8,
span: pat.span,
kind: box PatternKind::Constant {
value: tcx.mk_const(ty::Const {
val: ConstVal::Integral(ConstInt::U8(b)),
ty: tcx.types.u8
})
}
})).collect()
}
box PatternKind::Constant {
value: &ty::Const { val: ConstVal::Value(b), ty }
} => {
@ -209,7 +193,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
let alloc = tcx
.interpret_interner
.borrow()
.get_alloc(ptr.alloc_id.0)
.get_alloc(ptr.alloc_id)
.unwrap();
assert_eq!(ptr.offset, 0);
// FIXME: check length
@ -458,11 +442,7 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
ty::TyBool => {
[true, false].iter().map(|&b| {
ConstantValue(cx.tcx.mk_const(ty::Const {
val: if cx.tcx.sess.opts.debugging_opts.miri {
ConstVal::Value(Value::ByVal(PrimVal::Bytes(b as u128)))
} else {
ConstVal::Bool(b)
},
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b as u128))),
ty: cx.tcx.types.bool
}))
}).collect()
@ -575,9 +555,6 @@ fn max_slice_length<'p, 'a: 'p, 'tcx: 'a, I>(
for row in patterns {
match *row.kind {
PatternKind::Constant { value: &ty::Const { val: ConstVal::ByteStr(b), .. } } => {
max_fixed_len = cmp::max(max_fixed_len, b.data.len() as u64);
}
PatternKind::Constant {
value: &ty::Const {
val: ConstVal::Value(Value::ByVal(PrimVal::Ptr(ptr))),
@ -592,7 +569,7 @@ fn max_slice_length<'p, 'a: 'p, 'tcx: 'a, I>(
let alloc = cx.tcx
.interpret_interner
.borrow()
.get_alloc(ptr.alloc_id.0)
.get_alloc(ptr.alloc_id)
.unwrap();
max_fixed_len = cmp::max(max_fixed_len, alloc.bytes.len() as u64);
}
@ -971,7 +948,6 @@ fn slice_pat_covered_by_constructor(tcx: TyCtxt, _span: Span,
suffix: &[Pattern])
-> Result<bool, ErrorReported> {
let data: &[u8] = match *ctor {
ConstantValue(&ty::Const { val: ConstVal::ByteStr(b), .. }) => b.data,
ConstantValue(&ty::Const { val: ConstVal::Value(
Value::ByVal(PrimVal::Ptr(ptr))
), ty }) => {
@ -983,7 +959,7 @@ fn slice_pat_covered_by_constructor(tcx: TyCtxt, _span: Span,
tcx
.interpret_interner
.borrow()
.get_alloc(ptr.alloc_id.0)
.get_alloc(ptr.alloc_id)
.unwrap()
.bytes
.as_ref()
@ -1002,11 +978,6 @@ fn slice_pat_covered_by_constructor(tcx: TyCtxt, _span: Span,
{
match pat.kind {
box PatternKind::Constant { value } => match value.val {
ConstVal::Integral(ConstInt::U8(u)) => {
if u != *ch {
return Ok(false);
}
},
ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))) => {
assert_eq!(b as u8 as u128, b);
if b as u8 != *ch {
@ -1120,13 +1091,6 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>(
PatternKind::Constant { value } => {
match *constructor {
Slice(..) => match value.val {
ConstVal::ByteStr(b) => {
if wild_patterns.len() == b.data.len() {
Some(cx.lower_byte_str_pattern(pat))
} else {
None
}
}
ConstVal::Value(Value::ByVal(PrimVal::Ptr(ptr))) => {
let is_array_ptr = value.ty
.builtin_deref(true, ty::NoPreference)
@ -1136,7 +1100,7 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>(
let data_len = cx.tcx
.interpret_interner
.borrow()
.get_alloc(ptr.alloc_id.0)
.get_alloc(ptr.alloc_id)
.unwrap()
.bytes
.len();

View file

@ -0,0 +1,167 @@
// 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.
//! Lints statically known runtime failures
use rustc::mir::*;
use rustc::mir::visit::Visitor;
use rustc::mir::interpret::{Value, PrimVal};
use rustc::middle::const_val::{ConstVal, ConstEvalErr, ErrKind};
use rustc::traits;
use interpret::{eval_body_as_integer, check_body};
use rustc::ty::{TyCtxt, ParamEnv, self};
use rustc::ty::Instance;
use rustc::ty::layout::LayoutOf;
use rustc::hir::def_id::DefId;
pub fn check<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
if tcx.is_closure(def_id) {
return;
}
let generics = tcx.generics_of(def_id);
// FIXME: miri should be able to eval stuff that doesn't need info
// from the generics
if generics.parent_types as usize + generics.types.len() > 0 {
return;
}
let mir = &tcx.optimized_mir(def_id);
ConstErrVisitor {
tcx,
def_id,
mir,
}.visit_mir(mir);
let param_env = ParamEnv::empty(traits::Reveal::All);
let instance = Instance::mono(tcx, def_id);
for i in 0.. mir.promoted.len() {
use rustc_data_structures::indexed_vec::Idx;
check_body(tcx, instance, Some(Promoted::new(i)), param_env);
}
}
struct ConstErrVisitor<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId,
mir: &'a Mir<'tcx>,
}
impl<'a, 'tcx> ConstErrVisitor<'a, 'tcx> {
fn eval_op(&self, op: &Operand<'tcx>) -> Option<u128> {
let op = match *op {
Operand::Constant(ref c) => c,
_ => return None,
};
let param_env = ParamEnv::empty(traits::Reveal::All);
let val = match op.literal {
Literal::Value { value } => match value.val {
ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))) => b,
_ => return None,
},
Literal::Promoted { index } => {
let instance = Instance::mono(self.tcx, self.def_id);
eval_body_as_integer(self.tcx, param_env, instance, Some(index)).unwrap()
}
};
Some(val)
}
}
impl<'a, 'tcx> Visitor<'tcx> for ConstErrVisitor<'a, 'tcx> {
fn visit_terminator(&mut self,
block: BasicBlock,
terminator: &Terminator<'tcx>,
location: Location) {
self.super_terminator(block, terminator, location);
match terminator.kind {
TerminatorKind::Assert { cond: Operand::Constant(box Constant {
literal: Literal::Value {
value: &ty::Const {
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(cond))),
.. }
}, ..
}), expected, ref msg, .. } if (cond == 1) != expected => {
assert!(cond <= 1);
// If we know we always panic, and the error message
// is also constant, then we can produce a warning.
let kind = match *msg {
AssertMessage::BoundsCheck { ref len, ref index } => {
let len = match self.eval_op(len) {
Some(val) => val,
None => return,
};
let index = match self.eval_op(index) {
Some(val) => val,
None => return,
};
ErrKind::IndexOutOfBounds {
len: len as u64,
index: index as u64
}
}
AssertMessage::Math(ref err) => ErrKind::Math(err.clone()),
AssertMessage::GeneratorResumedAfterReturn |
// FIXME(oli-obk): can we report a const_err warning here?
AssertMessage::GeneratorResumedAfterPanic => return,
};
let span = terminator.source_info.span;
let msg = ConstEvalErr{ span, kind };
let scope_info = match self.mir.visibility_scope_info {
ClearCrossCrate::Set(ref data) => data,
ClearCrossCrate::Clear => return,
};
let node_id = scope_info[terminator.source_info.scope].lint_root;
self.tcx.lint_node(::rustc::lint::builtin::CONST_ERR,
node_id,
msg.span,
&msg.description().into_oneline().into_owned());
},
_ => {},
}
}
fn visit_rvalue(&mut self,
rvalue: &Rvalue<'tcx>,
location: Location) {
self.super_rvalue(rvalue, location);
use rustc::mir::BinOp;
match *rvalue {
Rvalue::BinaryOp(BinOp::Shr, ref lop, ref rop) |
Rvalue::BinaryOp(BinOp::Shl, ref lop, ref rop) => {
let val = match self.eval_op(rop) {
Some(val) => val,
None => return,
};
let ty = lop.ty(self.mir, self.tcx);
let param_env = ParamEnv::empty(traits::Reveal::All);
let bits = (self.tcx, param_env).layout_of(ty).unwrap().size.bits();
if val >= bits as u128 {
let data = &self.mir[location.block];
let stmt_idx = location.statement_index;
let source_info = if stmt_idx < data.statements.len() {
data.statements[stmt_idx].source_info
} else {
data.terminator().source_info
};
let span = source_info.span;
let scope_info = match self.mir.visibility_scope_info {
ClearCrossCrate::Set(ref data) => data,
ClearCrossCrate::Clear => return,
};
let node_id = scope_info[source_info.scope].lint_root;
self.tcx.lint_node(
::rustc::lint::builtin::EXCEEDING_BITSHIFTS,
node_id,
span,
"bitshift exceeds the type's number of bits");
}
}
_ => {}
}
}
}

View file

@ -9,41 +9,18 @@
// except according to those terms.
use rustc::middle::const_val::ConstVal::*;
use rustc::middle::const_val::ConstAggregate::*;
use rustc::middle::const_val::ErrKind::*;
use rustc::middle::const_val::{ByteArray, ConstVal, ConstEvalErr, EvalResult, ErrKind};
use rustc::middle::const_val::{ConstVal, ErrKind};
use rustc::hir::map::blocks::FnLikeNode;
use rustc::hir::def::{Def, CtorKind};
use rustc::hir::def_id::DefId;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::util::IntTypeExt;
use rustc::ty::subst::{Substs, Subst};
use rustc::util::common::ErrorReported;
use rustc::util::nodemap::NodeMap;
use rustc::ty::subst::Substs;
use syntax::abi::Abi;
use syntax::ast;
use syntax::attr;
use rustc::hir::{self, Expr};
use std::cmp::Ordering;
use rustc_const_math::*;
macro_rules! signal {
($e:expr, $exn:expr) => {
return Err(ConstEvalErr { span: $e.span, kind: $exn })
}
}
macro_rules! math {
($e:expr, $op:expr) => {
match $op {
Ok(val) => val,
Err(e) => signal!($e, ErrKind::from(e)),
}
}
}
/// * `DefId` is the id of the constant.
/// * `Substs` is the monomorphized substitutions for the expression.
@ -58,591 +35,94 @@ pub fn lookup_const_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
).map(|instance| (instance.def_id(), instance.substs))
}
pub struct ConstContext<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>,
tables: &'a ty::TypeckTables<'tcx>,
param_env: ty::ParamEnv<'tcx>,
substs: &'tcx Substs<'tcx>,
fn_args: Option<NodeMap<&'tcx ty::Const<'tcx>>>
}
impl<'a, 'tcx> ConstContext<'a, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
param_env_and_substs: ty::ParamEnvAnd<'tcx, &'tcx Substs<'tcx>>,
tables: &'a ty::TypeckTables<'tcx>)
-> Self {
ConstContext {
tcx,
param_env: param_env_and_substs.param_env,
tables,
substs: param_env_and_substs.value,
fn_args: None
}
}
/// Evaluate a constant expression in a context where the expression isn't
/// guaranteed to be evaluable.
pub fn eval(&self, e: &'tcx Expr) -> EvalResult<'tcx> {
if self.tables.tainted_by_errors {
signal!(e, TypeckError);
}
eval_const_expr_partial(self, e)
}
}
type CastResult<'tcx> = Result<ConstVal<'tcx>, ErrKind<'tcx>>;
fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
e: &'tcx Expr) -> EvalResult<'tcx> {
trace!("eval_const_expr_partial: {:?}", e);
let tcx = cx.tcx;
let ty = cx.tables.expr_ty(e).subst(tcx, cx.substs);
let mk_const = |val| tcx.mk_const(ty::Const { val, ty });
let result = match e.node {
hir::ExprUnary(hir::UnNeg, ref inner) => {
// unary neg literals already got their sign during creation
if let hir::ExprLit(ref lit) = inner.node {
return match lit_to_const(&lit.node, tcx, ty, true) {
Ok(val) => Ok(mk_const(val)),
Err(err) => signal!(e, err),
};
}
mk_const(match cx.eval(inner)?.val {
Float(f) => Float(-f),
Integral(i) => Integral(math!(e, -i)),
_ => signal!(e, TypeckError)
})
}
hir::ExprUnary(hir::UnNot, ref inner) => {
mk_const(match cx.eval(inner)?.val {
Integral(i) => Integral(math!(e, !i)),
Bool(b) => Bool(!b),
_ => signal!(e, TypeckError)
})
}
hir::ExprUnary(hir::UnDeref, _) => signal!(e, UnimplementedConstVal("deref operation")),
hir::ExprBinary(op, ref a, ref b) => {
// technically, if we don't have type hints, but integral eval
// gives us a type through a type-suffix, cast or const def type
// we need to re-eval the other value of the BinOp if it was
// not inferred
mk_const(match (cx.eval(a)?.val, cx.eval(b)?.val) {
(Float(a), Float(b)) => {
use std::cmp::Ordering::*;
match op.node {
hir::BiAdd => Float(math!(e, a + b)),
hir::BiSub => Float(math!(e, a - b)),
hir::BiMul => Float(math!(e, a * b)),
hir::BiDiv => Float(math!(e, a / b)),
hir::BiRem => Float(math!(e, a % b)),
hir::BiEq => Bool(math!(e, a.try_cmp(b)) == Equal),
hir::BiLt => Bool(math!(e, a.try_cmp(b)) == Less),
hir::BiLe => Bool(math!(e, a.try_cmp(b)) != Greater),
hir::BiNe => Bool(math!(e, a.try_cmp(b)) != Equal),
hir::BiGe => Bool(math!(e, a.try_cmp(b)) != Less),
hir::BiGt => Bool(math!(e, a.try_cmp(b)) == Greater),
_ => span_bug!(e.span, "typeck error"),
}
}
(Integral(a), Integral(b)) => {
use std::cmp::Ordering::*;
match op.node {
hir::BiAdd => Integral(math!(e, a + b)),
hir::BiSub => Integral(math!(e, a - b)),
hir::BiMul => Integral(math!(e, a * b)),
hir::BiDiv => Integral(math!(e, a / b)),
hir::BiRem => Integral(math!(e, a % b)),
hir::BiBitAnd => Integral(math!(e, a & b)),
hir::BiBitOr => Integral(math!(e, a | b)),
hir::BiBitXor => Integral(math!(e, a ^ b)),
hir::BiShl => Integral(math!(e, a << b)),
hir::BiShr => Integral(math!(e, a >> b)),
hir::BiEq => Bool(math!(e, a.try_cmp(b)) == Equal),
hir::BiLt => Bool(math!(e, a.try_cmp(b)) == Less),
hir::BiLe => Bool(math!(e, a.try_cmp(b)) != Greater),
hir::BiNe => Bool(math!(e, a.try_cmp(b)) != Equal),
hir::BiGe => Bool(math!(e, a.try_cmp(b)) != Less),
hir::BiGt => Bool(math!(e, a.try_cmp(b)) == Greater),
_ => span_bug!(e.span, "typeck error"),
}
}
(Bool(a), Bool(b)) => {
Bool(match op.node {
hir::BiAnd => a && b,
hir::BiOr => a || b,
hir::BiBitXor => a ^ b,
hir::BiBitAnd => a & b,
hir::BiBitOr => a | b,
hir::BiEq => a == b,
hir::BiNe => a != b,
hir::BiLt => a < b,
hir::BiLe => a <= b,
hir::BiGe => a >= b,
hir::BiGt => a > b,
_ => span_bug!(e.span, "typeck error"),
})
}
(Char(a), Char(b)) => {
Bool(match op.node {
hir::BiEq => a == b,
hir::BiNe => a != b,
hir::BiLt => a < b,
hir::BiLe => a <= b,
hir::BiGe => a >= b,
hir::BiGt => a > b,
_ => span_bug!(e.span, "typeck error"),
})
}
_ => signal!(e, MiscBinaryOp),
})
}
hir::ExprCast(ref base, _) => {
let base_val = cx.eval(base)?;
let base_ty = cx.tables.expr_ty(base).subst(tcx, cx.substs);
if ty == base_ty {
base_val
} else {
match cast_const(tcx, base_val.val, ty) {
Ok(val) => mk_const(val),
Err(kind) => signal!(e, kind),
}
}
}
hir::ExprPath(ref qpath) => {
let substs = cx.tables.node_substs(e.hir_id).subst(tcx, cx.substs);
match cx.tables.qpath_def(qpath, e.hir_id) {
Def::Const(def_id) |
Def::AssociatedConst(def_id) => {
let substs = tcx.normalize_associated_type_in_env(&substs, cx.param_env);
match tcx.at(e.span).const_eval(cx.param_env.and((def_id, substs))) {
Ok(val) => val,
Err(ConstEvalErr { kind: TypeckError, .. }) => {
signal!(e, TypeckError);
}
Err(err) => {
debug!("bad reference: {:?}, {:?}", err.description(), err.span);
signal!(e, ErroneousReferencedConstant(box err))
},
}
},
Def::VariantCtor(variant_def, CtorKind::Const) => {
mk_const(Variant(variant_def))
}
Def::VariantCtor(_, CtorKind::Fn) => {
signal!(e, UnimplementedConstVal("enum variants"));
}
Def::StructCtor(_, CtorKind::Const) => {
mk_const(Aggregate(Struct(&[])))
}
Def::StructCtor(_, CtorKind::Fn) => {
signal!(e, UnimplementedConstVal("tuple struct constructors"))
}
Def::Local(id) => {
debug!("Def::Local({:?}): {:?}", id, cx.fn_args);
if let Some(&val) = cx.fn_args.as_ref().and_then(|args| args.get(&id)) {
val
} else {
signal!(e, NonConstPath);
}
},
Def::Method(id) | Def::Fn(id) => mk_const(Function(id, substs)),
Def::Err => span_bug!(e.span, "typeck error"),
_ => signal!(e, NonConstPath),
}
}
hir::ExprCall(ref callee, ref args) => {
let (def_id, substs) = match cx.eval(callee)?.val {
Function(def_id, substs) => (def_id, substs),
_ => signal!(e, TypeckError),
};
if tcx.fn_sig(def_id).abi() == Abi::RustIntrinsic {
let layout_of = |ty: Ty<'tcx>| {
let ty = tcx.erase_regions(&ty);
tcx.at(e.span).layout_of(cx.param_env.and(ty)).map_err(|err| {
ConstEvalErr { span: e.span, kind: LayoutError(err) }
})
};
match &tcx.item_name(def_id)[..] {
"size_of" => {
let size = layout_of(substs.type_at(0))?.size.bytes();
return Ok(mk_const(Integral(Usize(ConstUsize::new(size,
tcx.sess.target.usize_ty).unwrap()))));
}
"min_align_of" => {
let align = layout_of(substs.type_at(0))?.align.abi();
return Ok(mk_const(Integral(Usize(ConstUsize::new(align,
tcx.sess.target.usize_ty).unwrap()))));
}
"type_id" => {
let type_id = tcx.type_id_hash(substs.type_at(0));
return Ok(mk_const(Integral(U64(type_id))));
}
_ => signal!(e, TypeckError)
}
}
let body = if let Some(node_id) = tcx.hir.as_local_node_id(def_id) {
if let Some(fn_like) = FnLikeNode::from_node(tcx.hir.get(node_id)) {
if fn_like.constness() == hir::Constness::Const {
tcx.hir.body(fn_like.body())
} else {
signal!(e, TypeckError)
}
} else {
signal!(e, TypeckError)
}
} else {
if tcx.is_const_fn(def_id) {
tcx.extern_const_body(def_id).body
} else {
signal!(e, TypeckError)
}
};
let arg_ids = body.arguments.iter().map(|arg| match arg.pat.node {
hir::PatKind::Binding(_, canonical_id, _, _) => Some(canonical_id),
_ => None
}).collect::<Vec<_>>();
assert_eq!(arg_ids.len(), args.len());
let mut call_args = NodeMap();
for (arg, arg_expr) in arg_ids.into_iter().zip(args.iter()) {
let arg_val = cx.eval(arg_expr)?;
debug!("const call arg: {:?}", arg);
if let Some(id) = arg {
assert!(call_args.insert(id, arg_val).is_none());
}
}
debug!("const call({:?})", call_args);
let callee_cx = ConstContext {
tcx,
param_env: cx.param_env,
tables: tcx.typeck_tables_of(def_id),
substs,
fn_args: Some(call_args)
};
callee_cx.eval(&body.value)?
},
hir::ExprLit(ref lit) => match lit_to_const(&lit.node, tcx, ty, false) {
Ok(val) => mk_const(val),
Err(err) => signal!(e, err),
},
hir::ExprBlock(ref block) => {
match block.expr {
Some(ref expr) => cx.eval(expr)?,
None => mk_const(Aggregate(Tuple(&[]))),
}
}
hir::ExprType(ref e, _) => cx.eval(e)?,
hir::ExprTup(ref fields) => {
let values = fields.iter().map(|e| cx.eval(e)).collect::<Result<Vec<_>, _>>()?;
mk_const(Aggregate(Tuple(tcx.alloc_const_slice(&values))))
}
hir::ExprStruct(_, ref fields, _) => {
mk_const(Aggregate(Struct(tcx.alloc_name_const_slice(&fields.iter().map(|f| {
cx.eval(&f.expr).map(|v| (f.name.node, v))
}).collect::<Result<Vec<_>, _>>()?))))
}
hir::ExprIndex(ref arr, ref idx) => {
if !tcx.features().const_indexing {
signal!(e, IndexOpFeatureGated);
}
let arr = cx.eval(arr)?;
let idx = match cx.eval(idx)?.val {
Integral(Usize(i)) => i.as_u64(),
_ => signal!(idx, IndexNotUsize),
};
assert_eq!(idx as usize as u64, idx);
match arr.val {
Aggregate(Array(v)) => {
if let Some(&elem) = v.get(idx as usize) {
elem
} else {
let n = v.len() as u64;
signal!(e, IndexOutOfBounds { len: n, index: idx })
}
}
Aggregate(Repeat(.., n)) if idx >= n => {
signal!(e, IndexOutOfBounds { len: n, index: idx })
}
Aggregate(Repeat(elem, _)) => elem,
ByteStr(b) if idx >= b.data.len() as u64 => {
signal!(e, IndexOutOfBounds { len: b.data.len() as u64, index: idx })
}
ByteStr(b) => {
mk_const(Integral(U8(b.data[idx as usize])))
},
_ => signal!(e, IndexedNonVec),
}
}
hir::ExprArray(ref v) => {
let values = v.iter().map(|e| cx.eval(e)).collect::<Result<Vec<_>, _>>()?;
mk_const(Aggregate(Array(tcx.alloc_const_slice(&values))))
}
hir::ExprRepeat(ref elem, _) => {
let n = match ty.sty {
ty::TyArray(_, n) => n.val.unwrap_u64(),
_ => span_bug!(e.span, "typeck error")
};
mk_const(Aggregate(Repeat(cx.eval(elem)?, n)))
},
hir::ExprTupField(ref base, index) => {
if let Aggregate(Tuple(fields)) = cx.eval(base)?.val {
fields[index.node]
} else {
span_bug!(base.span, "{:#?}", cx.eval(base)?.val);
//signal!(base, ExpectedConstTuple);
}
}
hir::ExprField(ref base, field_name) => {
if let Aggregate(Struct(fields)) = cx.eval(base)?.val {
if let Some(&(_, f)) = fields.iter().find(|&&(name, _)| name == field_name.node) {
f
} else {
signal!(e, MissingStructField);
}
} else {
signal!(base, ExpectedConstStruct);
}
}
hir::ExprAddrOf(..) => signal!(e, UnimplementedConstVal("address operator")),
_ => signal!(e, MiscCatchAll)
};
Ok(result)
}
fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
val: ConstInt,
ty: Ty<'tcx>)
-> CastResult<'tcx> {
let v = val.to_u128_unchecked();
match ty.sty {
ty::TyBool if v == 0 => Ok(Bool(false)),
ty::TyBool if v == 1 => Ok(Bool(true)),
ty::TyInt(ast::IntTy::I8) => Ok(Integral(I8(v as i128 as i8))),
ty::TyInt(ast::IntTy::I16) => Ok(Integral(I16(v as i128 as i16))),
ty::TyInt(ast::IntTy::I32) => Ok(Integral(I32(v as i128 as i32))),
ty::TyInt(ast::IntTy::I64) => Ok(Integral(I64(v as i128 as i64))),
ty::TyInt(ast::IntTy::I128) => Ok(Integral(I128(v as i128))),
ty::TyInt(ast::IntTy::Isize) => {
Ok(Integral(Isize(ConstIsize::new_truncating(v as i128, tcx.sess.target.isize_ty))))
},
ty::TyUint(ast::UintTy::U8) => Ok(Integral(U8(v as u8))),
ty::TyUint(ast::UintTy::U16) => Ok(Integral(U16(v as u16))),
ty::TyUint(ast::UintTy::U32) => Ok(Integral(U32(v as u32))),
ty::TyUint(ast::UintTy::U64) => Ok(Integral(U64(v as u64))),
ty::TyUint(ast::UintTy::U128) => Ok(Integral(U128(v as u128))),
ty::TyUint(ast::UintTy::Usize) => {
Ok(Integral(Usize(ConstUsize::new_truncating(v, tcx.sess.target.usize_ty))))
},
ty::TyFloat(fty) => {
if let Some(i) = val.to_u128() {
Ok(Float(ConstFloat::from_u128(i, fty)))
} else {
// The value must be negative, go through signed integers.
let i = val.to_u128_unchecked() as i128;
Ok(Float(ConstFloat::from_i128(i, fty)))
}
}
ty::TyRawPtr(_) => Err(ErrKind::UnimplementedConstVal("casting an address to a raw ptr")),
ty::TyChar => match val {
U8(u) => Ok(Char(u as char)),
_ => bug!(),
},
_ => Err(CannotCast),
}
}
fn cast_const_float<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
val: ConstFloat,
ty: Ty<'tcx>) -> CastResult<'tcx> {
let int_width = |ty| {
ty::layout::Integer::from_attr(tcx, ty).size().bits() as usize
};
match ty.sty {
ty::TyInt(ity) => {
if let Some(i) = val.to_i128(int_width(attr::SignedInt(ity))) {
cast_const_int(tcx, I128(i), ty)
} else {
Err(CannotCast)
}
}
ty::TyUint(uty) => {
if let Some(i) = val.to_u128(int_width(attr::UnsignedInt(uty))) {
cast_const_int(tcx, U128(i), ty)
} else {
Err(CannotCast)
}
}
ty::TyFloat(fty) => Ok(Float(val.convert(fty))),
_ => Err(CannotCast),
}
}
fn cast_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
val: ConstVal<'tcx>,
ty: Ty<'tcx>)
-> CastResult<'tcx> {
match val {
Integral(i) => cast_const_int(tcx, i, ty),
Bool(b) => cast_const_int(tcx, U8(b as u8), ty),
Float(f) => cast_const_float(tcx, f, ty),
Char(c) => cast_const_int(tcx, U32(c as u32), ty),
Variant(v) => {
let adt = tcx.adt_def(tcx.parent_def_id(v).unwrap());
let idx = adt.variant_index_with_id(v);
cast_const_int(tcx, adt.discriminant_for_variant(tcx, idx), ty)
}
Function(..) => Err(UnimplementedConstVal("casting fn pointers")),
ByteStr(b) => match ty.sty {
ty::TyRawPtr(_) => {
Err(ErrKind::UnimplementedConstVal("casting a bytestr to a raw ptr"))
},
ty::TyRef(_, ty::TypeAndMut { ref ty, mutbl: hir::MutImmutable }) => match ty.sty {
ty::TyArray(ty, n) => {
let n = n.val.unwrap_u64();
if ty == tcx.types.u8 && n == b.data.len() as u64 {
Ok(val)
} else {
Err(CannotCast)
}
}
ty::TySlice(_) => {
Err(ErrKind::UnimplementedConstVal("casting a bytestr to slice"))
},
_ => Err(CannotCast),
},
_ => Err(CannotCast),
},
Str(s) => match ty.sty {
ty::TyRawPtr(_) => Err(ErrKind::UnimplementedConstVal("casting a str to a raw ptr")),
ty::TyRef(_, ty::TypeAndMut { ref ty, mutbl: hir::MutImmutable }) => match ty.sty {
ty::TyStr => Ok(Str(s)),
_ => Err(CannotCast),
},
_ => Err(CannotCast),
},
_ => Err(CannotCast),
}
}
pub fn lit_to_const<'a, 'tcx>(lit: &'tcx ast::LitKind,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
mut ty: Ty<'tcx>,
ty: Ty<'tcx>,
neg: bool)
-> Result<ConstVal<'tcx>, ErrKind<'tcx>> {
use syntax::ast::*;
use syntax::ast::LitIntType::*;
if tcx.sess.opts.debugging_opts.miri {
use rustc::mir::interpret::*;
let lit = match *lit {
LitKind::Str(ref s, _) => {
let s = s.as_str();
let id = tcx.allocate_cached(s.as_bytes());
let ptr = MemoryPointer::new(AllocId(id), 0);
Value::ByValPair(
PrimVal::Ptr(ptr),
PrimVal::from_u128(s.len() as u128),
)
},
LitKind::ByteStr(ref data) => {
let id = tcx.allocate_cached(data);
let ptr = MemoryPointer::new(AllocId(id), 0);
Value::ByVal(PrimVal::Ptr(ptr))
},
LitKind::Byte(n) => Value::ByVal(PrimVal::Bytes(n as u128)),
LitKind::Int(n, _) if neg => {
let n = n as i128;
let n = n.overflowing_neg().0;
Value::ByVal(PrimVal::Bytes(n as u128))
},
LitKind::Int(n, _) => Value::ByVal(PrimVal::Bytes(n as u128)),
LitKind::Float(n, fty) => {
let n = n.as_str();
let mut f = parse_float(&n, fty)?;
if neg {
f = -f;
}
let bits = f.bits;
Value::ByVal(PrimVal::Bytes(bits))
use rustc::mir::interpret::*;
let lit = match *lit {
LitKind::Str(ref s, _) => {
let s = s.as_str();
let id = tcx.allocate_cached(s.as_bytes());
let ptr = MemoryPointer::new(id, 0);
Value::ByValPair(
PrimVal::Ptr(ptr),
PrimVal::from_u128(s.len() as u128),
)
},
LitKind::ByteStr(ref data) => {
let id = tcx.allocate_cached(data);
let ptr = MemoryPointer::new(id, 0);
Value::ByVal(PrimVal::Ptr(ptr))
},
LitKind::Byte(n) => Value::ByVal(PrimVal::Bytes(n as u128)),
LitKind::Int(n, _) => {
enum Int {
Signed(IntTy),
Unsigned(UintTy),
}
LitKind::FloatUnsuffixed(n) => {
let fty = match ty.sty {
ty::TyFloat(fty) => fty,
_ => bug!()
};
let n = n.as_str();
let mut f = parse_float(&n, fty)?;
if neg {
f = -f;
}
let bits = f.bits;
Value::ByVal(PrimVal::Bytes(bits))
}
LitKind::Bool(b) => Value::ByVal(PrimVal::Bytes(b as u128)),
LitKind::Char(c) => Value::ByVal(PrimVal::Bytes(c as u128)),
};
return Ok(ConstVal::Value(lit));
}
if let ty::TyAdt(adt, _) = ty.sty {
if adt.is_enum() {
ty = adt.repr.discr_type().to_ty(tcx)
}
}
match *lit {
LitKind::Str(ref s, _) => Ok(Str(s.as_str())),
LitKind::ByteStr(ref data) => Ok(ByteStr(ByteArray { data })),
LitKind::Byte(n) => Ok(Integral(U8(n))),
LitKind::Int(n, hint) => {
match (&ty.sty, hint) {
(&ty::TyInt(ity), _) |
(_, Signed(ity)) => {
let mut n = n as i128;
if neg {
n = n.overflowing_neg().0;
}
Ok(Integral(ConstInt::new_signed_truncating(n,
ity, tcx.sess.target.isize_ty)))
}
(&ty::TyUint(uty), _) |
(_, Unsigned(uty)) => {
Ok(Integral(ConstInt::new_unsigned_truncating(n,
uty, tcx.sess.target.usize_ty)))
}
_ => bug!()
}
}
let ty = match ty.sty {
ty::TyInt(IntTy::Isize) => Int::Signed(tcx.sess.target.isize_ty),
ty::TyInt(other) => Int::Signed(other),
ty::TyUint(UintTy::Usize) => Int::Unsigned(tcx.sess.target.usize_ty),
ty::TyUint(other) => Int::Unsigned(other),
_ => bug!(),
};
let n = match ty {
// FIXME(oli-obk): are these casts correct?
Int::Signed(IntTy::I8) if neg =>
(n as i128 as i8).overflowing_neg().0 as i128 as u128,
Int::Signed(IntTy::I16) if neg =>
(n as i128 as i16).overflowing_neg().0 as i128 as u128,
Int::Signed(IntTy::I32) if neg =>
(n as i128 as i32).overflowing_neg().0 as i128 as u128,
Int::Signed(IntTy::I64) if neg =>
(n as i128 as i64).overflowing_neg().0 as i128 as u128,
Int::Signed(IntTy::I128) if neg =>
(n as i128).overflowing_neg().0 as u128,
Int::Signed(IntTy::I8) => n as i128 as i8 as i128 as u128,
Int::Signed(IntTy::I16) => n as i128 as i16 as i128 as u128,
Int::Signed(IntTy::I32) => n as i128 as i32 as i128 as u128,
Int::Signed(IntTy::I64) => n as i128 as i64 as i128 as u128,
Int::Signed(IntTy::I128) => n,
Int::Unsigned(UintTy::U8) => n as u8 as u128,
Int::Unsigned(UintTy::U16) => n as u16 as u128,
Int::Unsigned(UintTy::U32) => n as u32 as u128,
Int::Unsigned(UintTy::U64) => n as u64 as u128,
Int::Unsigned(UintTy::U128) => n,
_ => bug!(),
};
Value::ByVal(PrimVal::Bytes(n))
},
LitKind::Float(n, fty) => {
let mut f = parse_float(&n.as_str(), fty)?;
let n = n.as_str();
let mut f = parse_float(&n, fty)?;
if neg {
f = -f;
}
Ok(Float(f))
let bits = f.bits;
Value::ByVal(PrimVal::Bytes(bits))
}
LitKind::FloatUnsuffixed(n) => {
let fty = match ty.sty {
ty::TyFloat(fty) => fty,
_ => bug!()
};
let mut f = parse_float(&n.as_str(), fty)?;
let n = n.as_str();
let mut f = parse_float(&n, fty)?;
if neg {
f = -f;
}
Ok(Float(f))
let bits = f.bits;
Value::ByVal(PrimVal::Bytes(bits))
}
LitKind::Bool(b) => Ok(Bool(b)),
LitKind::Char(c) => Ok(Char(c)),
}
LitKind::Bool(b) => Value::ByVal(PrimVal::Bytes(b as u128)),
LitKind::Char(c) => Value::ByVal(PrimVal::Bytes(c as u128)),
};
Ok(ConstVal::Value(lit))
}
fn parse_float<'tcx>(num: &str, fty: ast::FloatTy)
@ -657,41 +137,26 @@ pub fn compare_const_vals(a: &ConstVal, b: &ConstVal, ty: Ty) -> Option<Ordering
trace!("compare_const_vals: {:?}, {:?}", a, b);
use rustc::mir::interpret::{Value, PrimVal};
match (a, b) {
(&Integral(a), &Integral(b)) => a.try_cmp(b).ok(),
(&Char(a), &Char(b)) => Some(a.cmp(&b)),
(&Value(Value::ByVal(PrimVal::Bytes(a))),
&Value(Value::ByVal(PrimVal::Bytes(b)))) => {
Some(if ty.is_signed() {
(a as i128).cmp(&(b as i128))
} else {
a.cmp(&b)
})
match ty.sty {
ty::TyFloat(ty) => {
let l = ConstFloat {
bits: a,
ty,
};
let r = ConstFloat {
bits: b,
ty,
};
// FIXME(oli-obk): report cmp errors?
l.try_cmp(r).ok()
},
ty::TyInt(_) => Some((a as i128).cmp(&(b as i128))),
_ => Some(a.cmp(&b)),
}
},
_ if a == b => Some(Ordering::Equal),
_ => None,
}
}
impl<'a, 'tcx> ConstContext<'a, 'tcx> {
pub fn compare_lit_exprs(&self,
a: &'tcx Expr,
b: &'tcx Expr) -> Result<Option<Ordering>, ErrorReported> {
let tcx = self.tcx;
let ty = self.tables.expr_ty(a);
let a = match self.eval(a) {
Ok(a) => a,
Err(e) => {
e.report(tcx, a.span, "expression");
return Err(ErrorReported);
}
};
let b = match self.eval(b) {
Ok(b) => b,
Err(e) => {
e.report(tcx, b.span, "expression");
return Err(ErrorReported);
}
};
Ok(compare_const_vals(&a.val, &b.val, ty))
}
}

View file

@ -14,5 +14,6 @@ mod eval;
mod _match;
pub mod check_match;
pub mod pattern;
pub mod check;
pub use self::eval::*;

View file

@ -8,10 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use const_eval::eval;
use interpret::{const_val_field, const_discr};
use rustc::middle::const_val::{ConstEvalErr, ConstVal, ConstAggregate};
use rustc::middle::const_val::{ConstEvalErr, ErrKind, ConstVal};
use rustc::mir::{Field, BorrowKind, Mutability};
use rustc::mir::interpret::{Value, PrimVal};
use rustc::ty::{self, TyCtxt, AdtDef, Ty, Region};
@ -19,6 +18,7 @@ use rustc::ty::subst::{Substs, Kind};
use rustc::hir::{self, PatKind, RangeEnd};
use rustc::hir::def::{Def, CtorKind};
use rustc::hir::pat_util::EnumerateAndAdjustIterator;
use const_eval::eval::compare_const_vals;
use rustc_data_structures::indexed_vec::Idx;
@ -114,16 +114,7 @@ pub enum PatternKind<'tcx> {
fn print_const_val(value: &ty::Const, f: &mut fmt::Formatter) -> fmt::Result {
match value.val {
ConstVal::Float(ref x) => write!(f, "{}", x),
ConstVal::Integral(ref i) => write!(f, "{}", i),
ConstVal::Str(ref s) => write!(f, "{:?}", &s[..]),
ConstVal::ByteStr(b) => write!(f, "{:?}", b.data),
ConstVal::Bool(b) => write!(f, "{:?}", b),
ConstVal::Char(c) => write!(f, "{:?}", c),
ConstVal::Value(v) => print_miri_value(v, value.ty, f),
ConstVal::Variant(_) |
ConstVal::Function(..) |
ConstVal::Aggregate(_) |
ConstVal::Unevaluated(..) => bug!("{:?} not printable in a pattern", value)
}
}
@ -366,10 +357,27 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
PatKind::Lit(ref value) => self.lower_lit(value),
PatKind::Range(ref lo, ref hi, end) => {
match (self.lower_lit(lo), self.lower_lit(hi)) {
PatKind::Range(ref lo_expr, ref hi_expr, end) => {
match (self.lower_lit(lo_expr), self.lower_lit(hi_expr)) {
(PatternKind::Constant { value: lo },
PatternKind::Constant { value: hi }) => {
use std::cmp::Ordering;
match (end, compare_const_vals(&lo.val, &hi.val, ty).unwrap()) {
(RangeEnd::Excluded, Ordering::Less) => {},
(RangeEnd::Excluded, _) => span_err!(
self.tcx.sess,
lo_expr.span,
E0579,
"lower range bound must be less than upper",
),
(RangeEnd::Included, Ordering::Greater) => {
struct_span_err!(self.tcx.sess, lo_expr.span, E0030,
"lower range bound must be less than or equal to upper")
.span_label(lo_expr.span, "lower bound larger than upper bound")
.emit();
},
(RangeEnd::Included, _) => {}
}
PatternKind::Range { lo, hi, end }
}
_ => PatternKind::Wild
@ -487,7 +495,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
pattern: self.lower_pattern(field),
})
.collect();
self.lower_variant_or_leaf(def, ty, subpatterns)
self.lower_variant_or_leaf(def, pat.span, ty, subpatterns)
}
PatKind::Struct(ref qpath, ref fields, _) => {
@ -519,7 +527,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
})
.collect();
self.lower_variant_or_leaf(def, ty, subpatterns)
self.lower_variant_or_leaf(def, pat.span, ty, subpatterns)
}
};
@ -610,6 +618,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
fn lower_variant_or_leaf(
&mut self,
def: Def,
span: Span,
ty: Ty<'tcx>,
subpatterns: Vec<FieldPattern<'tcx>>)
-> PatternKind<'tcx>
@ -640,7 +649,13 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
PatternKind::Leaf { subpatterns: subpatterns }
}
_ => bug!()
_ => {
self.errors.push(PatternError::ConstEval(ConstEvalErr {
span,
kind: ErrKind::NonConstPath,
}));
PatternKind::Wild
}
}
}
@ -660,18 +675,13 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
let substs = self.tables.node_substs(id);
match self.tcx.at(span).const_eval(self.param_env.and((def_id, substs))) {
Ok(value) => {
if self.tcx.sess.opts.debugging_opts.miri {
if let ConstVal::Value(_) = value.val {} else {
panic!("const eval produced non-miri value: {:#?}", value);
}
}
let instance = ty::Instance::resolve(
self.tcx,
self.param_env,
def_id,
substs,
).unwrap();
return self.const_to_pat(instance, value, span)
return self.const_to_pat(instance, value, id, span)
},
Err(e) => {
self.errors.push(PatternError::ConstEval(e));
@ -679,7 +689,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
}
}
}
_ => self.lower_variant_or_leaf(def, ty, vec![]),
_ => self.lower_variant_or_leaf(def, span, ty, vec![]),
};
Pattern {
@ -690,68 +700,51 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
}
fn lower_lit(&mut self, expr: &'tcx hir::Expr) -> PatternKind<'tcx> {
if self.tcx.sess.opts.debugging_opts.miri {
return match expr.node {
hir::ExprLit(ref lit) => {
let ty = self.tables.expr_ty(expr);
match super::eval::lit_to_const(&lit.node, self.tcx, ty, false) {
Ok(value) => PatternKind::Constant {
value: self.tcx.mk_const(ty::Const {
ty,
val: value,
}),
},
Err(e) => {
self.errors.push(PatternError::ConstEval(ConstEvalErr {
span: lit.span,
kind: e,
}));
PatternKind::Wild
},
}
},
hir::ExprPath(ref qpath) => *self.lower_path(qpath, expr.hir_id, expr.span).kind,
hir::ExprUnary(hir::UnNeg, ref expr) => {
let ty = self.tables.expr_ty(expr);
let lit = match expr.node {
hir::ExprLit(ref lit) => lit,
_ => span_bug!(expr.span, "not a literal: {:?}", expr),
};
match super::eval::lit_to_const(&lit.node, self.tcx, ty, true) {
Ok(value) => PatternKind::Constant {
value: self.tcx.mk_const(ty::Const {
ty,
val: value,
}),
},
Err(e) => {
self.errors.push(PatternError::ConstEval(ConstEvalErr {
span: lit.span,
kind: e,
}));
PatternKind::Wild
},
}
match expr.node {
hir::ExprLit(ref lit) => {
let ty = self.tables.expr_ty(expr);
match super::eval::lit_to_const(&lit.node, self.tcx, ty, false) {
Ok(val) => {
let instance = ty::Instance::new(
self.tables.local_id_root.expect("literal outside any scope"),
self.substs,
);
let cv = self.tcx.mk_const(ty::Const { val, ty });
*self.const_to_pat(instance, cv, expr.hir_id, lit.span).kind
},
Err(e) => {
self.errors.push(PatternError::ConstEval(ConstEvalErr {
span: lit.span,
kind: e,
}));
PatternKind::Wild
},
}
_ => span_bug!(expr.span, "not a literal: {:?}", expr),
}
}
let const_cx = eval::ConstContext::new(self.tcx,
self.param_env.and(self.substs),
self.tables);
match const_cx.eval(expr) {
Ok(value) => {
if let ConstVal::Variant(def_id) = value.val {
let ty = self.tables.expr_ty(expr);
self.lower_variant_or_leaf(Def::Variant(def_id), ty, vec![])
} else {
PatternKind::Constant { value }
},
hir::ExprPath(ref qpath) => *self.lower_path(qpath, expr.hir_id, expr.span).kind,
hir::ExprUnary(hir::UnNeg, ref expr) => {
let ty = self.tables.expr_ty(expr);
let lit = match expr.node {
hir::ExprLit(ref lit) => lit,
_ => span_bug!(expr.span, "not a literal: {:?}", expr),
};
match super::eval::lit_to_const(&lit.node, self.tcx, ty, true) {
Ok(value) => PatternKind::Constant {
value: self.tcx.mk_const(ty::Const {
ty,
val: value,
}),
},
Err(e) => {
self.errors.push(PatternError::ConstEval(ConstEvalErr {
span: lit.span,
kind: e,
}));
PatternKind::Wild
},
}
}
Err(e) => {
self.errors.push(PatternError::ConstEval(e));
PatternKind::Wild
}
_ => span_bug!(expr.span, "not a literal: {:?}", expr),
}
}
@ -759,14 +752,23 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
&self,
instance: ty::Instance<'tcx>,
cv: &'tcx ty::Const<'tcx>,
id: hir::HirId,
span: Span,
) -> Pattern<'tcx> {
debug!("const_to_pat: cv={:#?}", cv);
let kind = match cv.ty.sty {
ty::TyFloat(_) => {
self.tcx.sess.span_err(span, "floating point constants cannot be used in patterns");
PatternKind::Wild
}
let id = self.tcx.hir.hir_to_node_id(id);
self.tcx.lint_node(
::rustc::lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
id,
span,
"floating-point types cannot be used in patterns",
);
PatternKind::Constant {
value: cv,
}
},
ty::TyAdt(adt_def, _) if adt_def.is_union() => {
// Matching on union fields is unsafe, we can't hide it in constants
self.tcx.sess.span_err(span, "cannot use unions in constant patterns");
@ -803,30 +805,18 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
let field = Field::new(i);
let val = match cv.val {
ConstVal::Value(miri) => const_val_field(
self.tcx, self.param_env, instance, Some(variant_index), field, miri, cv.ty,
self.tcx, self.param_env, instance,
Some(variant_index), field, miri, cv.ty,
).unwrap(),
_ => bug!("{:#?} is not a valid tuple", cv),
};
FieldPattern {
field,
pattern: self.const_to_pat(instance, val, span),
pattern: self.const_to_pat(instance, val, id, span),
}
}).collect(),
}
},
ConstVal::Variant(var_did) => {
let variant_index = adt_def
.variants
.iter()
.position(|var| var.did == var_did)
.unwrap();
PatternKind::Variant {
adt_def,
substs,
variant_index,
subpatterns: Vec::new(),
}
}
_ => return Pattern {
span,
ty: cv.ty,
@ -839,12 +829,9 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
ty::TyAdt(adt_def, _) => {
let struct_var = adt_def.struct_variant();
PatternKind::Leaf {
subpatterns: struct_var.fields.iter().enumerate().map(|(i, f)| {
subpatterns: struct_var.fields.iter().enumerate().map(|(i, _)| {
let field = Field::new(i);
let val = match cv.val {
ConstVal::Aggregate(ConstAggregate::Struct(consts)) => {
consts.iter().find(|&&(name, _)| name == f.name).unwrap().1
},
ConstVal::Value(miri) => const_val_field(
self.tcx, self.param_env, instance, None, field, miri, cv.ty,
).unwrap(),
@ -852,7 +839,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
};
FieldPattern {
field,
pattern: self.const_to_pat(instance, val, span),
pattern: self.const_to_pat(instance, val, id, span),
}
}).collect()
}
@ -862,7 +849,6 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
subpatterns: (0..fields.len()).map(|i| {
let field = Field::new(i);
let val = match cv.val {
ConstVal::Aggregate(ConstAggregate::Tuple(consts)) => consts[i],
ConstVal::Value(miri) => const_val_field(
self.tcx, self.param_env, instance, None, field, miri, cv.ty,
).unwrap(),
@ -870,29 +856,26 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
};
FieldPattern {
field,
pattern: self.const_to_pat(instance, val, span),
pattern: self.const_to_pat(instance, val, id, span),
}
}).collect()
}
}
ty::TyArray(_, n) => {
PatternKind::Leaf {
subpatterns: (0..n.val.unwrap_u64()).map(|i| {
PatternKind::Array {
prefix: (0..n.val.unwrap_u64()).map(|i| {
let i = i as usize;
let field = Field::new(i);
let val = match cv.val {
ConstVal::Aggregate(ConstAggregate::Array(consts)) => consts[i],
ConstVal::Aggregate(ConstAggregate::Repeat(cv, _)) => cv,
ConstVal::Value(miri) => const_val_field(
self.tcx, self.param_env, instance, None, field, miri, cv.ty,
).unwrap(),
_ => bug!("{:#?} is not a valid tuple", cv),
};
FieldPattern {
field,
pattern: self.const_to_pat(instance, val, span),
}
}).collect()
self.const_to_pat(instance, val, id, span)
}).collect(),
slice: None,
suffix: Vec::new(),
}
}
_ => {

View file

@ -325,6 +325,24 @@ match x {
```
"##,
E0030: r##"
When matching against a range, the compiler verifies that the range is
non-empty. Range patterns include both end-points, so this is equivalent to
requiring the start of the range to be less than or equal to the end of the
range.
For example:
```compile_fail
match 5u32 {
// This range is ok, albeit pointless.
1 ... 1 => {}
// This range is empty, and the compiler can tell.
1000 ... 5 => {}
}
```
"##,
E0158: r##"
`const` and `static` mean different things. A `const` is a compile-time
constant, an alias for a literal value. This property means you can match it
@ -2160,6 +2178,24 @@ fn main() {
```
"##,
E0579: r##"
When matching against an exclusive range, the compiler verifies that the range
is non-empty. Exclusive range patterns include the start point but not the end
point, so this is equivalent to requiring the start of the range to be less
than the end of the range.
For example:
```compile_fail
match 5u32 {
// This range is ok, albeit pointless.
1 .. 2 => {}
// This range is empty, and the compiler can tell.
5 .. 5 => {}
}
```
"##,
E0595: r##"
Closures cannot mutate immutable captured variables.

View file

@ -637,7 +637,7 @@ fn method_callee<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
kind: ExprKind::Literal {
literal: Literal::Value {
value: cx.tcx().mk_const(ty::Const {
val: const_fn(cx.tcx, def_id, substs),
val: ConstVal::Value(Value::ByVal(PrimVal::Undef)),
ty
}),
},
@ -677,28 +677,6 @@ fn convert_arm<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, arm: &'tcx hir::Arm)
}
}
fn const_fn<'a, 'gcx, 'tcx>(
tcx: TyCtxt<'a, 'gcx, 'tcx>,
def_id: DefId,
substs: &'tcx Substs<'tcx>,
) -> ConstVal<'tcx> {
if tcx.sess.opts.debugging_opts.miri {
/*
let inst = ty::Instance::new(def_id, substs);
let ptr = tcx
.interpret_interner
.borrow_mut()
.create_fn_alloc(inst);
let ptr = MemoryPointer::new(AllocId(ptr), 0);
ConstVal::Value(Value::ByVal(PrimVal::Ptr(ptr)))
*/
// ZST function type
ConstVal::Value(Value::ByVal(PrimVal::Undef))
} else {
ConstVal::Function(def_id, substs)
}
}
fn convert_path_expr<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
expr: &'tcx hir::Expr,
def: Def)
@ -706,13 +684,13 @@ fn convert_path_expr<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
let substs = cx.tables().node_substs(expr.hir_id);
match def {
// A regular function, constructor function or a constant.
Def::Fn(def_id) |
Def::Method(def_id) |
Def::StructCtor(def_id, CtorKind::Fn) |
Def::VariantCtor(def_id, CtorKind::Fn) => ExprKind::Literal {
Def::Fn(_) |
Def::Method(_) |
Def::StructCtor(_, CtorKind::Fn) |
Def::VariantCtor(_, CtorKind::Fn) => ExprKind::Literal {
literal: Literal::Value {
value: cx.tcx.mk_const(ty::Const {
val: const_fn(cx.tcx.global_tcx(), def_id, substs),
val: ConstVal::Value(Value::ByVal(PrimVal::Undef)),
ty: cx.tables().node_id_to_type(expr.hir_id)
}),
},

View file

@ -25,7 +25,7 @@ use rustc::infer::InferCtxt;
use rustc::ty::subst::Subst;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::subst::Substs;
use syntax::ast;
use syntax::ast::{self, LitKind};
use syntax::attr;
use syntax::symbol::Symbol;
use rustc::hir;
@ -119,11 +119,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
Ok(val) => {
Literal::Value {
value: self.tcx.mk_const(ty::Const {
val: if self.tcx.sess.opts.debugging_opts.miri {
ConstVal::Value(Value::ByVal(PrimVal::Bytes(value as u128)))
} else {
ConstVal::Integral(ConstInt::Usize(val))
},
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(val.as_u64() as u128))),
ty: self.tcx.types.usize
})
}
@ -143,11 +139,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
pub fn true_literal(&mut self) -> Literal<'tcx> {
Literal::Value {
value: self.tcx.mk_const(ty::Const {
val: if self.tcx.sess.opts.debugging_opts.miri {
ConstVal::Value(Value::ByVal(PrimVal::Bytes(1)))
} else {
ConstVal::Bool(true)
},
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(1))),
ty: self.tcx.types.bool
})
}
@ -156,11 +148,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
pub fn false_literal(&mut self) -> Literal<'tcx> {
Literal::Value {
value: self.tcx.mk_const(ty::Const {
val: if self.tcx.sess.opts.debugging_opts.miri {
ConstVal::Value(Value::ByVal(PrimVal::Bytes(0)))
} else {
ConstVal::Bool(false)
},
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(0))),
ty: self.tcx.types.bool
})
}
@ -175,13 +163,6 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
) -> Literal<'tcx> {
let tcx = self.tcx.global_tcx();
let mut repr_ty = ty;
if let ty::TyAdt(adt, _) = ty.sty {
if adt.is_enum() {
repr_ty = adt.repr.discr_type().to_ty(tcx)
}
}
let parse_float = |num: &str, fty| -> ConstFloat {
ConstFloat::from_str(num, fty).unwrap_or_else(|_| {
// FIXME(#31407) this is only necessary because float parsing is buggy
@ -189,128 +170,59 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
})
};
if tcx.sess.opts.debugging_opts.miri {
use rustc::mir::interpret::*;
let lit = match *lit {
LitKind::Str(ref s, _) => {
let s = s.as_str();
let id = self.tcx.allocate_cached(s.as_bytes());
let ptr = MemoryPointer::new(AllocId(id), 0);
Value::ByValPair(
PrimVal::Ptr(ptr),
PrimVal::from_u128(s.len() as u128),
)
},
LitKind::ByteStr(ref data) => {
let id = self.tcx.allocate_cached(data);
let ptr = MemoryPointer::new(AllocId(id), 0);
Value::ByVal(PrimVal::Ptr(ptr))
},
LitKind::Byte(n) => Value::ByVal(PrimVal::Bytes(n as u128)),
LitKind::Int(n, _) if neg => {
let n = n as i128;
let n = n.overflowing_neg().0;
Value::ByVal(PrimVal::Bytes(n as u128))
},
LitKind::Int(n, _) => Value::ByVal(PrimVal::Bytes(n)),
LitKind::Float(n, fty) => {
let n = n.as_str();
let mut f = parse_float(&n, fty);
if neg {
f = -f;
}
let bits = f.bits;
Value::ByVal(PrimVal::Bytes(bits))
}
LitKind::FloatUnsuffixed(n) => {
let fty = match ty.sty {
ty::TyFloat(fty) => fty,
_ => bug!()
};
let n = n.as_str();
let mut f = parse_float(&n, fty);
if neg {
f = -f;
}
let bits = f.bits;
Value::ByVal(PrimVal::Bytes(bits))
}
LitKind::Bool(b) => Value::ByVal(PrimVal::Bytes(b as u128)),
LitKind::Char(c) => Value::ByVal(PrimVal::Bytes(c as u128)),
};
return Literal::Value {
value: self.tcx.mk_const(ty::Const {
val: Value(lit),
ty,
}),
};
}
use syntax::ast::*;
use syntax::ast::LitIntType::*;
use rustc::middle::const_val::ConstVal::*;
use rustc_const_math::ConstInt::*;
use rustc::ty::util::IntTypeExt;
use rustc::middle::const_val::ByteArray;
use rustc_const_math::ConstFloat;
use rustc::mir::interpret::*;
let lit = match *lit {
LitKind::Str(ref s, _) => Ok(Str(s.as_str())),
LitKind::ByteStr(ref data) => {
let data: &'tcx [u8] = data;
Ok(ByteStr(ByteArray { data }))
LitKind::Str(ref s, _) => {
let s = s.as_str();
let id = self.tcx.allocate_cached(s.as_bytes());
let ptr = MemoryPointer::new(id, 0);
Value::ByValPair(
PrimVal::Ptr(ptr),
PrimVal::from_u128(s.len() as u128),
)
},
LitKind::Byte(n) => Ok(Integral(U8(n))),
LitKind::Int(n, hint) => {
match (&repr_ty.sty, hint) {
(&ty::TyInt(ity), _) |
(_, Signed(ity)) => {
let mut n = n as i128;
if neg {
n = n.overflowing_neg().0;
}
Ok(Integral(ConstInt::new_signed_truncating(n,
ity, tcx.sess.target.isize_ty)))
}
(&ty::TyUint(uty), _) |
(_, Unsigned(uty)) => {
Ok(Integral(ConstInt::new_unsigned_truncating(n,
uty, tcx.sess.target.usize_ty)))
}
_ => bug!()
}
}
LitKind::ByteStr(ref data) => {
let id = self.tcx.allocate_cached(data);
let ptr = MemoryPointer::new(id, 0);
Value::ByVal(PrimVal::Ptr(ptr))
},
LitKind::Byte(n) => Value::ByVal(PrimVal::Bytes(n as u128)),
LitKind::Int(n, _) if neg => {
let n = n as i128;
let n = n.overflowing_neg().0;
Value::ByVal(PrimVal::Bytes(n as u128))
},
LitKind::Int(n, _) => Value::ByVal(PrimVal::Bytes(n)),
LitKind::Float(n, fty) => {
let mut f = parse_float(&n.as_str(), fty);
let n = n.as_str();
let mut f = parse_float(&n, fty);
if neg {
f = -f;
}
Ok(ConstVal::Float(f))
let bits = f.bits;
Value::ByVal(PrimVal::Bytes(bits))
}
LitKind::FloatUnsuffixed(n) => {
let fty = match ty.sty {
ty::TyFloat(fty) => fty,
_ => bug!()
};
let mut f = parse_float(&n.as_str(), fty);
let n = n.as_str();
let mut f = parse_float(&n, fty);
if neg {
f = -f;
}
Ok(ConstVal::Float(f))
let bits = f.bits;
Value::ByVal(PrimVal::Bytes(bits))
}
LitKind::Bool(b) => Ok(Bool(b)),
LitKind::Char(c) => Ok(Char(c)),
LitKind::Bool(b) => Value::ByVal(PrimVal::Bytes(b as u128)),
LitKind::Char(c) => Value::ByVal(PrimVal::Bytes(c as u128)),
};
match lit {
Ok(value) => Literal::Value { value: self.tcx.mk_const(ty::Const {
val: value,
Literal::Value {
value: self.tcx.mk_const(ty::Const {
val: ConstVal::Value(lit),
ty,
}) },
Err(kind) => self.fatal_const_eval_err(&ConstEvalErr {
span: sp,
kind,
}, sp, "expression")
}),
}
}
@ -352,12 +264,8 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
return (method_ty,
Literal::Value {
value: self.tcx.mk_const(ty::Const {
val: if self.tcx.sess.opts.debugging_opts.miri {
// ZST function type
ConstVal::Value(Value::ByVal(PrimVal::Undef))
} else {
ConstVal::Function(item.def_id, substs)
},
// ZST function type
val: ConstVal::Value(Value::ByVal(PrimVal::Undef)),
ty: method_ty
}),
});

View file

@ -5,17 +5,13 @@ use rustc::hir::def_id::DefId;
use rustc::mir;
use rustc::middle::const_val::ErrKind::{CheckMatchError, TypeckError};
use rustc::middle::const_val::{ConstEvalErr, ConstVal};
use const_eval::{lookup_const_by_id, ConstContext};
use rustc::mir::Field;
use rustc_data_structures::indexed_vec::Idx;
use const_eval::lookup_const_by_id;
use syntax::ast::Mutability;
use syntax::codemap::Span;
use rustc::mir::interpret::{EvalResult, EvalError, EvalErrorKind, GlobalId, Value, MemoryPointer, Pointer, PrimVal};
use super::{Place, EvalContext, StackPopCleanup, ValTy, HasMemory, PlaceExtra};
use rustc_const_math::ConstInt;
use super::{Place, EvalContext, StackPopCleanup, ValTy, PlaceExtra};
use std::fmt;
use std::error::Error;
@ -43,93 +39,89 @@ pub fn mk_eval_cx<'a, 'tcx>(
pub fn eval_body<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
instance: Instance<'tcx>,
promoted: Option<mir::Promoted>,
param_env: ty::ParamEnv<'tcx>,
) -> EvalResult<'tcx, (Value, Pointer, Ty<'tcx>)> {
eval_body_and_ecx(tcx, instance, promoted, param_env).0
}
pub fn check_body<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
instance: Instance<'tcx>,
promoted: Option<mir::Promoted>,
param_env: ty::ParamEnv<'tcx>,
) {
let (res, ecx) = eval_body_and_ecx(tcx, instance, promoted, param_env);
if let Err(mut err) = res {
ecx.report(&mut err);
}
}
fn eval_body_and_ecx<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
instance: Instance<'tcx>,
promoted: Option<mir::Promoted>,
param_env: ty::ParamEnv<'tcx>,
) -> (EvalResult<'tcx, (Value, Pointer, Ty<'tcx>)>, EvalContext<'a, 'tcx, CompileTimeEvaluator>) {
debug!("eval_body: {:?}, {:?}", instance, param_env);
let limits = super::ResourceLimits::default();
let mut ecx = EvalContext::new(tcx, param_env, limits, CompileTimeEvaluator, ());
let cid = GlobalId {
instance,
promoted: None,
promoted,
};
if ecx.tcx.has_attr(instance.def_id(), "linkage") {
return Err(ConstEvalError::NotConst("extern global".to_string()).into());
}
let instance_ty = instance.ty(tcx);
if tcx.interpret_interner.borrow().get_cached(cid).is_none() {
let mir = ecx.load_mir(instance.def)?;
let layout = ecx.layout_of(instance_ty)?;
assert!(!layout.is_unsized());
let ptr = ecx.memory.allocate(
layout.size.bytes(),
layout.align,
None,
)?;
tcx.interpret_interner.borrow_mut().cache(cid, ptr.alloc_id);
let cleanup = StackPopCleanup::MarkStatic(Mutability::Immutable);
let name = ty::tls::with(|tcx| tcx.item_path_str(instance.def_id()));
trace!("const_eval: pushing stack frame for global: {}", name);
ecx.push_stack_frame(
instance,
mir.span,
mir,
Place::from_ptr(ptr, layout.align),
cleanup.clone(),
)?;
let res = (|| {
if ecx.tcx.has_attr(instance.def_id(), "linkage") {
return Err(ConstEvalError::NotConst("extern global".to_string()).into());
}
let instance_ty = instance.ty(tcx);
if tcx.interpret_interner.borrow().get_cached(cid).is_none() {
let mir = ecx.load_mir(instance.def)?;
let layout = ecx.layout_of(instance_ty)?;
assert!(!layout.is_unsized());
let ptr = ecx.memory.allocate(
layout.size.bytes(),
layout.align,
None,
)?;
tcx.interpret_interner.borrow_mut().cache(cid, ptr.alloc_id);
let cleanup = StackPopCleanup::MarkStatic(Mutability::Immutable);
let name = ty::tls::with(|tcx| tcx.item_path_str(instance.def_id()));
trace!("const_eval: pushing stack frame for global: {}", name);
ecx.push_stack_frame(
instance,
mir.span,
mir,
Place::from_ptr(ptr, layout.align),
cleanup.clone(),
)?;
while ecx.step()? {}
}
let alloc = tcx.interpret_interner.borrow().get_cached(cid).expect("global not cached");
let align = ecx.layout_of(instance_ty)?.align;
let ptr = MemoryPointer::new(alloc, 0).into();
let value = match ecx.try_read_value(ptr, align, instance_ty)? {
Some(val) => val,
_ => Value::ByRef(ptr, align),
};
Ok((value, ptr, instance_ty))
while ecx.step()? {}
}
let alloc = tcx.interpret_interner.borrow().get_cached(cid).expect("global not cached");
let align = ecx.layout_of(instance_ty)?.align;
let ptr = MemoryPointer::new(alloc, 0).into();
let value = match ecx.try_read_value(ptr, align, instance_ty)? {
Some(val) => val,
_ => Value::ByRef(ptr, align),
};
Ok((value, ptr, instance_ty))
})();
(res, ecx)
}
pub fn eval_body_as_integer<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
instance: Instance<'tcx>,
) -> EvalResult<'tcx, ConstInt> {
let (value, _, ty) = eval_body(tcx, instance, param_env)?;
let prim = match value {
Value::ByVal(prim) => prim.to_bytes()?,
_ => return err!(TypeNotPrimitive(ty)),
};
use syntax::ast::{IntTy, UintTy};
use rustc::ty::TypeVariants::*;
use rustc_const_math::{ConstIsize, ConstUsize};
Ok(match ty.sty {
TyInt(IntTy::I8) => ConstInt::I8(prim as i128 as i8),
TyInt(IntTy::I16) => ConstInt::I16(prim as i128 as i16),
TyInt(IntTy::I32) => ConstInt::I32(prim as i128 as i32),
TyInt(IntTy::I64) => ConstInt::I64(prim as i128 as i64),
TyInt(IntTy::I128) => ConstInt::I128(prim as i128),
TyInt(IntTy::Isize) => ConstInt::Isize(
ConstIsize::new(prim as i128 as i64, tcx.sess.target.isize_ty)
.expect("miri should already have errored"),
),
TyUint(UintTy::U8) => ConstInt::U8(prim as u8),
TyUint(UintTy::U16) => ConstInt::U16(prim as u16),
TyUint(UintTy::U32) => ConstInt::U32(prim as u32),
TyUint(UintTy::U64) => ConstInt::U64(prim as u64),
TyUint(UintTy::U128) => ConstInt::U128(prim),
TyUint(UintTy::Usize) => ConstInt::Usize(
ConstUsize::new(prim as u64, tcx.sess.target.usize_ty)
.expect("miri should already have errored"),
),
_ => {
return Err(
ConstEvalError::NeedsRfc(
"evaluating anything other than isize/usize during typeck".to_string(),
).into(),
)
}
})
promoted: Option<mir::Promoted>,
) -> EvalResult<'tcx, u128> {
let (value, _, ty) = eval_body(tcx, instance, promoted, param_env)?;
match value {
Value::ByVal(prim) => prim.to_bytes(),
_ => err!(TypeNotPrimitive(ty)),
}
}
pub struct CompileTimeEvaluator;
@ -337,7 +329,7 @@ fn const_val_field_inner<'a, 'tcx>(
trace!("const_val_field: {:?}, {:?}, {:?}, {:?}", instance, field, value, ty);
let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
let (mut field, ty) = match value {
Value::ByValPair(..) | Value::ByVal(_) => ecx.read_field(value, field, ty)?.expect("const_val_field on non-field"),
Value::ByValPair(..) | Value::ByVal(_) => ecx.read_field(value, variant, field, ty)?.expect("const_val_field on non-field"),
Value::ByRef(ptr, align) => {
let place = Place::Ptr {
ptr,
@ -422,248 +414,13 @@ pub fn const_eval_provider<'a, 'tcx>(
let instance = ty::Instance::new(def_id, substs);
if tcx.sess.opts.debugging_opts.miri {
return match ::interpret::eval_body(tcx, instance, key.param_env) {
Ok((miri_value, _, miri_ty)) => Ok(tcx.mk_const(ty::Const {
val: ConstVal::Value(miri_value),
ty: miri_ty,
})),
Err(err) => {
Err(ConstEvalErr { span: body.value.span, kind: err.into() })
}
};
}
trace!("running old const eval");
let old_result = ConstContext::new(tcx, key.param_env.and(substs), tables).eval(&body.value);
trace!("old const eval produced {:?}", old_result);
trace!("const eval instance: {:?}, {:?}", instance, key.param_env);
let miri_result = ::interpret::eval_body(tcx, instance, key.param_env);
match (miri_result, old_result) {
(Err(err), Ok(ok)) => {
trace!("miri failed, ctfe returned {:?}", ok);
tcx.sess.span_warn(
tcx.def_span(key.value.0),
"miri failed to eval, while ctfe succeeded",
);
let ecx = mk_eval_cx(tcx, instance, key.param_env).unwrap();
let () = unwrap_miri(&ecx, Err(err));
Ok(ok)
},
(Ok((value, _, ty)), Err(_)) => Ok(tcx.mk_const(ty::Const {
val: ConstVal::Value(value),
ty,
match ::interpret::eval_body(tcx, instance, None, key.param_env) {
Ok((miri_value, _, miri_ty)) => Ok(tcx.mk_const(ty::Const {
val: ConstVal::Value(miri_value),
ty: miri_ty,
})),
(Err(_), Err(err)) => Err(err),
(Ok((_, miri_ptr, miri_ty)), Ok(ctfe)) => {
let mut ecx = mk_eval_cx(tcx, instance, key.param_env).unwrap();
let layout = ecx.layout_of(miri_ty).unwrap();
let miri_place = Place::from_primval_ptr(miri_ptr, layout.align);
check_ctfe_against_miri(&mut ecx, miri_place, miri_ty, ctfe.val);
Ok(ctfe)
}
}
}
fn check_ctfe_against_miri<'a, 'tcx>(
ecx: &mut EvalContext<'a, 'tcx, CompileTimeEvaluator>,
miri_place: Place,
miri_ty: Ty<'tcx>,
ctfe: ConstVal<'tcx>,
) {
use rustc::middle::const_val::ConstAggregate::*;
use rustc_const_math::ConstFloat;
use rustc::ty::TypeVariants::*;
let miri_val = ValTy {
value: ecx.read_place(miri_place).unwrap(),
ty: miri_ty
};
match miri_ty.sty {
TyInt(int_ty) => {
let prim = get_prim(ecx, miri_val);
let c = ConstInt::new_signed_truncating(prim as i128,
int_ty,
ecx.tcx.sess.target.isize_ty);
let c = ConstVal::Integral(c);
assert_eq!(c, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", c, ctfe);
},
TyUint(uint_ty) => {
let prim = get_prim(ecx, miri_val);
let c = ConstInt::new_unsigned_truncating(prim,
uint_ty,
ecx.tcx.sess.target.usize_ty);
let c = ConstVal::Integral(c);
assert_eq!(c, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", c, ctfe);
},
TyFloat(ty) => {
let prim = get_prim(ecx, miri_val);
let f = ConstVal::Float(ConstFloat { bits: prim, ty });
assert_eq!(f, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", f, ctfe);
},
TyBool => {
let bits = get_prim(ecx, miri_val);
if bits > 1 {
bug!("miri evaluated to {}, but expected a bool {:?}", bits, ctfe);
}
let b = ConstVal::Bool(bits == 1);
assert_eq!(b, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", b, ctfe);
},
TyChar => {
let bits = get_prim(ecx, miri_val);
if let Some(cm) = ::std::char::from_u32(bits as u32) {
assert_eq!(
ConstVal::Char(cm), ctfe,
"miri evaluated to {:?}, but expected {:?}", cm, ctfe,
);
} else {
bug!("miri evaluated to {}, but expected a char {:?}", bits, ctfe);
}
},
TyStr => {
let value = ecx.follow_by_ref_value(miri_val.value, miri_val.ty);
if let Ok(Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(len))) = value {
let bytes = ecx
.memory
.read_bytes(ptr.into(), len as u64)
.expect("bad miri memory for str");
if let Ok(s) = ::std::str::from_utf8(bytes) {
if let ConstVal::Str(s2) = ctfe {
assert_eq!(s, s2, "miri produced {:?}, but expected {:?}", s, s2);
} else {
bug!("miri produced {:?}, but expected {:?}", s, ctfe);
}
} else {
bug!(
"miri failed to produce valid utf8 {:?}, while ctfe produced {:?}",
bytes,
ctfe,
);
}
} else {
bug!("miri evaluated to {:?}, but expected a str {:?}", value, ctfe);
}
},
TyArray(elem_ty, n) => {
let n = n.val.unwrap_u64();
let vec: Vec<(ConstVal, Ty<'tcx>)> = match ctfe {
ConstVal::ByteStr(arr) => arr.data.iter().map(|&b| {
(ConstVal::Integral(ConstInt::U8(b)), ecx.tcx.types.u8)
}).collect(),
ConstVal::Aggregate(Array(v)) => {
v.iter().map(|c| (c.val, c.ty)).collect()
},
ConstVal::Aggregate(Repeat(v, n)) => {
vec![(v.val, v.ty); n as usize]
},
_ => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe),
};
let layout = ecx.layout_of(miri_ty).unwrap();
for (i, elem) in vec.into_iter().enumerate() {
assert!((i as u64) < n);
let (field_place, _) =
ecx.place_field(miri_place, Field::new(i), layout).unwrap();
check_ctfe_against_miri(ecx, field_place, elem_ty, elem.0);
}
},
TyTuple(..) => {
let vec = match ctfe {
ConstVal::Aggregate(Tuple(v)) => v,
_ => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe),
};
let layout = ecx.layout_of(miri_ty).unwrap();
for (i, elem) in vec.into_iter().enumerate() {
let (field_place, _) =
ecx.place_field(miri_place, Field::new(i), layout).unwrap();
check_ctfe_against_miri(ecx, field_place, elem.ty, elem.val);
}
},
TyAdt(def, _) => {
let mut miri_place = miri_place;
let struct_variant = if def.is_enum() {
let discr = ecx.read_discriminant_value(miri_place, miri_ty).unwrap();
let variant = def.discriminants(ecx.tcx).position(|variant_discr| {
variant_discr.to_u128_unchecked() == discr
}).expect("miri produced invalid enum discriminant");
miri_place = ecx.place_downcast(miri_place, variant).unwrap();
&def.variants[variant]
} else {
def.non_enum_variant()
};
let vec = match ctfe {
ConstVal::Aggregate(Struct(v)) => v,
ConstVal::Variant(did) => {
assert_eq!(struct_variant.fields.len(), 0);
assert_eq!(did, struct_variant.did);
return;
},
ctfe => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe),
};
let layout = ecx.layout_of(miri_ty).unwrap();
for &(name, elem) in vec.into_iter() {
let field = struct_variant.fields.iter().position(|f| f.name == name).unwrap();
let (field_place, _) =
ecx.place_field(miri_place, Field::new(field), layout).unwrap();
check_ctfe_against_miri(ecx, field_place, elem.ty, elem.val);
}
},
TySlice(_) => bug!("miri produced a slice?"),
// not supported by ctfe
TyRawPtr(_) |
TyRef(..) => {}
TyDynamic(..) => bug!("miri produced a trait object"),
TyClosure(..) => bug!("miri produced a closure"),
TyGenerator(..) => bug!("miri produced a generator"),
TyGeneratorWitness(..) => bug!("miri produced a generator witness"),
TyNever => bug!("miri produced a value of the never type"),
TyProjection(_) => bug!("miri produced a projection"),
TyAnon(..) => bug!("miri produced an impl Trait type"),
TyParam(_) => bug!("miri produced an unmonomorphized type"),
TyInfer(_) => bug!("miri produced an uninferred type"),
TyError => bug!("miri produced a type error"),
TyForeign(_) => bug!("miri produced an extern type"),
// should be fine
TyFnDef(..) => {}
TyFnPtr(_) => {
let value = ecx.value_to_primval(miri_val);
let ptr = match value {
Ok(PrimVal::Ptr(ptr)) => ptr,
value => bug!("expected fn ptr, got {:?}", value),
};
let inst = ecx.memory.get_fn(ptr).unwrap();
match ctfe {
ConstVal::Function(did, substs) => {
let ctfe = ty::Instance::resolve(
ecx.tcx,
ecx.param_env,
did,
substs,
).unwrap();
assert_eq!(inst, ctfe, "expected fn ptr {:?}, but got {:?}", ctfe, inst);
},
_ => bug!("ctfe produced {:?}, but miri produced function {:?}", ctfe, inst),
}
},
}
}
fn get_prim<'a, 'tcx>(
ecx: &mut EvalContext<'a, 'tcx, CompileTimeEvaluator>,
val: ValTy<'tcx>,
) -> u128 {
let res = ecx.value_to_primval(val).and_then(|prim| prim.to_bytes());
unwrap_miri(ecx, res)
}
fn unwrap_miri<'a, 'tcx, T>(
ecx: &EvalContext<'a, 'tcx, CompileTimeEvaluator>,
res: Result<T, EvalError<'tcx>>,
) -> T {
match res {
Ok(val) => val,
Err(mut err) => {
ecx.report(&mut err);
ecx.tcx.sess.abort_if_errors();
bug!("{:#?}", err);
Err(err) => {
Err(ConstEvalErr { span: body.value.span, kind: err.into() })
}
}
}

View file

@ -241,39 +241,16 @@ 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> {
use rustc::middle::const_val::ConstVal;
let primval = match *const_val {
ConstVal::Integral(const_int) => PrimVal::Bytes(const_int.to_u128_unchecked()),
ConstVal::Float(val) => PrimVal::Bytes(val.bits),
ConstVal::Bool(b) => PrimVal::from_bool(b),
ConstVal::Char(c) => PrimVal::from_char(c),
ConstVal::Str(ref s) => return self.str_to_value(s),
ConstVal::ByteStr(ref bs) => {
let ptr = self.memory.allocate_cached(bs.data);
PrimVal::Ptr(ptr)
}
match *const_val {
ConstVal::Unevaluated(def_id, substs) => {
let instance = self.resolve(def_id, substs)?;
return Ok(self.read_global_as_value(GlobalId {
Ok(self.read_global_as_value(GlobalId {
instance,
promoted: None,
}, self.layout_of(ty)?));
}, self.layout_of(ty)?))
}
ConstVal::Aggregate(..) |
ConstVal::Variant(_) => bug!("should not have aggregate or variant constants in MIR"),
// function items are zero sized and thus have no readable value
ConstVal::Function(..) => PrimVal::Undef,
ConstVal::Value(val) => return Ok(val),
};
Ok(Value::ByVal(primval))
ConstVal::Value(val) => Ok(val),
}
}
pub(super) fn resolve(&self, def_id: DefId, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, ty::Instance<'tcx>> {

View file

@ -18,6 +18,6 @@ pub use self::place::{Place, PlaceExtra};
pub use self::memory::{Memory, MemoryKind, HasMemory};
pub use self::const_eval::{eval_body_as_integer, eval_body, CompileTimeEvaluator, const_eval_provider, const_val_field, const_discr};
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::machine::Machine;

View file

@ -118,10 +118,14 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
pub fn read_field(
&self,
base: Value,
variant: Option<usize>,
field: mir::Field,
base_ty: Ty<'tcx>,
) -> EvalResult<'tcx, Option<(Value, Ty<'tcx>)>> {
let base_layout = self.layout_of(base_ty)?;
let mut base_layout = self.layout_of(base_ty)?;
if let Some(variant_index) = variant {
base_layout = base_layout.for_variant(self, variant_index);
}
let field_index = field.index();
let field = base_layout.field(self, field_index)?;
let offset = base_layout.fields.offset(field_index);
@ -149,7 +153,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
};
let base_ty = self.place_ty(&proj.base);
match proj.elem {
Field(field, _) => Ok(self.read_field(base, field, base_ty)?.map(|(f, _)| f)),
Field(field, _) => Ok(self.read_field(base, None, field, base_ty)?.map(|(f, _)| f)),
// The NullablePointer cases should work fine, need to take care for normal enums
Downcast(..) |
Subslice { .. } |

View file

@ -16,7 +16,7 @@ use rustc::mir::*;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::subst::{Kind, Subst, Substs};
use rustc::ty::maps::Providers;
use rustc_const_math::{ConstInt, ConstUsize};
use rustc_const_math::ConstUsize;
use rustc::mir::interpret::{Value, PrimVal};
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
@ -444,12 +444,8 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> {
ty: func_ty,
literal: Literal::Value {
value: tcx.mk_const(ty::Const {
val: if tcx.sess.opts.debugging_opts.miri {
// ZST function type
ConstVal::Value(Value::ByVal(PrimVal::Undef))
} else {
ConstVal::Function(self.def_id, substs)
},
// ZST function type
val: ConstVal::Value(Value::ByVal(PrimVal::Undef)),
ty: func_ty
}),
},
@ -512,14 +508,12 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> {
ty: self.tcx.types.usize,
literal: Literal::Value {
value: self.tcx.mk_const(ty::Const {
val: if self.tcx.sess.opts.debugging_opts.miri {
ConstVal::Value(Value::ByVal(PrimVal::Bytes(value.into())))
} else {
val: {
let value = ConstUsize::new(
value,
self.tcx.sess.target.usize_ty,
).unwrap();
ConstVal::Integral(ConstInt::Usize(value))
).unwrap().as_u64();
ConstVal::Value(Value::ByVal(PrimVal::Bytes(value.into())))
},
ty: self.tcx.types.usize,
})
@ -752,12 +746,8 @@ fn build_call_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
ty,
literal: Literal::Value {
value: tcx.mk_const(ty::Const {
val: if tcx.sess.opts.debugging_opts.miri {
// ZST function type
ConstVal::Value(Value::ByVal(PrimVal::Undef))
} else {
ConstVal::Function(def_id, Substs::identity_for_item(tcx, def_id))
},
// ZST function type
val: ConstVal::Value(Value::ByVal(PrimVal::Undef)),
ty
}),
},

View file

@ -19,6 +19,7 @@ use rustc::hir;
use rustc::ty::{self, TyCtxt};
use rustc::mir::*;
use rustc::middle::const_val::ConstVal;
use rustc::mir::interpret::{Value, PrimVal};
use rustc::util::nodemap::FxHashMap;
use rustc_data_structures::indexed_set::IdxSetBuf;
use rustc_data_structures::indexed_vec::Idx;
@ -541,7 +542,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
ty: self.tcx.types.bool,
literal: Literal::Value {
value: self.tcx.mk_const(ty::Const {
val: ConstVal::Bool(val),
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(val as u128))),
ty: self.tcx.types.bool
})
}

View file

@ -68,7 +68,6 @@ use rustc::ty::{self, TyCtxt, AdtDef, Ty, GeneratorInterior};
use rustc::ty::subst::Substs;
use util::dump_mir;
use util::liveness::{self, LivenessMode};
use rustc_const_math::ConstInt;
use rustc_data_structures::indexed_vec::Idx;
use rustc_data_structures::indexed_set::IdxSetBuf;
use std::collections::HashMap;
@ -182,11 +181,7 @@ impl<'a, 'tcx> TransformVisitor<'a, 'tcx> {
ty: self.tcx.types.u32,
literal: Literal::Value {
value: self.tcx.mk_const(ty::Const {
val: if self.tcx.sess.opts.debugging_opts.miri {
ConstVal::Value(Value::ByVal(PrimVal::Bytes(state_disc.into())))
} else {
ConstVal::Integral(ConstInt::U32(state_disc))
},
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(state_disc.into()))),
ty: self.tcx.types.u32
}),
},
@ -703,7 +698,7 @@ fn insert_panic_block<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
ty: tcx.types.bool,
literal: Literal::Value {
value: tcx.mk_const(ty::Const {
val: ConstVal::Bool(false),
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(0))),
ty: tcx.types.bool
}),
},

View file

@ -13,6 +13,7 @@
use rustc::ty::{self, TyCtxt};
use rustc::middle::const_val::ConstVal;
use rustc::mir::*;
use rustc::mir::interpret::{Value, PrimVal};
use transform::{MirPass, MirSource};
use std::borrow::Cow;
@ -56,9 +57,12 @@ impl MirPass for SimplifyBranches {
},
TerminatorKind::Assert { target, cond: Operand::Constant(box Constant {
literal: Literal::Value {
value: &ty::Const { val: ConstVal::Bool(cond), .. }
value: &ty::Const {
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(cond))),
.. }
}, ..
}), expected, .. } if cond == expected => {
}), expected, .. } if (cond == 1) == expected => {
assert!(cond <= 1);
TerminatorKind::Goto { target: target }
},
TerminatorKind::FalseEdges { real_target, .. } => {

View file

@ -950,11 +950,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
ty: self.tcx().types.usize,
literal: Literal::Value {
value: self.tcx().mk_const(ty::Const {
val: if self.tcx().sess.opts.debugging_opts.miri {
ConstVal::Value(Value::ByVal(PrimVal::Bytes(val.into())))
} else {
ConstVal::Integral(self.tcx().const_usize(val))
},
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(val.into()))),
ty: self.tcx().types.usize
})
}

View file

@ -25,12 +25,6 @@
// by borrowck::gather_loans
use rustc::ty::cast::CastKind;
use rustc_mir::const_eval::ConstContext;
use rustc::middle::const_val::ConstEvalErr;
use rustc::middle::const_val::ErrKind::{IndexOpFeatureGated, UnimplementedConstVal, MiscCatchAll};
use rustc::middle::const_val::ErrKind::{ErroneousReferencedConstant, MiscBinaryOp, NonConstPath};
use rustc::middle::const_val::ErrKind::{TypeckError, Math, LayoutError};
use rustc_const_math::{ConstMathErr, Op};
use rustc::hir::def::{Def, CtorKind};
use rustc::hir::def_id::DefId;
use rustc::hir::map::blocks::FnLikeNode;
@ -41,17 +35,13 @@ use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::maps::{queries, Providers};
use rustc::ty::subst::Substs;
use rustc::traits::Reveal;
use rustc::util::common::ErrorReported;
use rustc::util::nodemap::{ItemLocalSet, NodeSet};
use rustc::lint::builtin::CONST_ERR;
use rustc::hir::{self, PatKind, RangeEnd};
use rustc::hir;
use rustc_data_structures::sync::Lrc;
use syntax::ast;
use syntax_pos::{Span, DUMMY_SP};
use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
use std::cmp::Ordering;
pub fn provide(providers: &mut Providers) {
*providers = Providers {
rvalue_promotable_map,
@ -124,32 +114,6 @@ struct CheckCrateVisitor<'a, 'tcx: 'a> {
}
impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> {
fn const_cx(&self) -> ConstContext<'a, 'gcx> {
ConstContext::new(self.tcx, self.param_env.and(self.identity_substs), self.tables)
}
fn check_const_eval(&self, expr: &'gcx hir::Expr) {
if self.tcx.sess.opts.debugging_opts.miri {
return;
}
if let Err(err) = self.const_cx().eval(expr) {
match err.kind {
UnimplementedConstVal(_) => {}
IndexOpFeatureGated => {}
ErroneousReferencedConstant(_) => {}
TypeckError => {}
MiscCatchAll => {}
_ => {
self.tcx.lint_node(CONST_ERR,
expr.id,
expr.span,
&format!("constant evaluation error: {}",
err.description().into_oneline()));
}
}
}
}
// Returns true iff all the values of the type are promotable.
fn type_has_only_promotable_values(&mut self, ty: Ty<'gcx>) -> bool {
ty.is_freeze(self.tcx, self.param_env, DUMMY_SP) &&
@ -199,9 +163,6 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> {
self.identity_substs = Substs::identity_for_item(self.tcx, item_def_id);
let body = self.tcx.hir.body(body_id);
if !self.in_fn {
self.check_const_eval(&body.value);
}
let tcx = self.tcx;
let param_env = self.param_env;
@ -217,54 +178,6 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> {
self.identity_substs = outer_identity_substs;
}
fn visit_pat(&mut self, p: &'tcx hir::Pat) {
match p.node {
PatKind::Lit(ref lit) => {
self.check_const_eval(lit);
}
PatKind::Range(ref start, ref end, RangeEnd::Excluded) => {
match self.const_cx().compare_lit_exprs(start, end) {
Ok(Some(Ordering::Less)) => {}
Ok(Some(Ordering::Equal)) |
Ok(Some(Ordering::Greater)) => {
span_err!(self.tcx.sess,
start.span,
E0579,
"lower range bound must be less than upper");
}
Ok(None) => bug!("ranges must be char or int"),
Err(ErrorReported) => {}
}
}
PatKind::Range(ref start, ref end, RangeEnd::Included) => {
match self.const_cx().compare_lit_exprs(start, end) {
Ok(Some(Ordering::Less)) |
Ok(Some(Ordering::Equal)) => {}
Ok(Some(Ordering::Greater)) => {
let mut err = struct_span_err!(
self.tcx.sess,
start.span,
E0030,
"lower range bound must be less than or equal to upper"
);
err.span_label(start.span, "lower bound larger than upper bound");
if self.tcx.sess.teach(&err.get_code().unwrap()) {
err.note("When matching against a range, the compiler verifies that \
the range is non-empty. Range patterns include both \
end-points, so this is equivalent to requiring the start of \
the range to be less than or equal to the end of the range.");
}
err.emit();
}
Ok(None) => bug!("ranges must be char or int"),
Err(ErrorReported) => {}
}
}
_ => {}
}
intravisit::walk_pat(self, p);
}
fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt) {
match stmt.node {
hir::StmtDecl(ref decl, _) => {
@ -313,30 +226,6 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> {
self.promotable = false;
}
if self.in_fn && self.promotable && !self.tcx.sess.opts.debugging_opts.miri {
match self.const_cx().eval(ex) {
Ok(_) => {}
Err(ConstEvalErr { kind: UnimplementedConstVal(_), .. }) |
Err(ConstEvalErr { kind: MiscCatchAll, .. }) |
Err(ConstEvalErr { kind: MiscBinaryOp, .. }) |
Err(ConstEvalErr { kind: NonConstPath, .. }) |
Err(ConstEvalErr { kind: ErroneousReferencedConstant(_), .. }) |
Err(ConstEvalErr { kind: Math(ConstMathErr::Overflow(Op::Shr)), .. }) |
Err(ConstEvalErr { kind: Math(ConstMathErr::Overflow(Op::Shl)), .. }) |
Err(ConstEvalErr { kind: IndexOpFeatureGated, .. }) => {}
Err(ConstEvalErr { kind: TypeckError, .. }) => {}
Err(ConstEvalErr {
kind: LayoutError(ty::layout::LayoutError::Unknown(_)), ..
}) => {}
Err(msg) => {
self.tcx.lint_node(CONST_ERR,
ex.id,
msg.span,
&msg.description().into_oneline().into_owned());
}
}
}
if self.promotable {
self.result.insert(ex.hir_id.local_id);
}

View file

@ -31,23 +31,6 @@ const FOO2: i32 = { 0 }; // but brackets are useless here
```
"##,
*/
E0030: r##"
When matching against a range, the compiler verifies that the range is
non-empty. Range patterns include both end-points, so this is equivalent to
requiring the start of the range to be less than or equal to the end of the
range.
For example:
```compile_fail
match 5u32 {
// This range is ok, albeit pointless.
1 ... 1 => {}
// This range is empty, and the compiler can tell.
1000 ... 5 => {}
}
```
"##,
E0130: r##"
You declared a pattern as an argument in a foreign function declaration.
@ -228,24 +211,6 @@ impl Foo for Bar {
"##,
E0579: r##"
When matching against an exclusive range, the compiler verifies that the range
is non-empty. Exclusive range patterns include the start point but not the end
point, so this is equivalent to requiring the start of the range to be less
than the end of the range.
For example:
```compile_fail
match 5u32 {
// This range is ok, albeit pointless.
1 .. 2 => {}
// This range is empty, and the compiler can tell.
5 .. 5 => {}
}
```
"##,
E0590: r##"
`break` or `continue` must include a label when used in the condition of a
`while` loop.

View file

@ -119,7 +119,6 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> {
}),
ref args, ..
} => match val {
ConstVal::Function(def_id, _) => Some((def_id, args)),
ConstVal::Value(Value::ByVal(PrimVal::Undef)) => match ty.sty {
ty::TyFnDef(did, _) => Some((did, args)),
_ => None,

View file

@ -10,7 +10,6 @@
use llvm::{self, ValueRef};
use rustc::middle::const_val::{ConstEvalErr, ConstVal, ErrKind};
use rustc_const_math::ConstInt::*;
use rustc_const_math::{ConstInt, ConstMathErr, MAX_F32_PLUS_HALF_ULP};
use rustc::hir::def_id::DefId;
use rustc::infer::TransNormalize;
@ -64,25 +63,6 @@ impl<'a, 'tcx> Const<'tcx> {
}
}
pub fn from_constint(cx: &CodegenCx<'a, 'tcx>, ci: &ConstInt) -> Const<'tcx> {
let tcx = cx.tcx;
let (llval, ty) = match *ci {
I8(v) => (C_int(Type::i8(cx), v as i64), tcx.types.i8),
I16(v) => (C_int(Type::i16(cx), v as i64), tcx.types.i16),
I32(v) => (C_int(Type::i32(cx), v as i64), tcx.types.i32),
I64(v) => (C_int(Type::i64(cx), v as i64), tcx.types.i64),
I128(v) => (C_uint_big(Type::i128(cx), v as u128), tcx.types.i128),
Isize(v) => (C_int(Type::isize(cx), v.as_i64()), tcx.types.isize),
U8(v) => (C_uint(Type::i8(cx), v as u64), tcx.types.u8),
U16(v) => (C_uint(Type::i16(cx), v as u64), tcx.types.u16),
U32(v) => (C_uint(Type::i32(cx), v as u64), tcx.types.u32),
U64(v) => (C_uint(Type::i64(cx), v), tcx.types.u64),
U128(v) => (C_uint_big(Type::i128(cx), v), tcx.types.u128),
Usize(v) => (C_uint(Type::isize(cx), v.as_u64()), tcx.types.usize),
};
Const { llval: llval, ty: ty }
}
pub fn from_bytes(ccx: &CrateContext<'a, 'tcx>, b: u128, ty: Ty<'tcx>) -> Const<'tcx> {
let llval = match ty.sty {
ty::TyInt(ast::IntTy::I128) |
@ -124,26 +104,7 @@ impl<'a, 'tcx> Const<'tcx> {
let llty = cx.layout_of(ty).llvm_type(cx);
trace!("from_constval: {:#?}: {}", cv, ty);
let val = match *cv {
ConstVal::Float(v) => {
let bits = match v.ty {
ast::FloatTy::F32 => C_u32(cx, v.bits as u32),
ast::FloatTy::F64 => C_u64(cx, v.bits as u64)
};
consts::bitcast(bits, llty)
}
ConstVal::Bool(v) => C_bool(cx, v),
ConstVal::Integral(ref i) => return Const::from_constint(cx, i),
ConstVal::Str(ref v) => C_str_slice(cx, v.clone()),
ConstVal::ByteStr(v) => {
consts::addr_of(cx, C_bytes(cx, v.data), cx.align_of(ty), "byte_str")
}
ConstVal::Char(c) => C_uint(Type::char(cx), c as u64),
ConstVal::Function(..) => C_undef(llty),
ConstVal::Variant(_) |
ConstVal::Aggregate(..) |
ConstVal::Unevaluated(..) => {
bug!("MIR must not use `{:?}` (aggregates are expanded to MIR rvalues)", cv)
}
ConstVal::Unevaluated(..) => unimplemented!("const val `{:?}`", cv),
ConstVal::Value(MiriValue::ByRef(..)) => unimplemented!("{:#?}:{}", cv, ty),
ConstVal::Value(MiriValue::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(len))) => {
match ty.sty {
@ -157,7 +118,7 @@ impl<'a, 'tcx> Const<'tcx> {
.tcx()
.interpret_interner
.borrow()
.get_alloc(ptr.alloc_id.0)
.get_alloc(ptr.alloc_id)
.expect("miri alloc not found");
assert_eq!(len as usize as u128, len);
let slice = &alloc.bytes[(ptr.offset as usize)..][..(len as usize)];
@ -174,7 +135,7 @@ impl<'a, 'tcx> Const<'tcx> {
.tcx()
.interpret_interner
.borrow()
.get_alloc(ptr.alloc_id.0)
.get_alloc(ptr.alloc_id)
.expect("miri alloc not found");
let data = &alloc.bytes[(ptr.offset as usize)..];
consts::addr_of(ccx, C_bytes(ccx, data), ccx.align_of(ty), "byte_str")

View file

@ -533,7 +533,6 @@ fn convert_enum_variant_types<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
}
match result {
Ok(&ty::Const { val: ConstVal::Integral(x), .. }) => Some(x),
Ok(&ty::Const {
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))),
..

View file

@ -12,6 +12,5 @@ fn main() {
// FIXME(#31407) this error should go away, but in the meantime we test that it
// is accompanied by a somewhat useful error message.
let _: f64 = 1234567890123456789012345678901234567890e-340;
//~^ ERROR constant evaluation error
//~| unimplemented constant expression: could not evaluate float literal
//~^ ERROR could not evaluate float literal (see issue #31407)
}

View file

@ -22,7 +22,9 @@ impl Dim for Dim3 {
fn main() {
let array: [usize; Dim3::dim()]
//~^ ERROR calls in constants are limited to constant functions
//~^ ERROR E0015
//~| ERROR E0080
= [0; Dim3::dim()];
//~^ ERROR calls in constants are limited to constant functions
//~^ ERROR E0015
//~| ERROR E0080
}

View file

@ -18,33 +18,33 @@
fn main() {
let x = 42.0;
match x {
5.0 => {}, //~ ERROR floating-point literals cannot be used
5.0 => {}, //~ ERROR floating-point cannot be used
//~| WARNING hard error
5.0f32 => {}, //~ ERROR floating-point literals cannot be used
5.0f32 => {}, //~ ERROR floating-point cannot be used
//~| WARNING hard error
-5.0 => {}, //~ ERROR floating-point literals cannot be used
-5.0 => {}, //~ ERROR floating-point cannot be used
//~| WARNING hard error
1.0 .. 33.0 => {}, //~ ERROR floating-point literals cannot be used
1.0 .. 33.0 => {}, //~ ERROR floating-point cannot be used
//~| WARNING hard error
//~| ERROR floating-point literals cannot be used
//~| ERROR floating-point cannot be used
//~| WARNING hard error
39.0 ... 70.0 => {}, //~ ERROR floating-point literals cannot be used
39.0 ... 70.0 => {}, //~ ERROR floating-point cannot be used
//~| WARNING hard error
//~| ERROR floating-point literals cannot be used
//~| ERROR floating-point cannot be used
//~| WARNING hard error
_ => {},
};
let y = 5.0;
// Same for tuples
match (x, 5) {
(3.14, 1) => {}, //~ ERROR floating-point literals cannot be used
(3.14, 1) => {}, //~ ERROR floating-point cannot be used
//~| WARNING hard error
_ => {},
}
// Or structs
struct Foo { x: f32 };
match (Foo { x }) {
Foo { x: 2.0 } => {}, //~ ERROR floating-point literals cannot be used
Foo { x: 2.0 } => {}, //~ ERROR floating-point cannot be used
//~| WARNING hard error
_ => {},
}

View file

@ -53,7 +53,7 @@ fn main() {
let n = n << 8; //~ ERROR: bitshift exceeds the type's number of bits
let n = 1u8 << -8; //~ ERROR: bitshift exceeds the type's number of bits
//~^ WARN: attempt to shift by a negative amount
let n = 1u8 << (4+3);
let n = 1u8 << (4+4); //~ ERROR: bitshift exceeds the type's number of bits

View file

@ -17,7 +17,6 @@
#[rustc_error]
fn main() { //~ ERROR: compilation successful
let x2: i8 = --128; //~ warn: literal out of range for i8
//~^ warn: attempt to negate with overflow
let x = -3.40282357e+38_f32; //~ warn: literal out of range for f32
let x = 3.40282357e+38_f32; //~ warn: literal out of range for f32

View file

@ -28,7 +28,8 @@ fn main() {
let x = 0.0;
match x {
f32::INFINITY => { }
//~^ ERROR floating point constants cannot be used in patterns
//~^ WARNING floating-point cannot be used in patterns
//~| WARNING will become a hard error in a future release
_ => { }
}
}

View file

@ -16,7 +16,6 @@ static A: u32 = 1;
static B: u32 = A;
//~^ ERROR thread-local statics cannot be accessed at compile-time
//~| ERROR cannot refer to other statics by value
//~| WARN non-constant path in constant expression
static C: &u32 = &A;
//~^ ERROR thread-local statics cannot be accessed at compile-time
@ -24,7 +23,6 @@ static C: &u32 = &A;
const D: u32 = A;
//~^ ERROR thread-local statics cannot be accessed at compile-time
//~| ERROR cannot refer to statics by value
//~| WARN non-constant path in constant expression
const E: &u32 = &A;
//~^ ERROR thread-local statics cannot be accessed at compile-time