Nuke the entire ctfe from orbit, it's the only way to be sure
This commit is contained in:
parent
0b9db5716f
commit
28572d2c1f
53 changed files with 629 additions and 2066 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)];
|
||||
|
|
|
|||
|
|
@ -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
|
||||
})))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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))),
|
||||
..
|
||||
|
|
|
|||
|
|
@ -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))),
|
||||
..
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()));
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
167
src/librustc_mir/const_eval/check.rs
Normal file
167
src/librustc_mir/const_eval/check.rs
Normal 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");
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,5 +14,6 @@ mod eval;
|
|||
mod _match;
|
||||
pub mod check_match;
|
||||
pub mod pattern;
|
||||
pub mod check;
|
||||
|
||||
pub use self::eval::*;
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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() })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>> {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 { .. } |
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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, .. } => {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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))),
|
||||
..
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
_ => {},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
_ => { }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue