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);
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,
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)]
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,
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;
#[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),
}
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)
},
}
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) {
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};
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
})
},
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),
}
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)];
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;
/// 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.
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,
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
})))
}
use errors::DiagnosticBuilder;
use syntax_pos::Span;
-use rustc_const_math::ConstInt;
-
use hir;
#[derive(Clone, Copy, Debug)]
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(),
// 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};
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);
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))),
..
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))),
..
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.
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))),
..
//! 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;
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))
}
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),
}
}
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)
//! 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;
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());
}
// 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;
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);
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;
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)?;
}
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()));
}
}
-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,
extern crate syntax_pos;
use rustc::lint;
-use rustc::middle;
use rustc::session;
use rustc::util;
UnusedParens,
UnusedImportBraces,
AnonymousParameters,
- IllegalFloatLiteralPattern,
UnusedDocComment,
);
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};
"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,
impl LintPass for TypeLimits {
fn get_lints(&self) -> LintArray {
lint_array!(UNUSED_COMPARISONS,
- OVERFLOWING_LITERALS,
- EXCEEDING_BITSHIFTS)
+ OVERFLOWING_LITERALS)
}
}
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 {
}
}
- 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,
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);
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)
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)
// 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(())
}
// 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)) => {
},
..
}) => 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()
})
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
}),
},
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
})
}
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
})
}
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)
.tcx()
.interpret_interner
.borrow()
- .get_alloc(p.alloc_id.0)
+ .get_alloc(p.alloc_id)
.map(|alloc| &alloc.bytes[..])
} else {
None
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> {
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)
- }
- };
-
- 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)
- }
- };
-
+ 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)
- },
+ val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(0))),
ty
})
}
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;
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 }
} => {
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
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()
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))),
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);
}
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 }) => {
tcx
.interpret_interner
.borrow()
- .get_alloc(ptr.alloc_id.0)
+ .get_alloc(ptr.alloc_id)
.unwrap()
.bytes
.as_ref()
{
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 {
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)
let data_len = cx.tcx
.interpret_interner
.borrow()
- .get_alloc(ptr.alloc_id.0)
+ .get_alloc(ptr.alloc_id)
.unwrap()
.bytes
.len();
--- /dev/null
+// 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");
+ }
+ }
+ _ => {}
+ }
+ }
+}
// 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.
).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))
- }
- 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!()
- }
- }
+ 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),
+ }
+ 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)
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))
- }
-}
mod _match;
pub mod check_match;
pub mod pattern;
+pub mod check;
pub use self::eval::*;
// 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};
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;
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)
}
}
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
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, _) => {
})
.collect();
- self.lower_variant_or_leaf(def, ty, subpatterns)
+ self.lower_variant_or_leaf(def, pat.span, ty, subpatterns)
}
};
fn lower_variant_or_leaf(
&mut self,
def: Def,
+ span: Span,
ty: Ty<'tcx>,
subpatterns: Vec<FieldPattern<'tcx>>)
-> PatternKind<'tcx>
PatternKind::Leaf { subpatterns: subpatterns }
}
- _ => bug!()
+ _ => {
+ self.errors.push(PatternError::ConstEval(ConstEvalErr {
+ span,
+ kind: ErrKind::NonConstPath,
+ }));
+ PatternKind::Wild
+ }
}
}
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));
}
}
}
- _ => self.lower_variant_or_leaf(def, ty, vec![]),
+ _ => self.lower_variant_or_leaf(def, span, ty, vec![]),
};
Pattern {
}
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),
}
}
&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");
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,
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(),
};
FieldPattern {
field,
- pattern: self.const_to_pat(instance, val, span),
+ pattern: self.const_to_pat(instance, val, id, span),
}
}).collect()
}
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(),
};
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(),
}
}
_ => {
```
"##,
+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
```
"##,
+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.
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
}),
},
}
}
-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)
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)
}),
},
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;
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
})
}
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
})
}
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
})
}
) -> 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
})
};
- 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::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::ByteStr(ref data) => {
- let data: &'tcx [u8] = data;
- Ok(ByteStr(ByteArray { data }))
+ let id = self.tcx.allocate_cached(data);
+ let ptr = MemoryPointer::new(id, 0);
+ Value::ByVal(PrimVal::Ptr(ptr))
},
- 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::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")
+ }),
}
}
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
}),
});
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;
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(),
- )?;
-
- 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))
+ 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))
+ })();
+ (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;
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,
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() })
}
}
}
}
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>> {
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;
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);
};
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 { .. } |
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};
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
}),
},
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,
})
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
}),
},
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;
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
})
}
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;
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
}),
},
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
}),
},
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;
},
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, .. } => {
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
})
}
// 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;
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,
}
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) &&
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;
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, _) => {
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);
}
```
"##,
*/
-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.
"##,
-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.
}),
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,
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;
}
}
- 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) |
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 {
.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)];
.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")
}
match result {
- Ok(&ty::Const { val: ConstVal::Integral(x), .. }) => Some(x),
Ok(&ty::Const {
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))),
..
// 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)
}
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
}
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
_ => {},
}
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
#[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
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
_ => { }
}
}
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
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