From 38b5ddd39b22dbdf9f004090146c561327ed51e7 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 16 Jan 2018 09:12:54 +0100 Subject: [PATCH 001/110] Add a variant to ConstVal for storing miri results --- src/librustc/ich/impls_ty.rs | 24 +++++++++++ src/librustc/lib.rs | 1 + src/librustc/middle/const_val.rs | 3 ++ src/librustc/mir/interpret/mod.rs | 11 ++++-- src/librustc/mir/interpret/value.rs | 8 ++-- src/librustc/mir/mod.rs | 3 +- src/librustc/ty/context.rs | 19 +++++++++ src/librustc/ty/flags.rs | 1 + src/librustc/ty/instance.rs | 4 +- src/librustc/ty/layout.rs | 2 +- src/librustc/ty/structural_impls.rs | 2 + src/librustc/ty/walk.rs | 1 + src/librustc/ty/wf.rs | 1 + src/librustc_const_eval/pattern.rs | 1 + src/librustc_metadata/decoder.rs | 46 +++++++++++++++++++++- src/librustc_metadata/encoder.rs | 39 +++++++++++++++++- src/librustc_mir/interpret/eval_context.rs | 23 +++++------ src/librustc_trans/mir/constant.rs | 1 + 18 files changed, 165 insertions(+), 25 deletions(-) diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index a8ed885e78d55..06a60bfb0eba6 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -21,6 +21,7 @@ use std::mem; use middle::region; use traits; use ty; +use mir; impl<'gcx, T> HashStable> for &'gcx ty::Slice @@ -388,6 +389,9 @@ for ::middle::const_val::ConstVal<'gcx> { def_id.hash_stable(hcx, hasher); substs.hash_stable(hcx, hasher); } + Value(ref value) => { + value.hash_stable(hcx, hasher); + } } } } @@ -396,6 +400,26 @@ impl_stable_hash_for!(struct ::middle::const_val::ByteArray<'tcx> { data }); +impl_stable_hash_for!(enum mir::interpret::Value { + ByVal(v), + ByValPair(a, b), + ByRef(ptr, align) +}); + +impl_stable_hash_for!(struct mir::interpret::MemoryPointer { + alloc_id, + offset +}); + +impl_stable_hash_for!(tuple_struct mir::interpret::AllocId{id}); +impl_stable_hash_for!(struct mir::interpret::Pointer{primval}); + +impl_stable_hash_for!(enum mir::interpret::PrimVal { + Bytes(b), + Ptr(p), + Undef +}); + impl_stable_hash_for!(struct ty::Const<'tcx> { ty, val diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index d08a41010ab16..56de2939ffae1 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -49,6 +49,7 @@ #![feature(core_intrinsics)] #![feature(drain_filter)] #![feature(dyn_trait)] +#![feature(entry_or_default)] #![feature(from_ref)] #![feature(fs_read_write)] #![feature(i128)] diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index 440af39a0d469..2309131c57a9f 100644 --- a/src/librustc/middle/const_val.rs +++ b/src/librustc/middle/const_val.rs @@ -14,6 +14,7 @@ use hir::def_id::DefId; use ty::{self, TyCtxt, layout}; use ty::subst::Substs; use rustc_const_math::*; +use mir::interpret::Value; use graphviz::IntoCow; use errors::DiagnosticBuilder; @@ -38,6 +39,8 @@ pub enum ConstVal<'tcx> { Function(DefId, &'tcx Substs<'tcx>), Aggregate(ConstAggregate<'tcx>), Unevaluated(DefId, &'tcx Substs<'tcx>), + /// A miri value, currently only produced if old ctfe fails, but miri succeeds + Value(Value), } #[derive(Copy, Clone, Debug, Hash, RustcEncodable, Eq, PartialEq)] diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs index a80695ec9b987..8b4f56e1dba5f 100644 --- a/src/librustc/mir/interpret/mod.rs +++ b/src/librustc/mir/interpret/mod.rs @@ -41,7 +41,7 @@ pub enum AccessKind { } /// Uniquely identifies a specific constant or static. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, RustcEncodable, RustcDecodable)] pub struct GlobalId<'tcx> { /// For a constant or static, the `Instance` of the item itself. /// For a promoted global, the `Instance` of the function they belong to. @@ -101,7 +101,7 @@ pub trait PointerArithmetic: layout::HasDataLayout { impl PointerArithmetic for T {} -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash)] pub struct MemoryPointer { pub alloc_id: AllocId, pub offset: u64, @@ -148,13 +148,16 @@ impl<'tcx> MemoryPointer { #[derive(Copy, Clone, Default, Eq, Hash, Ord, PartialEq, PartialOrd, Debug)] pub struct AllocId(pub u64); +impl ::rustc_serialize::UseSpecializedEncodable for AllocId {} +impl ::rustc_serialize::UseSpecializedDecodable for AllocId {} + impl fmt::Display for AllocId { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) } } -#[derive(Debug, Eq, PartialEq, Hash)] +#[derive(Debug, Eq, PartialEq, Hash, RustcEncodable, RustcDecodable)] pub struct Allocation { /// The actual bytes of the allocation. /// Note that the bytes of a pointer represent the offset of the pointer @@ -188,7 +191,7 @@ impl Allocation { type Block = u64; const BLOCK_SIZE: u64 = 64; -#[derive(Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Clone, Debug, Eq, PartialEq, Hash, RustcEncodable, RustcDecodable)] pub struct UndefMask { blocks: Vec, len: u64, diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc/mir/interpret/value.rs index 0bfff2a80e678..8d67856c0df8d 100644 --- a/src/librustc/mir/interpret/value.rs +++ b/src/librustc/mir/interpret/value.rs @@ -29,7 +29,7 @@ pub fn bytes_to_f64(bits: u128) -> ConstFloat { /// For optimization of a few very common cases, there is also a representation for a pair of /// primitive values (`ByValPair`). It allows Miri to avoid making allocations for checked binary /// operations and fat pointers. This idea was taken from rustc's trans. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash)] pub enum Value { ByRef(Pointer, Align), ByVal(PrimVal), @@ -43,9 +43,9 @@ pub enum Value { /// I (@oli-obk) believe it is less easy to mix up generic primvals and primvals that are just /// the representation of pointers. Also all the sites that convert between primvals and pointers /// are explicit now (and rare!) -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash)] pub struct Pointer { - primval: PrimVal, + pub primval: PrimVal, } impl<'tcx> Pointer { @@ -138,7 +138,7 @@ impl ::std::convert::From for Pointer { /// `memory::Allocation`. It is in many ways like a small chunk of a `Allocation`, up to 8 bytes in /// size. Like a range of bytes in an `Allocation`, a `PrimVal` can either represent the raw bytes /// of a simple value, a pointer into another `Allocation`, or be undefined. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash)] pub enum PrimVal { /// The raw bytes of a simple value. Bytes(u128), diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 7c9feb506afd0..4c1b8cb79ed1a 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -1881,7 +1881,8 @@ fn fmt_const_val(fmt: &mut W, const_val: &ConstVal) -> fmt::Result { 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) + Unevaluated(..) => write!(fmt, "{:?}", const_val), + Value(val) => write!(fmt, "{:?}", val), } } diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 47a3580e86769..520da34c40acb 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -905,6 +905,11 @@ pub struct InterpretInterner<'tcx> { /// Allows obtaining const allocs via a unique identifier alloc_by_id: FxHashMap, + /// Reverse map of `alloc_cache` + /// + /// Multiple globals may share the same memory + global_cache: FxHashMap>>, + /// The AllocId to assign to the next new regular allocation. /// Always incremented, never gets smaller. next_id: interpret::AllocId, @@ -955,11 +960,25 @@ impl<'tcx> InterpretInterner<'tcx> { global_id: interpret::GlobalId<'tcx>, ptr: 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) { bug!("tried to cache {:?}, but was already existing as {:#?}", global_id, old); } } + pub fn get_globals( + &self, + ptr: interpret::Pointer, + ) -> &[interpret::GlobalId<'tcx>] { + match self.global_cache.get(&ptr) { + Some(v) => v, + None => &[], + } + } + pub fn intern_at_reserved( &mut self, id: interpret::AllocId, diff --git a/src/librustc/ty/flags.rs b/src/librustc/ty/flags.rs index 2889322a1ce77..60bf4afc2fc88 100644 --- a/src/librustc/ty/flags.rs +++ b/src/librustc/ty/flags.rs @@ -224,6 +224,7 @@ impl FlagComputation { ConstVal::ByteStr(_) | ConstVal::Bool(_) | ConstVal::Char(_) | + ConstVal::Value(_) | ConstVal::Variant(_) => {} ConstVal::Function(_, substs) => { self.add_substs(substs); diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs index a5f0abb9bc05c..614158bafa638 100644 --- a/src/librustc/ty/instance.rs +++ b/src/librustc/ty/instance.rs @@ -17,13 +17,13 @@ use util::ppaux; use std::fmt; -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] pub struct Instance<'tcx> { pub def: InstanceDef<'tcx>, pub substs: &'tcx Substs<'tcx>, } -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] pub enum InstanceDef<'tcx> { Item(DefId), Intrinsic(DefId), diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 5069c59562678..1aa7f671ad391 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -342,7 +342,7 @@ impl AddAssign for Size { /// Each field is a power of two, giving the alignment a maximum /// value of 2(28 - 1), which is limited by LLVM to a i32, with /// a maximum capacity of 231 - 1 or 2147483647. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] pub struct Align { abi: u8, pref: u8, diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 055835ed69c1d..3e7468d2ccab4 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -1249,6 +1249,7 @@ impl<'tcx> TypeFoldable<'tcx> for ConstVal<'tcx> { 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)) @@ -1304,6 +1305,7 @@ impl<'tcx> TypeFoldable<'tcx> for ConstVal<'tcx> { ConstVal::ByteStr(_) | ConstVal::Bool(_) | ConstVal::Char(_) | + ConstVal::Value(_) | ConstVal::Variant(_) => false, ConstVal::Function(_, substs) => substs.visit_with(visitor), ConstVal::Aggregate(ConstAggregate::Struct(fields)) => { diff --git a/src/librustc/ty/walk.rs b/src/librustc/ty/walk.rs index 45f0ad1cf1a5f..4ef7706c45e3e 100644 --- a/src/librustc/ty/walk.rs +++ b/src/librustc/ty/walk.rs @@ -146,6 +146,7 @@ fn push_const<'tcx>(stack: &mut TypeWalkerStack<'tcx>, constant: &'tcx ty::Const ConstVal::ByteStr(_) | ConstVal::Bool(_) | ConstVal::Char(_) | + ConstVal::Value(_) | ConstVal::Variant(_) => {} ConstVal::Function(_, substs) => { stack.extend(substs.types().rev()); diff --git a/src/librustc/ty/wf.rs b/src/librustc/ty/wf.rs index ea99bd39e8792..443e0e857a7a1 100644 --- a/src/librustc/ty/wf.rs +++ b/src/librustc/ty/wf.rs @@ -224,6 +224,7 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> { ConstVal::Bool(_) | ConstVal::Char(_) | ConstVal::Variant(_) | + ConstVal::Value(_) | ConstVal::Function(..) => {} ConstVal::Aggregate(ConstAggregate::Struct(fields)) => { for &(_, v) in fields { diff --git a/src/librustc_const_eval/pattern.rs b/src/librustc_const_eval/pattern.rs index bdb1001124de6..a09e2f2edd59d 100644 --- a/src/librustc_const_eval/pattern.rs +++ b/src/librustc_const_eval/pattern.rs @@ -121,6 +121,7 @@ fn print_const_val(value: &ConstVal, f: &mut fmt::Formatter) -> fmt::Result { ConstVal::Variant(_) | ConstVal::Function(..) | ConstVal::Aggregate(_) | + ConstVal::Value(_) | ConstVal::Unevaluated(..) => bug!("{:?} not printable in a pattern", value) } } diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 60a0d4e03b54a..3c3c489d0ff78 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -24,11 +24,12 @@ use rustc::hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc::ich::Fingerprint; use rustc::middle::lang_items; -use rustc::mir; +use rustc::mir::{self, interpret}; use rustc::session::Session; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::codec::TyDecoder; use rustc::mir::Mir; +use rustc::util::nodemap::FxHashMap; use std::cell::Ref; use std::collections::BTreeMap; @@ -54,6 +55,9 @@ pub struct DecodeContext<'a, 'tcx: 'a> { last_filemap_index: usize, lazy_state: LazyState, + + // interpreter allocation cache + interpret_alloc_cache: FxHashMap, } /// Abstract over the various ways one can create metadata decoders. @@ -72,6 +76,7 @@ pub trait Metadata<'a, 'tcx>: Copy { tcx, last_filemap_index: 0, lazy_state: LazyState::NoNode, + interpret_alloc_cache: FxHashMap::default(), } } } @@ -268,6 +273,45 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { } } +impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { + fn specialized_decode(&mut self) -> Result { + const MAX1: usize = usize::max_value() - 1; + let mut interpret_interner = self.tcx.unwrap().interpret_interner.borrow_mut(); + let pos = self.position(); + match self.read_usize()? { + ::std::usize::MAX => { + let allocation = interpret::Allocation::decode(self)?; + let id = interpret_interner.reserve(); + let allocation = self.tcx.unwrap().intern_const_alloc(allocation); + interpret_interner.intern_at_reserved(id, allocation); + let id = interpret::AllocId(id); + self.interpret_alloc_cache.insert(pos, id); + + let num = usize::decode(self)?; + let ptr = interpret::Pointer { + primval: interpret::PrimVal::Ptr(interpret::MemoryPointer { + alloc_id: id, + offset: 0, + }), + }; + for _ in 0..num { + let glob = interpret::GlobalId::decode(self)?; + interpret_interner.cache(glob, ptr); + } + + Ok(id) + }, + MAX1 => { + let instance = ty::Instance::decode(self)?; + let id = interpret::AllocId(interpret_interner.create_fn_alloc(instance)); + self.interpret_alloc_cache.insert(pos, id); + Ok(id) + }, + shorthand => Ok(self.interpret_alloc_cache[&shorthand]), + } + } +} + impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { fn specialized_decode(&mut self) -> Result { let tag = u8::decode(self)?; diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 830121b446fca..928bf0a56ae51 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -23,7 +23,7 @@ use rustc::middle::dependency_format::Linkage; use rustc::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel, metadata_symbol_name}; use rustc::middle::lang_items; -use rustc::mir; +use rustc::mir::{self, interpret}; use rustc::traits::specialization_graph; use rustc::ty::{self, Ty, TyCtxt, ReprOptions, SymbolName}; use rustc::ty::codec::{self as ty_codec, TyEncoder}; @@ -59,6 +59,7 @@ pub struct EncodeContext<'a, 'tcx: 'a> { lazy_state: LazyState, type_shorthands: FxHashMap, usize>, predicate_shorthands: FxHashMap, usize>, + interpret_alloc_shorthands: FxHashMap, // This is used to speed up Span encoding. filemap_cache: Lrc, @@ -186,6 +187,41 @@ impl<'a, 'tcx> SpecializedEncoder> for EncodeContext<'a, 'tcx> { } } +impl<'a, 'tcx> SpecializedEncoder for EncodeContext<'a, 'tcx> { + fn specialized_encode(&mut self, alloc_id: &interpret::AllocId) -> Result<(), Self::Error> { + if let Some(shorthand) = self.interpret_alloc_shorthands.get(alloc_id).cloned() { + return self.emit_usize(shorthand); + } + let start = self.position(); + let interpret_interner = self.tcx.interpret_interner.borrow(); + if let Some(alloc) = interpret_interner.get_alloc(alloc_id.0) { + 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, + }), + }); + globals.len().encode(self)?; + for glob in globals { + glob.encode(self)?; + } + } else if let Some(fn_instance) = interpret_interner.get_fn(alloc_id.0) { + (usize::max_value() - 1).encode(self)?; + fn_instance.encode(self)?; + } else { + bug!("alloc id without corresponding allocation: {}", alloc_id.0); + } + let len = self.position() - start * 7; + // Check that the shorthand is a not longer than the + // full encoding itself, i.e. it's an obvious win. + assert!(len >= 64 || (start as u64) < (1 << len)); + self.interpret_alloc_shorthands.insert(*alloc_id, start); + Ok(()) + } +} + impl<'a, 'tcx> SpecializedEncoder> for EncodeContext<'a, 'tcx> { fn specialized_encode(&mut self, predicates: &ty::GenericPredicates<'tcx>) @@ -1699,6 +1735,7 @@ pub fn encode_metadata<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, type_shorthands: Default::default(), predicate_shorthands: Default::default(), filemap_cache: tcx.sess.codemap().files()[0].clone(), + interpret_alloc_shorthands: Default::default(), }; // Encode the rustc version string in a predictable location. diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 25f933c5da6e7..e9e8ccd03b10f 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -241,24 +241,24 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } pub(super) fn const_to_value(&mut self, const_val: &ConstVal<'tcx>, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { - use rustc::middle::const_val::ConstVal::*; + use rustc::middle::const_val::ConstVal; let primval = match *const_val { - Integral(const_int) => PrimVal::Bytes(const_int.to_u128_unchecked()), + ConstVal::Integral(const_int) => PrimVal::Bytes(const_int.to_u128_unchecked()), - Float(val) => PrimVal::Bytes(val.bits), + ConstVal::Float(val) => PrimVal::Bytes(val.bits), - Bool(b) => PrimVal::from_bool(b), - Char(c) => PrimVal::from_char(c), + ConstVal::Bool(b) => PrimVal::from_bool(b), + ConstVal::Char(c) => PrimVal::from_char(c), - Str(ref s) => return self.str_to_value(s), + ConstVal::Str(ref s) => return self.str_to_value(s), - ByteStr(ref bs) => { + ConstVal::ByteStr(ref bs) => { let ptr = self.memory.allocate_cached(bs.data); PrimVal::Ptr(ptr) } - Unevaluated(def_id, substs) => { + ConstVal::Unevaluated(def_id, substs) => { let instance = self.resolve(def_id, substs)?; return Ok(self.read_global_as_value(GlobalId { instance, @@ -266,10 +266,11 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { }, self.layout_of(ty)?)); } - Aggregate(..) | - Variant(_) => bug!("should not have aggregate or variant constants in MIR"), + 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 - Function(..) => PrimVal::Undef, + ConstVal::Function(..) => PrimVal::Undef, + ConstVal::Value(val) => return Ok(val), }; Ok(Value::ByVal(primval)) diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index d470f92b75231..c853230b15ab7 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -108,6 +108,7 @@ impl<'a, 'tcx> Const<'tcx> { ConstVal::Unevaluated(..) => { bug!("MIR must not use `{:?}` (aggregates are expanded to MIR rvalues)", cv) } + ConstVal::Value(_) => unimplemented!(), }; assert!(!ty.has_erasable_regions()); From c0574c054c1979a4d77822d4fe36ba7571760b00 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 16 Jan 2018 09:13:50 +0100 Subject: [PATCH 002/110] Add miri errors to the const eval error enum --- src/librustc/ich/impls_ty.rs | 170 ++++++++++++++++++++++++++++ src/librustc/middle/const_val.rs | 8 ++ src/librustc/mir/interpret/error.rs | 16 +-- src/librustc/ty/structural_impls.rs | 112 ++++++++++++++++++ 4 files changed, 294 insertions(+), 12 deletions(-) diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 06a60bfb0eba6..4d802491c96ed 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -470,6 +470,7 @@ for ::middle::const_val::ErrKind<'gcx> { ErroneousReferencedConstant(ref const_val) => { const_val.hash_stable(hcx, hasher); } + Miri(ref err) => err.hash_stable(hcx, hasher), } } } @@ -483,6 +484,175 @@ impl_stable_hash_for!(struct ty::GenericPredicates<'tcx> { predicates }); +impl<'gcx> HashStable> +for ::mir::interpret::EvalError<'gcx> { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + use mir::interpret::EvalErrorKind::*; + + mem::discriminant(&self.kind).hash_stable(hcx, hasher); + + match self.kind { + DanglingPointerDeref | + DoubleFree | + InvalidMemoryAccess | + InvalidFunctionPointer | + InvalidBool | + InvalidDiscriminant | + InvalidNullPointerUsage | + ReadPointerAsBytes | + ReadBytesAsPointer | + InvalidPointerMath | + ReadUndefBytes | + DeadLocal | + ExecutionTimeLimitReached | + StackFrameLimitReached | + OutOfTls | + TlsOutOfBounds | + CalledClosureAsFunction | + VtableForArgumentlessMethod | + ModifiedConstantMemory | + AssumptionNotHeld | + InlineAsm | + ReallocateNonBasePtr | + DeallocateNonBasePtr | + HeapAllocZeroBytes | + Unreachable | + Panic | + ReadFromReturnPointer | + UnimplementedTraitSelection | + TypeckError | + DerefFunctionPointer | + ExecuteMemory | + OverflowingMath => {} + MachineError(ref err) => err.hash_stable(hcx, hasher), + FunctionPointerTyMismatch(a, b) => { + a.hash_stable(hcx, hasher); + b.hash_stable(hcx, hasher) + }, + NoMirFor(ref s) => s.hash_stable(hcx, hasher), + UnterminatedCString(ptr) => ptr.hash_stable(hcx, hasher), + PointerOutOfBounds { + ptr, + access, + allocation_size, + } => { + ptr.hash_stable(hcx, hasher); + access.hash_stable(hcx, hasher); + allocation_size.hash_stable(hcx, hasher) + }, + InvalidBoolOp(bop) => bop.hash_stable(hcx, hasher), + Unimplemented(ref s) => s.hash_stable(hcx, hasher), + ArrayIndexOutOfBounds(sp, a, b) => { + sp.hash_stable(hcx, hasher); + a.hash_stable(hcx, hasher); + b.hash_stable(hcx, hasher) + }, + Math(sp, ref err) => { + sp.hash_stable(hcx, hasher); + err.hash_stable(hcx, hasher) + }, + Intrinsic(ref s) => s.hash_stable(hcx, hasher), + InvalidChar(c) => c.hash_stable(hcx, hasher), + OutOfMemory { + allocation_size, + memory_size, + memory_usage, + } => { + allocation_size.hash_stable(hcx, hasher); + memory_size.hash_stable(hcx, hasher); + memory_usage.hash_stable(hcx, hasher) + }, + AbiViolation(ref s) => s.hash_stable(hcx, hasher), + AlignmentCheckFailed { + required, + has, + } => { + required.hash_stable(hcx, hasher); + has.hash_stable(hcx, hasher) + }, + MemoryLockViolation { + ptr, + len, + frame, + access, + ref lock, + } => { + ptr.hash_stable(hcx, hasher); + len.hash_stable(hcx, hasher); + frame.hash_stable(hcx, hasher); + access.hash_stable(hcx, hasher); + lock.hash_stable(hcx, hasher) + }, + MemoryAcquireConflict { + ptr, + len, + kind, + ref lock, + } => { + ptr.hash_stable(hcx, hasher); + len.hash_stable(hcx, hasher); + kind.hash_stable(hcx, hasher); + lock.hash_stable(hcx, hasher) + }, + InvalidMemoryLockRelease { + ptr, + len, + frame, + ref lock, + } => { + ptr.hash_stable(hcx, hasher); + len.hash_stable(hcx, hasher); + frame.hash_stable(hcx, hasher); + lock.hash_stable(hcx, hasher) + }, + DeallocatedLockedMemory { + ptr, + ref lock, + } => { + ptr.hash_stable(hcx, hasher); + lock.hash_stable(hcx, hasher) + }, + ValidationFailure(ref s) => s.hash_stable(hcx, hasher), + TypeNotPrimitive(ty) => ty.hash_stable(hcx, hasher), + ReallocatedWrongMemoryKind(ref a, ref b) => { + a.hash_stable(hcx, hasher); + b.hash_stable(hcx, hasher) + }, + DeallocatedWrongMemoryKind(ref a, ref b) => { + a.hash_stable(hcx, hasher); + b.hash_stable(hcx, hasher) + }, + IncorrectAllocationInformation(a, b, c, d) => { + a.hash_stable(hcx, hasher); + b.hash_stable(hcx, hasher); + c.hash_stable(hcx, hasher); + d.hash_stable(hcx, hasher) + }, + Layout(lay) => lay.hash_stable(hcx, hasher), + HeapAllocNonPowerOfTwoAlignment(n) => n.hash_stable(hcx, hasher), + PathNotFound(ref v) => v.hash_stable(hcx, hasher), + } + } +} + +impl_stable_hash_for!(enum mir::interpret::Lock { + NoLock, + WriteLock(dl), + ReadLock(v) +}); + +impl_stable_hash_for!(struct mir::interpret::DynamicLifetime { + frame, + region +}); + +impl_stable_hash_for!(enum mir::interpret::AccessKind { + Read, + Write +}); + impl_stable_hash_for!(enum ty::Variance { Covariant, Invariant, diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index 2309131c57a9f..589890947cd5a 100644 --- a/src/librustc/middle/const_val.rs +++ b/src/librustc/middle/const_val.rs @@ -111,6 +111,13 @@ pub enum ErrKind<'tcx> { TypeckError, CheckMatchError, + Miri(::mir::interpret::EvalError<'tcx>), +} + +impl<'tcx> From<::mir::interpret::EvalError<'tcx>> for ErrKind<'tcx> { + fn from(err: ::mir::interpret::EvalError<'tcx>) -> ErrKind<'tcx> { + ErrKind::Miri(err) + } } impl<'tcx> From for ErrKind<'tcx> { @@ -173,6 +180,7 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { TypeckError => simple!("type-checking failed"), CheckMatchError => simple!("match-checking failed"), + Miri(ref err) => simple!("miri failed: {}", err), } } diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs index f9ea544156ce3..6386de5952f99 100644 --- a/src/librustc/mir/interpret/error.rs +++ b/src/librustc/mir/interpret/error.rs @@ -12,7 +12,7 @@ use rustc_const_math::ConstMathErr; use syntax::codemap::Span; use backtrace::Backtrace; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct EvalError<'tcx> { pub kind: EvalErrorKind<'tcx>, pub backtrace: Option, @@ -31,11 +31,11 @@ impl<'tcx> From> for EvalError<'tcx> { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum EvalErrorKind<'tcx> { /// This variant is used by machines to signal their own errors that do not /// match an existing variant - MachineError(Box), + MachineError(String), FunctionPointerTyMismatch(FnSig<'tcx>, FnSig<'tcx>), NoMirFor(String), UnterminatedCString(MemoryPointer), @@ -132,7 +132,7 @@ impl<'tcx> Error for EvalError<'tcx> { fn description(&self) -> &str { use self::EvalErrorKind::*; match self.kind { - MachineError(ref inner) => inner.description(), + MachineError(ref inner) => inner, FunctionPointerTyMismatch(..) => "tried to call a function through a function pointer of a different type", InvalidMemoryAccess => @@ -247,14 +247,6 @@ impl<'tcx> Error for EvalError<'tcx> { "encountered constants with type errors, stopping evaluation", } } - - fn cause(&self) -> Option<&dyn Error> { - use self::EvalErrorKind::*; - match self.kind { - MachineError(ref inner) => Some(&**inner), - _ => None, - } - } } impl<'tcx> fmt::Display for EvalError<'tcx> { diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 3e7468d2ccab4..54db2d4f06be5 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -18,6 +18,7 @@ use ty::{self, Lift, Ty, TyCtxt}; use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use rustc_data_structures::accumulate_vec::AccumulateVec; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; +use mir::interpret; use std::rc::Rc; @@ -585,6 +586,116 @@ impl<'a, 'tcx> Lift<'tcx> for ConstEvalErr<'a> { } } +impl<'a, 'tcx> Lift<'tcx> for interpret::EvalError<'a> { + type Lifted = interpret::EvalError<'tcx>; + fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option { + use mir::interpret::EvalErrorKind::*; + let kind = match self.kind { + MachineError(ref err) => MachineError(err.clone()), + FunctionPointerTyMismatch(a, b) => FunctionPointerTyMismatch( + tcx.lift(&a)?, + tcx.lift(&b)?, + ), + NoMirFor(ref s) => NoMirFor(s.clone()), + UnterminatedCString(ptr) => UnterminatedCString(ptr), + DanglingPointerDeref => DanglingPointerDeref, + DoubleFree => DoubleFree, + InvalidMemoryAccess => InvalidMemoryAccess, + InvalidFunctionPointer => InvalidFunctionPointer, + InvalidBool => InvalidBool, + InvalidDiscriminant => InvalidDiscriminant, + PointerOutOfBounds { + ptr, + access, + allocation_size, + } => PointerOutOfBounds { ptr, access, allocation_size }, + InvalidNullPointerUsage => InvalidNullPointerUsage, + ReadPointerAsBytes => ReadPointerAsBytes, + ReadBytesAsPointer => ReadBytesAsPointer, + InvalidPointerMath => InvalidPointerMath, + ReadUndefBytes => ReadUndefBytes, + DeadLocal => DeadLocal, + InvalidBoolOp(bop) => InvalidBoolOp(bop), + Unimplemented(ref s) => Unimplemented(s.clone()), + DerefFunctionPointer => DerefFunctionPointer, + ExecuteMemory => ExecuteMemory, + ArrayIndexOutOfBounds(sp, a, b) => ArrayIndexOutOfBounds(sp, a, b), + Math(sp, ref err) => Math(sp, err.clone()), + Intrinsic(ref s) => Intrinsic(s.clone()), + OverflowingMath => OverflowingMath, + InvalidChar(c) => InvalidChar(c), + OutOfMemory { + allocation_size, + memory_size, + memory_usage, + } => OutOfMemory { allocation_size, memory_size, memory_usage }, + ExecutionTimeLimitReached => ExecutionTimeLimitReached, + StackFrameLimitReached => StackFrameLimitReached, + OutOfTls => OutOfTls, + TlsOutOfBounds => TlsOutOfBounds, + AbiViolation(ref s) => AbiViolation(s.clone()), + AlignmentCheckFailed { + required, + has, + } => AlignmentCheckFailed { required, has }, + MemoryLockViolation { + ptr, + len, + frame, + access, + ref lock, + } => MemoryLockViolation { ptr, len, frame, access, lock: lock.clone() }, + MemoryAcquireConflict { + ptr, + len, + kind, + ref lock, + } => MemoryAcquireConflict { ptr, len, kind, lock: lock.clone() }, + InvalidMemoryLockRelease { + ptr, + len, + frame, + ref lock, + } => InvalidMemoryLockRelease { ptr, len, frame, lock: lock.clone() }, + DeallocatedLockedMemory { + ptr, + ref lock, + } => DeallocatedLockedMemory { ptr, lock: lock.clone() }, + ValidationFailure(ref s) => ValidationFailure(s.clone()), + CalledClosureAsFunction => CalledClosureAsFunction, + VtableForArgumentlessMethod => VtableForArgumentlessMethod, + ModifiedConstantMemory => ModifiedConstantMemory, + AssumptionNotHeld => AssumptionNotHeld, + InlineAsm => InlineAsm, + TypeNotPrimitive(ty) => TypeNotPrimitive(tcx.lift(&ty)?), + ReallocatedWrongMemoryKind(ref a, ref b) => { + ReallocatedWrongMemoryKind(a.clone(), b.clone()) + }, + DeallocatedWrongMemoryKind(ref a, ref b) => { + DeallocatedWrongMemoryKind(a.clone(), b.clone()) + }, + ReallocateNonBasePtr => ReallocateNonBasePtr, + DeallocateNonBasePtr => DeallocateNonBasePtr, + IncorrectAllocationInformation(a, b, c, d) => { + IncorrectAllocationInformation(a, b, c, d) + }, + Layout(lay) => Layout(tcx.lift(&lay)?), + HeapAllocZeroBytes => HeapAllocZeroBytes, + HeapAllocNonPowerOfTwoAlignment(n) => HeapAllocNonPowerOfTwoAlignment(n), + Unreachable => Unreachable, + Panic => Panic, + ReadFromReturnPointer => ReadFromReturnPointer, + PathNotFound(ref v) => PathNotFound(v.clone()), + UnimplementedTraitSelection => UnimplementedTraitSelection, + TypeckError => TypeckError, + }; + Some(interpret::EvalError { + kind, + backtrace: self.backtrace.clone(), + }) + } +} + impl<'a, 'tcx> Lift<'tcx> for const_val::ErrKind<'a> { type Lifted = const_val::ErrKind<'tcx>; fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option { @@ -614,6 +725,7 @@ impl<'a, 'tcx> Lift<'tcx> for const_val::ErrKind<'a> { TypeckError => TypeckError, CheckMatchError => CheckMatchError, + Miri(ref e) => return tcx.lift(e).map(Miri), }) } } From 918b6d763319863fb53c5b7bceebc14ad5fb4024 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 16 Jan 2018 09:24:38 +0100 Subject: [PATCH 003/110] Produce instead of pointers --- src/libcore/cmp.rs | 4 +- src/librustc/dep_graph/dep_node.rs | 1 + src/librustc/middle/const_val.rs | 39 +- src/librustc/middle/lang_items.rs | 1 + src/librustc/middle/mem_categorization.rs | 3 +- src/librustc/mir/interpret/value.rs | 10 + src/librustc/mir/mod.rs | 80 ++++- src/librustc/mir/tcx.rs | 2 +- src/librustc/mir/visit.rs | 14 +- src/librustc/ty/context.rs | 39 +- src/librustc/ty/error.rs | 11 +- src/librustc/ty/inhabitedness/mod.rs | 2 +- src/librustc/ty/layout.rs | 4 +- src/librustc/ty/maps/config.rs | 1 + src/librustc/ty/maps/keys.rs | 1 + src/librustc/ty/maps/on_disk_cache.rs | 9 +- src/librustc/ty/mod.rs | 28 ++ src/librustc/ty/relate.rs | 9 + src/librustc/ty/structural_impls.rs | 55 +++ src/librustc/ty/util.rs | 2 + src/librustc/util/ppaux.rs | 4 + src/librustc_const_eval/_match.rs | 180 ++++++++-- src/librustc_const_eval/eval.rs | 180 +++++----- src/librustc_const_eval/pattern.rs | 335 +++++++++++------- src/librustc_data_structures/stable_hasher.rs | 8 + src/librustc_lint/types.rs | 11 + src/librustc_metadata/decoder.rs | 38 +- src/librustc_metadata/encoder.rs | 14 +- .../borrow_check/nll/type_check/mod.rs | 47 ++- src/librustc_mir/build/expr/as_rvalue.rs | 21 +- src/librustc_mir/build/matches/mod.rs | 2 +- src/librustc_mir/build/matches/test.rs | 69 +++- src/librustc_mir/build/misc.rs | 19 +- src/librustc_mir/hair/cx/expr.rs | 46 ++- src/librustc_mir/hair/cx/mod.rs | 178 +++++++++- src/librustc_mir/interpret/const_eval.rs | 153 ++++++-- src/librustc_mir/interpret/eval_context.rs | 4 +- src/librustc_mir/interpret/mod.rs | 2 +- src/librustc_mir/interpret/operator.rs | 5 + src/librustc_mir/interpret/place.rs | 43 ++- src/librustc_mir/interpret/terminator/mod.rs | 4 +- src/librustc_mir/monomorphize/item.rs | 2 +- src/librustc_mir/shim.rs | 29 +- src/librustc_mir/transform/generator.rs | 9 +- src/librustc_mir/transform/inline.rs | 7 + src/librustc_mir/transform/qualify_consts.rs | 2 +- .../transform/simplify_branches.rs | 4 +- src/librustc_mir/util/elaborate_drops.rs | 15 +- src/librustc_passes/consts.rs | 23 +- src/librustc_trans/base.rs | 2 +- src/librustc_trans/debuginfo/metadata.rs | 2 +- src/librustc_trans/debuginfo/type_names.rs | 2 +- src/librustc_trans/mir/analyze.rs | 19 +- src/librustc_trans/mir/block.rs | 9 +- src/librustc_trans/mir/constant.rs | 110 ++++-- src/librustc_trans/mir/rvalue.rs | 2 +- src/librustc_typeck/check/_match.rs | 2 +- src/librustc_typeck/check/mod.rs | 4 +- src/librustc_typeck/check/op.rs | 8 +- src/librustc_typeck/collect.rs | 16 +- src/librustc_typeck/lib.rs | 1 + src/test/mir-opt/end_region_2.rs | 2 +- src/test/mir-opt/end_region_3.rs | 2 +- src/test/mir-opt/end_region_9.rs | 2 +- src/test/mir-opt/end_region_cyclic.rs | 2 +- src/test/mir-opt/issue-38669.rs | 2 +- src/test/mir-opt/match_false_edges.rs | 10 +- src/test/mir-opt/nll/region-liveness-basic.rs | 2 +- src/test/mir-opt/simplify_if.rs | 2 +- src/test/ui/const-eval-overflow-2.rs | 3 +- src/test/ui/const-eval-overflow-4.rs | 3 +- src/test/ui/const-eval/issue-43197.rs | 2 - src/test/ui/const-expr-addr-operator.rs | 3 +- src/test/ui/const-fn-error.rs | 13 +- src/test/ui/const-fn-error.stderr | 14 + .../ui/const-len-underflow-separate-spans.rs | 3 +- .../ui/const-pattern-not-const-evaluable.rs | 4 +- src/test/ui/feature-gate-const-indexing.rs | 3 +- src/test/ui/issue-38875/issue_38875.rs | 1 + src/test/ui/union/union-const-eval.rs | 9 +- 80 files changed, 1496 insertions(+), 532 deletions(-) diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs index b98470449827e..6602643dc5106 100644 --- a/src/libcore/cmp.rs +++ b/src/libcore/cmp.rs @@ -427,6 +427,7 @@ impl Ord for Reverse { /// } /// } /// ``` +#[cfg_attr(not(stage0), lang = "ord")] #[stable(feature = "rust1", since = "1.0.0")] pub trait Ord: Eq + PartialOrd { /// This method returns an `Ordering` between `self` and `other`. @@ -596,7 +597,8 @@ impl PartialOrd for Ordering { /// assert_eq!(x < y, true); /// assert_eq!(x.lt(&y), true); /// ``` -#[lang = "ord"] +#[cfg_attr(stage0, lang = "ord")] +#[cfg_attr(not(stage0), lang = "partial_ord")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented = "can't compare `{Self}` with `{Rhs}`"] pub trait PartialOrd: PartialEq { diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index ed46296389da4..15e1d38be6950 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -63,6 +63,7 @@ use hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX}; use hir::map::DefPathHash; use hir::{HirId, ItemLocalId}; +use mir; use ich::Fingerprint; use ty::{TyCtxt, Instance, InstanceDef, ParamEnv, ParamEnvAnd, PolyTraitRef, Ty}; diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index 589890947cd5a..cf322010e05c3 100644 --- a/src/librustc/middle/const_val.rs +++ b/src/librustc/middle/const_val.rs @@ -14,7 +14,7 @@ use hir::def_id::DefId; use ty::{self, TyCtxt, layout}; use ty::subst::Substs; use rustc_const_math::*; -use mir::interpret::Value; +use mir::interpret::{Value, PrimVal}; use graphviz::IntoCow; use errors::DiagnosticBuilder; @@ -39,7 +39,7 @@ pub enum ConstVal<'tcx> { Function(DefId, &'tcx Substs<'tcx>), Aggregate(ConstAggregate<'tcx>), Unevaluated(DefId, &'tcx Substs<'tcx>), - /// A miri value, currently only produced if old ctfe fails, but miri succeeds + /// A miri value, currently only produced if --miri is enabled Value(Value), } @@ -71,12 +71,37 @@ impl<'tcx> Decodable for ConstAggregate<'tcx> { } impl<'tcx> ConstVal<'tcx> { - pub fn to_const_int(&self) -> Option { + pub fn to_u128(&self) -> Option { match *self { - ConstVal::Integral(i) => Some(i), - ConstVal::Bool(b) => Some(ConstInt::U8(b as u8)), - ConstVal::Char(ch) => Some(ConstInt::U32(ch as u32)), - _ => None + 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) + }, + _ => None, + } + } + pub fn unwrap_u64(&self) -> u64 { + match self.to_u128() { + Some(val) => { + assert_eq!(val as u64 as u128, val); + val as u64 + }, + None => bug!("expected constant u64, got {:#?}", self), + } + } + 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) { + Ok(val) => val, + Err(e) => bug!("{:#?} is not a usize {:?}", self, e), + } + }, + _ => bug!("expected constant u64, got {:#?}", self), } } } diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 447ce46ee5c5c..3b37031cf4614 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -280,6 +280,7 @@ language_item_table! { GeneratorTraitLangItem, "generator", gen_trait; EqTraitLangItem, "eq", eq_trait; + PartialOrdTraitLangItem, "partial_ord", partial_ord_trait; OrdTraitLangItem, "ord", ord_trait; // A number of panic-related lang items. The `panic` item corresponds to diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index c532427cc9b42..1c60ae36cd334 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -913,8 +913,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { // Always promote `[T; 0]` (even when e.g. borrowed mutably). let promotable = match expr_ty.sty { - ty::TyArray(_, len) if - len.val.to_const_int().and_then(|i| i.to_u64()) == Some(0) => true, + ty::TyArray(_, len) if len.val.to_u128() == Some(0) => true, _ => promotable, }; diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc/mir/interpret/value.rs index 8d67856c0df8d..c00956c0a8570 100644 --- a/src/librustc/mir/interpret/value.rs +++ b/src/librustc/mir/interpret/value.rs @@ -1,6 +1,7 @@ #![allow(unknown_lints)] use ty::layout::{Align, HasDataLayout}; +use ty; use super::{EvalResult, MemoryPointer, PointerArithmetic}; use syntax::ast::FloatTy; @@ -36,6 +37,15 @@ pub enum Value { ByValPair(PrimVal, PrimVal), } +impl<'tcx> ty::TypeFoldable<'tcx> for Value { + fn super_fold_with<'gcx: 'tcx, F: ty::fold::TypeFolder<'gcx, 'tcx>>(&self, _: &mut F) -> Self { + *self + } + fn super_visit_with>(&self, _: &mut V) -> bool { + false + } +} + /// A wrapper type around `PrimVal` that cannot be turned back into a `PrimVal` accidentally. /// This type clears up a few APIs where having a `PrimVal` argument for something that is /// potentially an integer pointer or a pointer to an allocation was unclear. diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 4c1b8cb79ed1a..d35cbd0027fb6 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -15,7 +15,7 @@ use graphviz::IntoCow; use middle::const_val::ConstVal; use middle::region; -use rustc_const_math::{ConstUsize, ConstInt, ConstMathErr}; +use rustc_const_math::{ConstUsize, ConstMathErr}; use rustc_data_structures::sync::{Lrc}; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; use rustc_data_structures::control_flow_graph::dominators::{Dominators, dominators}; @@ -25,9 +25,11 @@ use rustc_serialize as serialize; use hir::def::CtorKind; use hir::def_id::DefId; use mir::visit::MirVisitable; +use mir::interpret::{Value, PrimVal}; use ty::subst::{Subst, Substs}; use ty::{self, AdtDef, ClosureSubsts, Region, Ty, TyCtxt, GeneratorInterior}; use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; +use ty::TypeAndMut; use util::ppaux; use std::slice; use hir::{self, InlineAsm}; @@ -707,7 +709,7 @@ pub enum TerminatorKind<'tcx> { /// Possible values. The locations to branch to in each case /// are found in the corresponding indices from the `targets` vector. - values: Cow<'tcx, [ConstInt]>, + values: Cow<'tcx, [u128]>, /// Possible branch sites. The last element of this vector is used /// for the otherwise branch, so targets.len() == values.len() + 1 @@ -858,7 +860,7 @@ impl<'tcx> Terminator<'tcx> { impl<'tcx> TerminatorKind<'tcx> { pub fn if_<'a, 'gcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, cond: Operand<'tcx>, t: BasicBlock, f: BasicBlock) -> TerminatorKind<'tcx> { - static BOOL_SWITCH_FALSE: &'static [ConstInt] = &[ConstInt::U8(0)]; + static BOOL_SWITCH_FALSE: &'static [u128] = &[0]; TerminatorKind::SwitchInt { discr: cond, switch_ty: tcx.types.bool, @@ -1144,12 +1146,16 @@ impl<'tcx> TerminatorKind<'tcx> { match *self { Return | Resume | Abort | Unreachable | GeneratorDrop => vec![], Goto { .. } => vec!["".into()], - SwitchInt { ref values, .. } => { + SwitchInt { ref values, switch_ty, .. } => { values.iter() - .map(|const_val| { - let mut buf = String::new(); - fmt_const_val(&mut buf, &ConstVal::Integral(*const_val)).unwrap(); - buf.into() + .map(|&u| { + let mut s = String::new(); + print_miri_value( + Value::ByVal(PrimVal::Bytes(u)), + switch_ty, + &mut s, + ).unwrap(); + s.into() }) .chain(iter::once(String::from("otherwise").into())) .collect() @@ -1533,7 +1539,12 @@ impl<'tcx> Operand<'tcx> { ty, literal: Literal::Value { value: tcx.mk_const(ty::Const { - val: ConstVal::Function(def_id, substs), + val: if tcx.sess.opts.debugging_opts.miri { + // ZST function type + ConstVal::Value(Value::ByVal(PrimVal::Undef)) + } else { + ConstVal::Function(def_id, substs) + }, ty }) }, @@ -1853,7 +1864,7 @@ impl<'tcx> Debug for Literal<'tcx> { match *self { Value { value } => { write!(fmt, "const ")?; - fmt_const_val(fmt, &value.val) + fmt_const_val(fmt, value) } Promoted { index } => { write!(fmt, "{:?}", index) @@ -1863,9 +1874,9 @@ impl<'tcx> Debug for Literal<'tcx> { } /// Write a `ConstVal` in a way closer to the original source code than the `Debug` output. -fn fmt_const_val(fmt: &mut W, const_val: &ConstVal) -> fmt::Result { +fn fmt_const_val(fmt: &mut W, const_val: &ty::Const) -> fmt::Result { use middle::const_val::ConstVal::*; - match *const_val { + match const_val.val { Float(f) => write!(fmt, "{:?}", f), Integral(n) => write!(fmt, "{}", n), Str(s) => write!(fmt, "{:?}", s), @@ -1882,7 +1893,41 @@ fn fmt_const_val(fmt: &mut W, const_val: &ConstVal) -> fmt::Result { 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) => write!(fmt, "{:?}", val), + Value(val) => print_miri_value(val, const_val.ty, fmt), + } +} + +fn print_miri_value(value: Value, ty: Ty, f: &mut W) -> fmt::Result { + use ty::TypeVariants::*; + use rustc_const_math::ConstFloat; + match (value, &ty.sty) { + (Value::ByVal(PrimVal::Bytes(0)), &TyBool) => write!(f, "false"), + (Value::ByVal(PrimVal::Bytes(1)), &TyBool) => write!(f, "true"), + (Value::ByVal(PrimVal::Bytes(bits)), &TyFloat(fty)) => + write!(f, "{}", ConstFloat { bits, ty: fty }), + (Value::ByVal(PrimVal::Bytes(n)), &TyUint(ui)) => write!(f, "{:?}{}", n, ui), + (Value::ByVal(PrimVal::Bytes(n)), &TyInt(i)) => write!(f, "{:?}{}", n as i128, i), + (Value::ByVal(PrimVal::Bytes(n)), &TyChar) => + write!(f, "{:?}", ::std::char::from_u32(n as u32).unwrap()), + (Value::ByVal(PrimVal::Undef), &TyFnDef(did, _)) => + write!(f, "{}", item_path_str(did)), + (Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(len)), &TyRef(_, TypeAndMut { + ty: &ty::TyS { sty: TyStr, .. }, .. + })) => { + ty::tls::with(|tcx| { + let alloc = tcx + .interpret_interner + .borrow() + .get_alloc(ptr.alloc_id.0) + .expect("miri alloc not found"); + assert_eq!(len as usize as u128, len); + let slice = &alloc.bytes[(ptr.offset as usize)..][..(len as usize)]; + let s = ::std::str::from_utf8(slice) + .expect("non utf8 str from miri"); + write!(f, "{:?}", s) + }) + }, + _ => write!(f, "{:?}:{}", value, ty), } } @@ -2468,6 +2513,15 @@ impl<'tcx, B, V, T> TypeFoldable<'tcx> for Projection<'tcx, B, V, T> } } +impl<'tcx> TypeFoldable<'tcx> for Field { + fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, _: &mut F) -> Self { + *self + } + fn super_visit_with>(&self, _: &mut V) -> bool { + false + } +} + impl<'tcx> TypeFoldable<'tcx> for Constant<'tcx> { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { Constant { diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index bbfb9c89b3fc4..067c1742040bf 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -70,7 +70,7 @@ impl<'a, 'gcx, 'tcx> PlaceTy<'tcx> { PlaceTy::Ty { ty: match ty.sty { ty::TyArray(inner, size) => { - let size = size.val.to_const_int().unwrap().to_u64().unwrap(); + let size = size.val.unwrap_u64(); let len = size - (from as u64) - (to as u64); tcx.mk_array(inner, len) } diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 0b6f1275bdb4c..54d3ed38d6537 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -243,12 +243,6 @@ macro_rules! make_mir_visitor { self.super_generator_interior(interior); } - fn visit_const_int(&mut self, - const_int: &ConstInt, - _: Location) { - self.super_const_int(const_int); - } - fn visit_const_usize(&mut self, const_usize: & $($mutability)* ConstUsize, _: Location) { @@ -426,13 +420,10 @@ macro_rules! make_mir_visitor { TerminatorKind::SwitchInt { ref $($mutability)* discr, ref $($mutability)* switch_ty, - ref values, + values: _, ref targets } => { self.visit_operand(discr, source_location); self.visit_ty(switch_ty, TyContext::Location(source_location)); - for value in &values[..] { - self.visit_const_int(value, source_location); - } for &target in targets { self.visit_branch(block, target); } @@ -798,9 +789,6 @@ macro_rules! make_mir_visitor { _substs: & $($mutability)* ClosureSubsts<'tcx>) { } - fn super_const_int(&mut self, _const_int: &ConstInt) { - } - fn super_const_usize(&mut self, _const_usize: & $($mutability)* ConstUsize) { } diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 520da34c40acb..17926eeec0020 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -30,7 +30,8 @@ use middle::cstore::EncodedMetadata; use middle::lang_items; use middle::resolve_lifetime::{self, ObjectLifetimeDefault}; use middle::stability; -use mir::{Mir, interpret}; +use mir::{self, Mir, interpret}; +use mir::interpret::{Value, PrimVal}; use ty::subst::{Kind, Substs}; use ty::ReprOptions; use ty::Instance; @@ -1267,6 +1268,36 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self.get_lang_items(LOCAL_CRATE) } + pub fn is_binop_lang_item(&self, def_id: DefId) -> Option<(mir::BinOp, bool)> { + let items = self.lang_items(); + let def_id = Some(def_id); + if items.i128_add_fn() == def_id { Some((mir::BinOp::Add, false)) } + else if items.u128_add_fn() == def_id { Some((mir::BinOp::Add, false)) } + else if items.i128_sub_fn() == def_id { Some((mir::BinOp::Sub, false)) } + else if items.u128_sub_fn() == def_id { Some((mir::BinOp::Sub, false)) } + else if items.i128_mul_fn() == def_id { Some((mir::BinOp::Mul, false)) } + else if items.u128_mul_fn() == def_id { Some((mir::BinOp::Mul, false)) } + else if items.i128_div_fn() == def_id { Some((mir::BinOp::Div, false)) } + else if items.u128_div_fn() == def_id { Some((mir::BinOp::Div, false)) } + else if items.i128_rem_fn() == def_id { Some((mir::BinOp::Rem, false)) } + else if items.u128_rem_fn() == def_id { Some((mir::BinOp::Rem, false)) } + else if items.i128_shl_fn() == def_id { Some((mir::BinOp::Shl, false)) } + else if items.u128_shl_fn() == def_id { Some((mir::BinOp::Shl, false)) } + else if items.i128_shr_fn() == def_id { Some((mir::BinOp::Shr, false)) } + else if items.u128_shr_fn() == def_id { Some((mir::BinOp::Shr, false)) } + else if items.i128_addo_fn() == def_id { Some((mir::BinOp::Add, true)) } + else if items.u128_addo_fn() == def_id { Some((mir::BinOp::Add, true)) } + else if items.i128_subo_fn() == def_id { Some((mir::BinOp::Sub, true)) } + else if items.u128_subo_fn() == def_id { Some((mir::BinOp::Sub, true)) } + else if items.i128_mulo_fn() == def_id { Some((mir::BinOp::Mul, true)) } + else if items.u128_mulo_fn() == def_id { Some((mir::BinOp::Mul, true)) } + else if items.i128_shlo_fn() == def_id { Some((mir::BinOp::Shl, true)) } + else if items.u128_shlo_fn() == def_id { Some((mir::BinOp::Shl, true)) } + else if items.i128_shro_fn() == def_id { Some((mir::BinOp::Shr, true)) } + else if items.u128_shro_fn() == def_id { Some((mir::BinOp::Shr, true)) } + else { None } + } + pub fn stability(self) -> Lrc> { self.stability_index(LOCAL_CRATE) } @@ -2068,7 +2099,11 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { pub fn mk_array_const_usize(self, ty: Ty<'tcx>, n: ConstUsize) -> Ty<'tcx> { self.mk_ty(TyArray(ty, self.mk_const(ty::Const { - val: ConstVal::Integral(ConstInt::Usize(n)), + val: if self.sess.opts.debugging_opts.miri { + ConstVal::Value(Value::ByVal(PrimVal::Bytes(n.as_u64().into()))) + } else { + ConstVal::Integral(ConstInt::Usize(n)) + }, ty: self.types.usize }))) } diff --git a/src/librustc/ty/error.rs b/src/librustc/ty/error.rs index be89aeebdea75..07920c58271cd 100644 --- a/src/librustc/ty/error.rs +++ b/src/librustc/ty/error.rs @@ -11,6 +11,7 @@ use hir::def_id::DefId; use middle::const_val::ConstVal; use ty::{self, BoundRegion, Region, Ty, TyCtxt}; +use mir::interpret::{Value, PrimVal}; use std::fmt; use syntax::abi; @@ -186,10 +187,12 @@ impl<'a, 'gcx, 'lcx, 'tcx> ty::TyS<'tcx> { ty::TyAdt(def, _) => format!("{} `{}`", def.descr(), tcx.item_path_str(def.did)), ty::TyForeign(def_id) => format!("extern type `{}`", tcx.item_path_str(def_id)), ty::TyArray(_, n) => { - if let ConstVal::Integral(ConstInt::Usize(n)) = n.val { - format!("array of {} elements", n) - } else { - "array".to_string() + 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(), } } ty::TySlice(_) => "slice".to_string(), diff --git a/src/librustc/ty/inhabitedness/mod.rs b/src/librustc/ty/inhabitedness/mod.rs index 93e4cd9adf888..bfaa661b2432d 100644 --- a/src/librustc/ty/inhabitedness/mod.rs +++ b/src/librustc/ty/inhabitedness/mod.rs @@ -262,7 +262,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { })) }, TyArray(ty, len) => { - match len.val.to_const_int().and_then(|i| i.to_u64()) { + match len.val.to_u128() { // If the array is definitely non-empty, it's uninhabited if // the type of its elements is uninhabited. Some(n) if n != 0 => ty.uninhabited_from(visited, tcx), diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 1aa7f671ad391..35d8fb2a67ac9 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -952,7 +952,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { enum StructKind { /// A tuple, closure, or univariant which cannot be coerced to unsized. AlwaysSized, - /// A univariant, the last field of which may be coerced to unsized. + /// A univariant, the last field of which fn compute_uncachedmay be coerced to unsized. MaybeUnsized, /// A univariant, but with a prefix of an arbitrary size & alignment (e.g. enum tag). Prefixed(Size, Align), @@ -1237,7 +1237,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { } let element = self.layout_of(element)?; - let count = count.val.to_const_int().unwrap().to_u64().unwrap(); + let count = count.val.unwrap_u64(); let size = element.size.checked_mul(count, dl) .ok_or(LayoutError::SizeOverflow(ty))?; diff --git a/src/librustc/ty/maps/config.rs b/src/librustc/ty/maps/config.rs index d880b022e2f18..ef1c8a8d4fa44 100644 --- a/src/librustc/ty/maps/config.rs +++ b/src/librustc/ty/maps/config.rs @@ -13,6 +13,7 @@ use hir::def_id::{CrateNum, DefId, DefIndex}; use ty::{self, Ty, TyCtxt}; use ty::maps::queries; use ty::subst::Substs; +use mir; use std::hash::Hash; use syntax_pos::symbol::InternedString; diff --git a/src/librustc/ty/maps/keys.rs b/src/librustc/ty/maps/keys.rs index b7b64c9761a8e..3dd482ad16401 100644 --- a/src/librustc/ty/maps/keys.rs +++ b/src/librustc/ty/maps/keys.rs @@ -14,6 +14,7 @@ use hir::def_id::{CrateNum, DefId, LOCAL_CRATE, DefIndex}; use ty::{self, Ty, TyCtxt}; use ty::subst::Substs; use ty::fast_reject::SimplifiedType; +use mir; use std::fmt::Debug; use std::hash::Hash; diff --git a/src/librustc/ty/maps/on_disk_cache.rs b/src/librustc/ty/maps/on_disk_cache.rs index b18837ff35aa8..77e022fe73003 100644 --- a/src/librustc/ty/maps/on_disk_cache.rs +++ b/src/librustc/ty/maps/on_disk_cache.rs @@ -15,7 +15,7 @@ use hir::def_id::{CrateNum, DefIndex, DefId, LocalDefId, RESERVED_FOR_INCR_COMP_CACHE, LOCAL_CRATE}; use hir::map::definitions::DefPathHash; use ich::{CachingCodemapView, Fingerprint}; -use mir; +use mir::{self, interpret}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; @@ -542,6 +542,13 @@ impl<'a, 'tcx: 'a, 'x> ty_codec::TyDecoder<'a, 'tcx> for CacheDecoder<'a, 'tcx, implement_ty_decoder!( CacheDecoder<'a, 'tcx, 'x> ); + +impl<'a, 'tcx, 'x> SpecializedDecoder for CacheDecoder<'a, 'tcx, 'x> { + fn specialized_decode(&mut self) -> Result { + unimplemented!() + } +} + impl<'a, 'tcx, 'x> SpecializedDecoder for CacheDecoder<'a, 'tcx, 'x> { fn specialized_decode(&mut self) -> Result { let tag: u8 = Decodable::decode(self)?; diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index be27e3d51529b..1577e78d81e22 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -26,6 +26,7 @@ use middle::lang_items::{FnTraitLangItem, FnMutTraitLangItem, FnOnceTraitLangIte use middle::privacy::AccessLevels; use middle::resolve_lifetime::ObjectLifetimeDefault; use mir::Mir; +use mir::interpret::{Value, PrimVal}; use mir::GeneratorLayout; use session::CrateDisambiguator; use traits; @@ -1838,6 +1839,19 @@ impl<'a, 'gcx, 'tcx> AdtDef { Ok(&ty::Const { val: ConstVal::Integral(v), .. }) => { discr = v; } + Ok(&ty::Const { + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))), + .. + }) => { + trace!("discriminants: {} ({:?})", b, repr_type); + use syntax::attr::IntType; + discr = match repr_type { + IntType::SignedInt(int_type) => ConstInt::new_signed( + b as i128, int_type, tcx.sess.target.isize_ty).unwrap(), + IntType::UnsignedInt(uint_type) => ConstInt::new_unsigned( + b, uint_type, tcx.sess.target.usize_ty).unwrap(), + }; + } err => { if !expr_did.is_local() { span_bug!(tcx.def_span(expr_did), @@ -1879,6 +1893,20 @@ impl<'a, 'gcx, 'tcx> AdtDef { explicit_value = v; break; } + Ok(&ty::Const { + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))), + .. + }) => { + trace!("discriminants: {} ({:?})", b, repr_type); + use syntax::attr::IntType; + explicit_value = match repr_type { + IntType::SignedInt(int_type) => ConstInt::new_signed( + b as i128, int_type, tcx.sess.target.isize_ty).unwrap(), + IntType::UnsignedInt(uint_type) => ConstInt::new_unsigned( + b, uint_type, tcx.sess.target.usize_ty).unwrap(), + }; + break; + } err => { if !expr_did.is_local() { span_bug!(tcx.def_span(expr_did), diff --git a/src/librustc/ty/relate.rs b/src/librustc/ty/relate.rs index b9927c7eeb2fa..bac78508993a5 100644 --- a/src/librustc/ty/relate.rs +++ b/src/librustc/ty/relate.rs @@ -20,6 +20,7 @@ use ty::subst::{UnpackedKind, Substs}; use ty::{self, Ty, TyCtxt, TypeFoldable}; use ty::fold::{TypeVisitor, TypeFolder}; use ty::error::{ExpectedFound, TypeError}; +use mir::interpret::{Value, PrimVal}; use util::common::ErrorReported; use std::rc::Rc; use std::iter; @@ -483,6 +484,7 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R, let to_u64 = |x: &'tcx ty::Const<'tcx>| -> Result { 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. let param_env = ty::ParamEnv::empty(Reveal::UserFacing); @@ -492,6 +494,13 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R, Ok(&ty::Const { val: ConstVal::Integral(x), .. }) => { return Ok(x.to_u64().unwrap()); } + Ok(&ty::Const { + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))), + .. + }) => { + assert_eq!(b as u64 as u128, b); + return Ok(b as u64); + } _ => {} } } diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 54db2d4f06be5..80250949b0b4a 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -890,6 +890,61 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::Slice> { } } +impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> { + fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { + use ty::InstanceDef::*; + Self { + substs: self.substs.fold_with(folder), + def: match self.def { + Item(did) => Item(did.fold_with(folder)), + Intrinsic(did) => Intrinsic(did.fold_with(folder)), + FnPtrShim(did, ty) => FnPtrShim( + did.fold_with(folder), + ty.fold_with(folder), + ), + Virtual(did, i) => Virtual( + did.fold_with(folder), + i, + ), + ClosureOnceShim { call_once } => ClosureOnceShim { + call_once: call_once.fold_with(folder), + }, + DropGlue(did, ty) => DropGlue( + did.fold_with(folder), + ty.fold_with(folder), + ), + CloneShim(did, ty) => CloneShim( + did.fold_with(folder), + ty.fold_with(folder), + ), + }, + } + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + use ty::InstanceDef::*; + self.substs.visit_with(visitor) || + match self.def { + Item(did) => did.visit_with(visitor), + Intrinsic(did) => did.visit_with(visitor), + FnPtrShim(did, ty) => { + did.visit_with(visitor) || + ty.visit_with(visitor) + }, + Virtual(did, _) => did.visit_with(visitor), + ClosureOnceShim { call_once } => call_once.visit_with(visitor), + DropGlue(did, ty) => { + did.visit_with(visitor) || + ty.visit_with(visitor) + }, + CloneShim(did, ty) => { + did.visit_with(visitor) || + ty.visit_with(visitor) + }, + } + } +} + impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { let sty = match self.sty { diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 785035ee5f4f0..6ad2901ce3a10 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -24,6 +24,7 @@ use ty::maps::TyCtxtAt; use ty::TypeVariants::*; use util::common::ErrorReported; use middle::lang_items; +use mir::interpret::{Value, PrimVal}; use rustc_const_math::{ConstInt, ConstIsize, ConstUsize}; use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult, @@ -765,6 +766,7 @@ impl<'a, 'gcx, 'tcx, W> TypeVisitor<'tcx> for TypeIdHasher<'a, 'gcx, 'tcx, W> 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) } diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index a2620da4c10ca..63d1f1468251b 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -21,6 +21,7 @@ use ty::{TyClosure, TyGenerator, TyGeneratorWitness, TyForeign, TyProjection, Ty use ty::{TyDynamic, TyInt, TyUint, TyInfer}; use ty::{self, Ty, TyCtxt, TypeFoldable}; use util::nodemap::FxHashSet; +use mir::interpret::{Value, PrimVal}; use std::cell::Cell; use std::fmt; @@ -1168,6 +1169,9 @@ define_print! { ConstVal::Integral(ConstInt::Usize(sz)) => { write!(f, "{}", sz)?; } + ConstVal::Value(Value::ByVal(PrimVal::Bytes(sz))) => { + write!(f, "{}", sz)?; + } ConstVal::Unevaluated(_def_id, substs) => { write!(f, "", &substs[..])?; } diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs index 8e3b99f2dbfee..9e9eb4a81d035 100644 --- a/src/librustc_const_eval/_match.rs +++ b/src/librustc_const_eval/_match.rs @@ -28,6 +28,7 @@ use rustc::hir::RangeEnd; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc::mir::Field; +use rustc::mir::interpret::{Value, PrimVal}; use rustc::util::common::ErrorReported; use syntax_pos::{Span, DUMMY_SP}; @@ -195,6 +196,41 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { } })).collect() } + box PatternKind::Constant { + value: &ty::Const { val: ConstVal::Value(b), ty } + } => { + match b { + Value::ByVal(PrimVal::Ptr(ptr)) => { + let is_array_ptr = ty + .builtin_deref(true, ty::NoPreference) + .and_then(|t| t.ty.builtin_index()) + .map_or(false, |t| t == tcx.types.u8); + assert!(is_array_ptr); + let alloc = tcx + .interpret_interner + .borrow() + .get_alloc(ptr.alloc_id.0) + .unwrap(); + assert_eq!(ptr.offset, 0); + // FIXME: check length + alloc.bytes.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::Value(Value::ByVal( + PrimVal::Bytes(*b as u128), + )), + ty: tcx.types.u8 + }) + } + }) + }).collect() + }, + _ => bug!("not a byte str: {:?}", b), + } + } _ => span_bug!(pat.span, "unexpected byte array pattern {:?}", pat) } }).clone() @@ -422,13 +458,17 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, ty::TyBool => { [true, false].iter().map(|&b| { ConstantValue(cx.tcx.mk_const(ty::Const { - val: ConstVal::Bool(b), + val: if cx.tcx.sess.opts.debugging_opts.miri { + ConstVal::Value(Value::ByVal(PrimVal::Bytes(b as u128))) + } else { + ConstVal::Bool(b) + }, ty: cx.tcx.types.bool })) }).collect() } - ty::TyArray(ref sub_ty, len) if len.val.to_const_int().is_some() => { - let len = len.val.to_const_int().unwrap().to_u64().unwrap(); + ty::TyArray(ref sub_ty, len) if len.val.to_u128().is_some() => { + let len = len.val.unwrap_u64(); if len != 0 && cx.is_uninhabited(sub_ty) { vec![] } else { @@ -461,7 +501,7 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, } fn max_slice_length<'p, 'a: 'p, 'tcx: 'a, I>( - _cx: &mut MatchCheckCtxt<'a, 'tcx>, + cx: &mut MatchCheckCtxt<'a, 'tcx>, patterns: I) -> u64 where I: Iterator> { @@ -538,6 +578,25 @@ fn max_slice_length<'p, 'a: 'p, 'tcx: 'a, I>( 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))), + ty, + } + } => { + let is_array_ptr = ty + .builtin_deref(true, ty::NoPreference) + .and_then(|t| t.ty.builtin_index()) + .map_or(false, |t| t == cx.tcx.types.u8); + if is_array_ptr { + let alloc = cx.tcx + .interpret_interner + .borrow() + .get_alloc(ptr.alloc_id.0) + .unwrap(); + max_fixed_len = cmp::max(max_fixed_len, alloc.bytes.len() as u64); + } + } PatternKind::Slice { ref prefix, slice: None, ref suffix } => { let fixed_len = prefix.len() as u64 + suffix.len() as u64; max_fixed_len = cmp::max(max_fixed_len, fixed_len); @@ -581,7 +640,7 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, witness: WitnessPreference) -> Usefulness<'tcx> { let &Matrix(ref rows) = matrix; - debug!("is_useful({:?}, {:?})", matrix, v); + debug!("is_useful({:#?}, {:#?})", matrix, v); // The base case. We are pattern-matching on () and the return value is // based on whether our matrix has a row or not. @@ -626,10 +685,10 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, max_slice_length: max_slice_length(cx, rows.iter().map(|r| r[0]).chain(Some(v[0]))) }; - debug!("is_useful_expand_first_col: pcx={:?}, expanding {:?}", pcx, v[0]); + debug!("is_useful_expand_first_col: pcx={:#?}, expanding {:#?}", pcx, v[0]); if let Some(constructors) = pat_constructors(cx, v[0], pcx) { - debug!("is_useful - expanding constructors: {:?}", constructors); + debug!("is_useful - expanding constructors: {:#?}", constructors); constructors.into_iter().map(|c| is_useful_specialized(cx, matrix, v, c.clone(), pcx.ty, witness) ).find(|result| result.is_useful()).unwrap_or(NotUseful) @@ -639,9 +698,9 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, let used_ctors: Vec = rows.iter().flat_map(|row| { pat_constructors(cx, row[0], pcx).unwrap_or(vec![]) }).collect(); - debug!("used_ctors = {:?}", used_ctors); + debug!("used_ctors = {:#?}", used_ctors); let all_ctors = all_constructors(cx, pcx); - debug!("all_ctors = {:?}", all_ctors); + debug!("all_ctors = {:#?}", all_ctors); let missing_ctors: Vec = all_ctors.iter().filter(|c| { !used_ctors.contains(*c) }).cloned().collect(); @@ -669,7 +728,7 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, all_ctors.is_empty() && !cx.is_uninhabited(pcx.ty); let is_declared_nonexhaustive = cx.is_non_exhaustive_enum(pcx.ty) && !cx.is_local(pcx.ty); - debug!("missing_ctors={:?} is_privately_empty={:?} is_declared_nonexhaustive={:?}", + debug!("missing_ctors={:#?} is_privately_empty={:#?} is_declared_nonexhaustive={:#?}", missing_ctors, is_privately_empty, is_declared_nonexhaustive); // For privately empty and non-exhaustive enums, we work as if there were an "extra" @@ -769,7 +828,7 @@ fn is_useful_specialized<'p, 'a:'p, 'tcx: 'a>( lty: Ty<'tcx>, witness: WitnessPreference) -> Usefulness<'tcx> { - debug!("is_useful_specialized({:?}, {:?}, {:?})", v, ctor, lty); + debug!("is_useful_specialized({:#?}, {:#?}, {:?})", v, ctor, lty); let sub_pat_tys = constructor_sub_pattern_tys(cx, &ctor, lty); let wild_patterns_owned: Vec<_> = sub_pat_tys.iter().map(|ty| { Pattern { @@ -821,7 +880,7 @@ fn pat_constructors<'tcx>(_cx: &mut MatchCheckCtxt, Some(vec![ConstantRange(lo, hi, end)]), PatternKind::Array { .. } => match pcx.ty.sty { ty::TyArray(_, length) => Some(vec![ - Slice(length.val.to_const_int().unwrap().to_u64().unwrap()) + Slice(length.val.unwrap_u64()) ]), _ => span_bug!(pat.span, "bad ty {:?} for array pattern", pcx.ty) }, @@ -842,7 +901,7 @@ fn pat_constructors<'tcx>(_cx: &mut MatchCheckCtxt, /// For instance, a tuple pattern (_, 42, Some([])) has the arity of 3. /// A struct pattern's arity is the number of fields it contains, etc. fn constructor_arity(_cx: &MatchCheckCtxt, ctor: &Constructor, ty: Ty) -> u64 { - debug!("constructor_arity({:?}, {:?})", ctor, ty); + debug!("constructor_arity({:#?}, {:?})", ctor, ty); match ty.sty { ty::TyTuple(ref fs, _) => fs.len() as u64, ty::TySlice(..) | ty::TyArray(..) => match *ctor { @@ -866,12 +925,13 @@ fn constructor_sub_pattern_tys<'a, 'tcx: 'a>(cx: &MatchCheckCtxt<'a, 'tcx>, ctor: &Constructor, ty: Ty<'tcx>) -> Vec> { - debug!("constructor_sub_pattern_tys({:?}, {:?})", ctor, ty); + debug!("constructor_sub_pattern_tys({:#?}, {:?})", ctor, ty); match ty.sty { ty::TyTuple(ref fs, _) => fs.into_iter().map(|t| *t).collect(), ty::TySlice(ty) | ty::TyArray(ty, _) => match *ctor { Slice(length) => (0..length).map(|_| ty).collect(), ConstantValue(_) => vec![], + Single => vec![ty], _ => bug!("bad slice pattern {:?} {:?}", ctor, ty) }, ty::TyRef(_, ref ty_and_mut) => vec![ty_and_mut.ty], @@ -880,6 +940,9 @@ fn constructor_sub_pattern_tys<'a, 'tcx: 'a>(cx: &MatchCheckCtxt<'a, 'tcx>, // Use T as the sub pattern type of Box. vec![substs.type_at(0)] } else { + if let ConstantValue(_) = *ctor { + return vec![]; + } adt.variants[ctor.variant_index_for_adt(adt)].fields.iter().map(|field| { let is_visible = adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx); @@ -901,14 +964,30 @@ fn constructor_sub_pattern_tys<'a, 'tcx: 'a>(cx: &MatchCheckCtxt<'a, 'tcx>, } } -fn slice_pat_covered_by_constructor(_tcx: TyCtxt, _span: Span, +fn slice_pat_covered_by_constructor(tcx: TyCtxt, _span: Span, ctor: &Constructor, prefix: &[Pattern], slice: &Option, suffix: &[Pattern]) -> Result { - let data = match *ctor { + 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 }) => { + let is_array_ptr = ty + .builtin_deref(true, ty::NoPreference) + .and_then(|t| t.ty.builtin_index()) + .map_or(false, |t| t == tcx.types.u8); + assert!(is_array_ptr); + tcx + .interpret_interner + .borrow() + .get_alloc(ptr.alloc_id.0) + .unwrap() + .bytes + .as_ref() + } _ => bug!() }; @@ -928,6 +1007,12 @@ fn slice_pat_covered_by_constructor(_tcx: TyCtxt, _span: Span, return Ok(false); } }, + ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))) => { + assert_eq!(b as u8 as u128, b); + if b as u8 != *ch { + return Ok(false); + } + } _ => span_bug!(pat.span, "bad const u8 {:?}", value) }, _ => {} @@ -937,32 +1022,43 @@ fn slice_pat_covered_by_constructor(_tcx: TyCtxt, _span: Span, Ok(true) } -fn constructor_covered_by_range(tcx: TyCtxt, span: Span, - ctor: &Constructor, +fn constructor_covered_by_range(ctor: &Constructor, from: &ConstVal, to: &ConstVal, - end: RangeEnd) + end: RangeEnd, + ty: Ty) -> Result { - let cmp_from = |c_from| Ok(compare_const_vals(tcx, span, c_from, from)? != Ordering::Less); - let cmp_to = |c_to| compare_const_vals(tcx, span, c_to, to); + trace!("constructor_covered_by_range {:#?}, {:#?}, {:#?}, {}", ctor, from, to, ty); + let cmp_from = |c_from| compare_const_vals(c_from, from, ty) + .map(|res| res != Ordering::Less); + let cmp_to = |c_to| compare_const_vals(c_to, to, ty); + macro_rules! some_or_ok { + ($e:expr) => { + match $e { + Some(to) => to, + None => return Ok(false), // not char or int + } + }; + } match *ctor { ConstantValue(value) => { - let to = cmp_to(&value.val)?; + let to = some_or_ok!(cmp_to(&value.val)); let end = (to == Ordering::Less) || (end == RangeEnd::Included && to == Ordering::Equal); - Ok(cmp_from(&value.val)? && end) + Ok(some_or_ok!(cmp_from(&value.val)) && end) }, ConstantRange(from, to, RangeEnd::Included) => { - let to = cmp_to(&to.val)?; + let to = some_or_ok!(cmp_to(&to.val)); let end = (to == Ordering::Less) || (end == RangeEnd::Included && to == Ordering::Equal); - Ok(cmp_from(&from.val)? && end) + Ok(some_or_ok!(cmp_from(&from.val)) && end) }, ConstantRange(from, to, RangeEnd::Excluded) => { - let to = cmp_to(&to.val)?; + let to = some_or_ok!(cmp_to(&to.val)); let end = (to == Ordering::Less) || (end == RangeEnd::Excluded && to == Ordering::Equal); - Ok(cmp_from(&from.val)? && end) + Ok(some_or_ok!(cmp_from(&from.val)) && end) } + Variant(_) | Single => Ok(true), _ => bug!(), } @@ -979,7 +1075,7 @@ fn patterns_for_variant<'p, 'a: 'p, 'tcx: 'a>( result[subpat.field.index()] = &subpat.pattern; } - debug!("patterns_for_variant({:?}, {:?}) = {:?}", subpatterns, wild_patterns, result); + debug!("patterns_for_variant({:#?}, {:#?}) = {:#?}", subpatterns, wild_patterns, result); result } @@ -994,7 +1090,7 @@ fn patterns_for_variant<'p, 'a: 'p, 'tcx: 'a>( fn specialize<'p, 'a: 'p, 'tcx: 'a>( cx: &mut MatchCheckCtxt<'a, 'tcx>, r: &[&'p Pattern<'tcx>], - constructor: &Constructor, + constructor: &Constructor<'tcx>, wild_patterns: &[&'p Pattern<'tcx>]) -> Option>> { @@ -1031,12 +1127,32 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>( None } } + ConstVal::Value(Value::ByVal(PrimVal::Ptr(ptr))) => { + let is_array_ptr = value.ty + .builtin_deref(true, ty::NoPreference) + .and_then(|t| t.ty.builtin_index()) + .map_or(false, |t| t == cx.tcx.types.u8); + assert!(is_array_ptr); + let data_len = cx.tcx + .interpret_interner + .borrow() + .get_alloc(ptr.alloc_id.0) + .unwrap() + .bytes + .len(); + if wild_patterns.len() == data_len { + Some(cx.lower_byte_str_pattern(pat)) + } else { + None + } + } _ => span_bug!(pat.span, "unexpected const-val {:?} with ctor {:?}", value, constructor) }, _ => { match constructor_covered_by_range( - cx.tcx, pat.span, constructor, &value.val, &value.val, RangeEnd::Included + constructor, &value.val, &value.val, RangeEnd::Included, + value.ty, ) { Ok(true) => Some(vec![]), Ok(false) => None, @@ -1048,7 +1164,7 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>( PatternKind::Range { lo, hi, ref end } => { match constructor_covered_by_range( - cx.tcx, pat.span, constructor, &lo.val, &hi.val, end.clone() + constructor, &lo.val, &hi.val, end.clone(), lo.ty, ) { Ok(true) => Some(vec![]), Ok(false) => None, @@ -1092,7 +1208,7 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>( } } }; - debug!("specialize({:?}, {:?}) = {:?}", r[0], wild_patterns, head); + debug!("specialize({:#?}, {:#?}) = {:#?}", r[0], wild_patterns, head); head.map(|mut head| { head.extend_from_slice(&r[1 ..]); diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index 2a571fa82643b..58fe40d12be84 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -26,7 +26,6 @@ use syntax::abi::Abi; use syntax::ast; use syntax::attr; use rustc::hir::{self, Expr}; -use syntax_pos::Span; use std::cmp::Ordering; @@ -104,60 +103,10 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, hir::ExprUnary(hir::UnNeg, ref inner) => { // unary neg literals already got their sign during creation if let hir::ExprLit(ref lit) = inner.node { - use syntax::ast::*; - use syntax::ast::LitIntType::*; - const I8_OVERFLOW: u128 = i8::min_value() as u8 as u128; - const I16_OVERFLOW: u128 = i16::min_value() as u16 as u128; - const I32_OVERFLOW: u128 = i32::min_value() as u32 as u128; - const I64_OVERFLOW: u128 = i64::min_value() as u64 as u128; - const I128_OVERFLOW: u128 = i128::min_value() as u128; - let negated = match (&lit.node, &ty.sty) { - (&LitKind::Int(I8_OVERFLOW, _), &ty::TyInt(IntTy::I8)) | - (&LitKind::Int(I8_OVERFLOW, Signed(IntTy::I8)), _) => { - Some(I8(i8::min_value())) - }, - (&LitKind::Int(I16_OVERFLOW, _), &ty::TyInt(IntTy::I16)) | - (&LitKind::Int(I16_OVERFLOW, Signed(IntTy::I16)), _) => { - Some(I16(i16::min_value())) - }, - (&LitKind::Int(I32_OVERFLOW, _), &ty::TyInt(IntTy::I32)) | - (&LitKind::Int(I32_OVERFLOW, Signed(IntTy::I32)), _) => { - Some(I32(i32::min_value())) - }, - (&LitKind::Int(I64_OVERFLOW, _), &ty::TyInt(IntTy::I64)) | - (&LitKind::Int(I64_OVERFLOW, Signed(IntTy::I64)), _) => { - Some(I64(i64::min_value())) - }, - (&LitKind::Int(I128_OVERFLOW, _), &ty::TyInt(IntTy::I128)) | - (&LitKind::Int(I128_OVERFLOW, Signed(IntTy::I128)), _) => { - Some(I128(i128::min_value())) - }, - (&LitKind::Int(n, _), &ty::TyInt(IntTy::Isize)) | - (&LitKind::Int(n, Signed(IntTy::Isize)), _) => { - match tcx.sess.target.isize_ty { - IntTy::I16 => if n == I16_OVERFLOW { - Some(Isize(Is16(i16::min_value()))) - } else { - None - }, - IntTy::I32 => if n == I32_OVERFLOW { - Some(Isize(Is32(i32::min_value()))) - } else { - None - }, - IntTy::I64 => if n == I64_OVERFLOW { - Some(Isize(Is64(i64::min_value()))) - } else { - None - }, - _ => span_bug!(e.span, "typeck error") - } - }, - _ => None + return match lit_to_const(&lit.node, tcx, ty, true) { + Ok(val) => Ok(mk_const(val)), + Err(err) => signal!(e, err), }; - if let Some(i) = negated { - return Ok(mk_const(Integral(i))); - } } mk_const(match cx.eval(inner)?.val { Float(f) => Float(-f), @@ -377,7 +326,7 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, }; callee_cx.eval(&body.value)? }, - hir::ExprLit(ref lit) => match lit_to_const(&lit.node, tcx, ty) { + hir::ExprLit(ref lit) => match lit_to_const(&lit.node, tcx, ty, false) { Ok(val) => mk_const(val), Err(err) => signal!(e, err), }, @@ -438,7 +387,7 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, } hir::ExprRepeat(ref elem, _) => { let n = match ty.sty { - ty::TyArray(_, n) => n.val.to_const_int().unwrap().to_u64().unwrap(), + ty::TyArray(_, n) => n.val.unwrap_u64(), _ => span_bug!(e.span, "typeck error") }; mk_const(Aggregate(Repeat(cx.eval(elem)?, n))) @@ -447,7 +396,8 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, if let Aggregate(Tuple(fields)) = cx.eval(base)?.val { fields[index.node] } else { - signal!(base, ExpectedConstTuple); + span_bug!(base.span, "{:#?}", cx.eval(base)?.val); + //signal!(base, ExpectedConstTuple); } } hir::ExprField(ref base, field_name) => { @@ -557,7 +507,7 @@ fn cast_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, }, ty::TyRef(_, ty::TypeAndMut { ref ty, mutbl: hir::MutImmutable }) => match ty.sty { ty::TyArray(ty, n) => { - let n = n.val.to_const_int().unwrap().to_u64().unwrap(); + let n = n.val.unwrap_u64(); if ty == tcx.types.u8 && n == b.data.len() as u64 { Ok(val) } else { @@ -583,13 +533,66 @@ fn cast_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } -fn lit_to_const<'a, 'tcx>(lit: &'tcx ast::LitKind, +pub fn lit_to_const<'a, 'tcx>(lit: &'tcx ast::LitKind, tcx: TyCtxt<'a, 'tcx, 'tcx>, - mut ty: Ty<'tcx>) + mut ty: Ty<'tcx>, + neg: bool) -> Result, 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) @@ -604,26 +607,38 @@ fn lit_to_const<'a, 'tcx>(lit: &'tcx ast::LitKind, match (&ty.sty, hint) { (&ty::TyInt(ity), _) | (_, Signed(ity)) => { - Ok(Integral(ConstInt::new_signed_truncating(n as i128, + 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 as u128, + Ok(Integral(ConstInt::new_unsigned_truncating(n, uty, tcx.sess.target.usize_ty))) } _ => bug!() } } LitKind::Float(n, fty) => { - parse_float(&n.as_str(), fty).map(Float) + let mut f = parse_float(&n.as_str(), fty)?; + if neg { + f = -f; + } + Ok(Float(f)) } LitKind::FloatUnsuffixed(n) => { let fty = match ty.sty { ty::TyFloat(fty) => fty, _ => bug!() }; - parse_float(&n.as_str(), fty).map(Float) + let mut f = parse_float(&n.as_str(), fty)?; + if neg { + f = -f; + } + Ok(Float(f)) } LitKind::Bool(b) => Ok(Bool(b)), LitKind::Char(c) => Ok(Char(c)), @@ -638,36 +653,31 @@ fn parse_float<'tcx>(num: &str, fty: ast::FloatTy) }) } -pub fn compare_const_vals(tcx: TyCtxt, span: Span, a: &ConstVal, b: &ConstVal) - -> Result -{ - let result = match (a, b) { +pub fn compare_const_vals(a: &ConstVal, b: &ConstVal, ty: Ty) -> Option { + trace!("compare_const_vals: {:?}, {:?}", a, b); + use rustc::mir::interpret::{Value, PrimVal}; + match (a, b) { (&Integral(a), &Integral(b)) => a.try_cmp(b).ok(), - (&Float(a), &Float(b)) => a.try_cmp(b).ok(), - (&Str(ref a), &Str(ref b)) => Some(a.cmp(b)), - (&Bool(a), &Bool(b)) => Some(a.cmp(&b)), - (&ByteStr(a), &ByteStr(b)) => Some(a.data.cmp(b.data)), (&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) + }) + }, + _ if a == b => Some(Ordering::Equal), _ => None, - }; - - match result { - Some(result) => Ok(result), - None => { - // FIXME: can this ever be reached? - tcx.sess.delay_span_bug(span, - &format!("type mismatch comparing {:?} and {:?}", a, b)); - Err(ErrorReported) - } } } impl<'a, 'tcx> ConstContext<'a, 'tcx> { pub fn compare_lit_exprs(&self, - span: Span, a: &'tcx Expr, - b: &'tcx Expr) -> Result { + b: &'tcx Expr) -> Result, ErrorReported> { let tcx = self.tcx; + let ty = self.tables.expr_ty(a); let a = match self.eval(a) { Ok(a) => a, Err(e) => { @@ -682,6 +692,6 @@ impl<'a, 'tcx> ConstContext<'a, 'tcx> { return Err(ErrorReported); } }; - compare_const_vals(tcx, span, &a.val, &b.val) + Ok(compare_const_vals(&a.val, &b.val, ty)) } } diff --git a/src/librustc_const_eval/pattern.rs b/src/librustc_const_eval/pattern.rs index a09e2f2edd59d..a2daf22c3b45b 100644 --- a/src/librustc_const_eval/pattern.rs +++ b/src/librustc_const_eval/pattern.rs @@ -10,8 +10,9 @@ use eval; -use rustc::middle::const_val::{ConstEvalErr, ConstVal}; +use rustc::middle::const_val::{ConstEvalErr, ConstVal, ConstAggregate}; use rustc::mir::{Field, BorrowKind, Mutability}; +use rustc::mir::interpret::{Value, PrimVal}; use rustc::ty::{self, TyCtxt, AdtDef, Ty, Region}; use rustc::ty::subst::{Substs, Kind}; use rustc::hir::{self, PatKind, RangeEnd}; @@ -110,22 +111,35 @@ pub enum PatternKind<'tcx> { }, } -fn print_const_val(value: &ConstVal, f: &mut fmt::Formatter) -> fmt::Result { - match *value { +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::Value(_) | ConstVal::Unevaluated(..) => bug!("{:?} not printable in a pattern", value) } } +fn print_miri_value(value: Value, ty: Ty, f: &mut fmt::Formatter) -> fmt::Result { + use rustc::ty::TypeVariants::*; + match (value, &ty.sty) { + (Value::ByVal(PrimVal::Bytes(0)), &TyBool) => write!(f, "false"), + (Value::ByVal(PrimVal::Bytes(1)), &TyBool) => write!(f, "true"), + (Value::ByVal(PrimVal::Bytes(n)), &TyUint(..)) => write!(f, "{:?}", n), + (Value::ByVal(PrimVal::Bytes(n)), &TyInt(..)) => write!(f, "{:?}", n as i128), + (Value::ByVal(PrimVal::Bytes(n)), &TyChar) => + write!(f, "{:?}", ::std::char::from_u32(n as u32).unwrap()), + _ => bug!("{:?}: {} not printable in a pattern", value, ty), + } +} + impl<'tcx> fmt::Display for Pattern<'tcx> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self.kind { @@ -233,15 +247,15 @@ impl<'tcx> fmt::Display for Pattern<'tcx> { write!(f, "{}", subpattern) } PatternKind::Constant { value } => { - print_const_val(&value.val, f) + print_const_val(value, f) } PatternKind::Range { lo, hi, end } => { - print_const_val(&lo.val, f)?; + print_const_val(lo, f)?; match end { RangeEnd::Included => write!(f, "...")?, RangeEnd::Excluded => write!(f, "..")?, } - print_const_val(&hi.val, f) + print_const_val(hi, f) } PatternKind::Slice { ref prefix, ref slice, ref suffix } | PatternKind::Array { ref prefix, ref slice, ref suffix } => { @@ -362,7 +376,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } PatKind::Path(ref qpath) => { - return self.lower_path(qpath, pat.hir_id, pat.id, pat.span); + return self.lower_path(qpath, pat.hir_id, pat.span); } PatKind::Ref(ref subpattern, _) | @@ -581,7 +595,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { ty::TyArray(_, len) => { // fixed-length array - let len = len.val.to_const_int().unwrap().to_u64().unwrap(); + let len = len.val.unwrap_u64(); assert!(len >= prefix.len() as u64 + suffix.len() as u64); PatternKind::Array { prefix: prefix, slice: slice, suffix: suffix } } @@ -632,7 +646,6 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { fn lower_path(&mut self, qpath: &hir::QPath, id: hir::HirId, - pat_id: ast::NodeId, span: Span) -> Pattern<'tcx> { let ty = self.tables.node_id_to_type(id); @@ -644,29 +657,23 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { let kind = match def { Def::Const(def_id) | Def::AssociatedConst(def_id) => { let substs = self.tables.node_substs(id); - match eval::lookup_const_by_id(self.tcx, self.param_env.and((def_id, substs))) { - Some((def_id, substs)) => { - // Enter the inlined constant's tables&substs temporarily. - let old_tables = self.tables; - let old_substs = self.substs; - self.tables = self.tcx.typeck_tables_of(def_id); - self.substs = substs; - let body = if let Some(id) = self.tcx.hir.as_local_node_id(def_id) { - self.tcx.hir.body(self.tcx.hir.body_owned_by(id)) - } else { - self.tcx.extern_const_body(def_id).body - }; - let pat = self.lower_const_expr(&body.value, pat_id, span); - self.tables = old_tables; - self.substs = old_substs; - return pat; - } - None => { - self.errors.push(if is_associated_const { - PatternError::AssociatedConstInPattern(span) - } else { - PatternError::StaticInPattern(span) - }); + 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) + }, + Err(e) => { + self.errors.push(PatternError::ConstEval(e)); PatternKind::Wild } } @@ -682,6 +689,52 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } fn lower_lit(&mut self, expr: &'tcx hir::Expr) -> PatternKind<'tcx> { + if self.tcx.sess.opts.debugging_opts.miri { + return match expr.node { + hir::ExprLit(ref lit) => { + let ty = self.tables.expr_ty(expr); + match ::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 ::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 + }, + } + } + _ => span_bug!(expr.span, "not a literal: {:?}", expr), + } + } let const_cx = eval::ConstContext::new(self.tcx, self.param_env.and(self.substs), self.tables); @@ -701,118 +754,156 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } } - fn lower_const_expr(&mut self, - expr: &'tcx hir::Expr, - pat_id: ast::NodeId, - span: Span) - -> Pattern<'tcx> { - let pat_ty = self.tables.expr_ty(expr); - debug!("expr={:?} pat_ty={:?} pat_id={}", expr, pat_ty, pat_id); - match pat_ty.sty { + fn const_to_pat( + &self, + instance: ty::Instance<'tcx>, + cv: &'tcx ty::Const<'tcx>, + 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 } 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"); + PatternKind::Wild } + ty::TyAdt(adt_def, _) if !self.tcx.has_attr(adt_def.did, "structural_match") => { + let msg = format!("to use a constant of type `{}` in a pattern, \ + `{}` must be annotated with `#[derive(PartialEq, Eq)]`", + self.tcx.item_path_str(adt_def.did), + self.tcx.item_path_str(adt_def.did)); + self.tcx.sess.span_err(span, &msg); + PatternKind::Wild + }, + ty::TyAdt(adt_def, substs) if adt_def.is_enum() => { + match cv.val { + ConstVal::Value(val) => { + let discr = self.tcx.const_discr(self.param_env.and(( + instance, val, cv.ty + ))).unwrap(); + let variant_index = adt_def + .discriminants(self.tcx) + .position(|var| var.to_u128_unchecked() == discr) + .unwrap(); + PatternKind::Variant { + adt_def, + substs, + variant_index, + subpatterns: adt_def + .variants[variant_index] + .fields + .iter() + .enumerate() + .map(|(i, _)| { + let field = Field::new(i); + let val = match cv.val { + ConstVal::Value(miri) => self.tcx.const_val_field( + self.param_env.and((instance, field, miri, cv.ty)), + ).unwrap(), + _ => bug!("{:#?} is not a valid tuple", cv), + }; + FieldPattern { + field, + pattern: self.const_to_pat(instance, val, 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, + kind: Box::new(PatternKind::Constant { + value: cv, + }), + } + } + }, ty::TyAdt(adt_def, _) => { - if !self.tcx.has_attr(adt_def.did, "structural_match") { - let msg = format!("to use a constant of type `{}` in a pattern, \ - `{}` must be annotated with `#[derive(PartialEq, Eq)]`", - self.tcx.item_path_str(adt_def.did), - self.tcx.item_path_str(adt_def.did)); - self.tcx.sess.span_err(span, &msg); + let struct_var = adt_def.struct_variant(); + PatternKind::Leaf { + subpatterns: struct_var.fields.iter().enumerate().map(|(i, f)| { + 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) => self.tcx.const_val_field( + self.param_env.and((instance, field, miri, cv.ty)), + ).unwrap(), + _ => bug!("{:#?} is not a valid tuple", cv), + }; + FieldPattern { + field, + pattern: self.const_to_pat(instance, val, span), + } + }).collect() } } - _ => { } - } - let kind = match expr.node { - hir::ExprTup(ref exprs) => { + ty::TyTuple(fields, _) => { PatternKind::Leaf { - subpatterns: exprs.iter().enumerate().map(|(i, expr)| { + 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) => self.tcx.const_val_field( + self.param_env.and((instance, field, miri, cv.ty)), + ).unwrap(), + _ => bug!("{:#?} is not a valid tuple", cv), + }; FieldPattern { - field: Field::new(i), - pattern: self.lower_const_expr(expr, pat_id, span) + field, + pattern: self.const_to_pat(instance, val, span), } }).collect() } } - - hir::ExprCall(ref callee, ref args) => { - let qpath = match callee.node { - hir::ExprPath(ref qpath) => qpath, - _ => bug!() - }; - let ty = self.tables.node_id_to_type(callee.hir_id); - let def = self.tables.qpath_def(qpath, callee.hir_id); - match def { - Def::Fn(..) | Def::Method(..) => self.lower_lit(expr), - _ => { - let subpatterns = args.iter().enumerate().map(|(i, expr)| { - FieldPattern { - field: Field::new(i), - pattern: self.lower_const_expr(expr, pat_id, span) - } - }).collect(); - self.lower_variant_or_leaf(def, ty, subpatterns) - } + ty::TyArray(_, n) => { + PatternKind::Leaf { + subpatterns: (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) => self.tcx.const_val_field( + self.param_env.and((instance, field, miri, cv.ty)), + ).unwrap(), + _ => bug!("{:#?} is not a valid tuple", cv), + }; + FieldPattern { + field, + pattern: self.const_to_pat(instance, val, span), + } + }).collect() } } - - hir::ExprStruct(ref qpath, ref fields, None) => { - let def = self.tables.qpath_def(qpath, expr.hir_id); - let adt_def = match pat_ty.sty { - ty::TyAdt(adt_def, _) => adt_def, - _ => { - span_bug!( - expr.span, - "struct expr without ADT type"); - } - }; - let variant_def = adt_def.variant_of_def(def); - - let subpatterns = - fields.iter() - .map(|field| { - let index = variant_def.index_of_field_named(field.name.node); - let index = index.unwrap_or_else(|| { - span_bug!( - expr.span, - "no field with name {:?}", - field.name); - }); - FieldPattern { - field: Field::new(index), - pattern: self.lower_const_expr(&field.expr, pat_id, span), - } - }) - .collect(); - - self.lower_variant_or_leaf(def, pat_ty, subpatterns) - } - - hir::ExprArray(ref exprs) => { - let pats = exprs.iter() - .map(|expr| self.lower_const_expr(expr, pat_id, span)) - .collect(); - PatternKind::Array { - prefix: pats, - slice: None, - suffix: vec![] + _ => { + PatternKind::Constant { + value: cv, } - } - - hir::ExprPath(ref qpath) => { - return self.lower_path(qpath, expr.hir_id, pat_id, span); - } - - _ => self.lower_lit(expr) + }, }; Pattern { span, - ty: pat_ty, + ty: cv.ty, kind: Box::new(kind), } } diff --git a/src/librustc_data_structures/stable_hasher.rs b/src/librustc_data_structures/stable_hasher.rs index d82b712b5b14b..70733bc6aeda0 100644 --- a/src/librustc_data_structures/stable_hasher.rs +++ b/src/librustc_data_structures/stable_hasher.rs @@ -259,6 +259,14 @@ impl HashStable for f64 { } } +impl HashStable for ::std::cmp::Ordering { + fn hash_stable(&self, + ctx: &mut CTX, + hasher: &mut StableHasher) { + (*self as i8).hash_stable(ctx, hasher); + } +} + impl, CTX> HashStable for (T1,) { fn hash_stable(&self, ctx: &mut CTX, diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 1c4bd0ff4c2ec..f400ce42a9046 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -16,6 +16,7 @@ use rustc::ty::{self, AdtKind, Ty, TyCtxt}; use rustc::ty::layout::{self, LayoutOf}; use middle::const_val::ConstVal; use rustc_const_eval::ConstContext; +use rustc::mir::interpret::{Value, PrimVal}; use util::nodemap::FxHashSet; use lint::{LateContext, LintContext, LintArray}; use lint::{LintPass, LateLintPass}; @@ -122,6 +123,16 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { .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, } }; diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 3c3c489d0ff78..1663cab0a592e 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -276,38 +276,54 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { fn specialized_decode(&mut self) -> Result { const MAX1: usize = usize::max_value() - 1; - let mut interpret_interner = self.tcx.unwrap().interpret_interner.borrow_mut(); + let tcx = self.tcx; + let interpret_interner = || tcx.unwrap().interpret_interner.borrow_mut(); let pos = self.position(); - match self.read_usize()? { + match usize::decode(self)? { ::std::usize::MAX => { + let id = interpret_interner().reserve(); + let alloc_id = interpret::AllocId(id); + 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)?; - let id = interpret_interner.reserve(); + trace!("decoded alloc {:?} {:#?}", alloc_id, allocation); let allocation = self.tcx.unwrap().intern_const_alloc(allocation); - interpret_interner.intern_at_reserved(id, allocation); - let id = interpret::AllocId(id); - self.interpret_alloc_cache.insert(pos, id); + interpret_interner().intern_at_reserved(id, allocation); let num = usize::decode(self)?; let ptr = interpret::Pointer { primval: interpret::PrimVal::Ptr(interpret::MemoryPointer { - alloc_id: id, + alloc_id, offset: 0, }), }; for _ in 0..num { let glob = interpret::GlobalId::decode(self)?; - interpret_interner.cache(glob, ptr); + interpret_interner().cache(glob, ptr); } - Ok(id) + Ok(alloc_id) }, MAX1 => { + trace!("creating fn alloc id at {}", pos); let instance = ty::Instance::decode(self)?; - let id = interpret::AllocId(interpret_interner.create_fn_alloc(instance)); + trace!("decoded fn alloc instance: {:?}", instance); + let id = interpret::AllocId(interpret_interner().create_fn_alloc(instance)); + trace!("created fn alloc id: {:?}", id); self.interpret_alloc_cache.insert(pos, id); Ok(id) }, - shorthand => Ok(self.interpret_alloc_cache[&shorthand]), + shorthand => { + trace!("loading shorthand {}", shorthand); + if let Some(&alloc_id) = self.interpret_alloc_cache.get(&shorthand) { + return Ok(alloc_id); + } + trace!("shorthand {} not cached, loading entire allocation", shorthand); + // need to load allocation + self.with_position(shorthand, |this| interpret::AllocId::decode(this)) + }, } } } diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 928bf0a56ae51..1e1baba2bac5e 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -189,12 +189,18 @@ impl<'a, 'tcx> SpecializedEncoder> for EncodeContext<'a, 'tcx> { impl<'a, 'tcx> SpecializedEncoder for EncodeContext<'a, 'tcx> { fn specialized_encode(&mut self, alloc_id: &interpret::AllocId) -> Result<(), Self::Error> { + trace!("encoding {:?} at {}", alloc_id, self.position()); if let Some(shorthand) = self.interpret_alloc_shorthands.get(alloc_id).cloned() { - return self.emit_usize(shorthand); + trace!("encoding {:?} as shorthand to {}", alloc_id, shorthand); + return shorthand.encode(self); } let start = self.position(); + // cache the allocation shorthand now, because the allocation itself might recursively + // 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) { + trace!("encoding {:?} with {:#?}", alloc_id, alloc); usize::max_value().encode(self)?; alloc.encode(self)?; let globals = interpret_interner.get_globals(interpret::Pointer { @@ -208,16 +214,12 @@ impl<'a, 'tcx> SpecializedEncoder for EncodeContext<'a, 'tcx glob.encode(self)?; } } else if let Some(fn_instance) = interpret_interner.get_fn(alloc_id.0) { + 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); } - let len = self.position() - start * 7; - // Check that the shorthand is a not longer than the - // full encoding itself, i.e. it's an obvious win. - assert!(len >= 64 || (start as u64) < (1 << len)); - self.interpret_alloc_shorthands.insert(*alloc_id, start); Ok(()) } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index bbf4357e5b0f9..5955d0ca59adf 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -26,6 +26,7 @@ use rustc::ty::fold::TypeFoldable; use rustc::ty::{self, ToPolyTraitRef, Ty, TyCtxt, TypeVariants}; use rustc::middle::const_val::ConstVal; use rustc::mir::*; +use rustc::mir::interpret::{Value, PrimVal}; use rustc::mir::tcx::PlaceTy; use rustc::mir::visit::{PlaceContext, Visitor}; use std::fmt; @@ -258,7 +259,24 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { // constraints on `'a` and `'b`. These constraints // would be lost if we just look at the normalized // value. - if let ConstVal::Function(def_id, ..) = value.val { + 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) + .map(|instance| instance.def_id()) + }, + ConstVal::Value(Value::ByVal(PrimVal::Undef)) => { + match value.ty.sty { + ty::TyFnDef(ty_def_id, _) => Some(ty_def_id), + _ => None, + } + }, + _ => None, + }; + if let Some(def_id) = did { let tcx = self.tcx(); let type_checker = &mut self.cx; @@ -436,7 +454,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { ProjectionElem::Subslice { from, to } => PlaceTy::Ty { ty: match base_ty.sty { ty::TyArray(inner, size) => { - let size = size.val.to_const_int().unwrap().to_u64().unwrap(); + let size = size.val.unwrap_u64(); let min_size = (from as u64) + (to as u64); if let Some(rest_size) = size.checked_sub(min_size) { tcx.mk_array(inner, rest_size) @@ -1019,13 +1037,32 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { Literal::Value { value: &ty::Const { - val: ConstVal::Function(def_id, _), - .. + val, + ty, }, .. }, .. - }) => Some(def_id) == self.tcx().lang_items().box_free_fn(), + }) => 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); + inst.map_or(false, |inst| { + Some(inst.def_id()) == self.tcx().lang_items().box_free_fn() + }) + }, + ConstVal::Value(Value::ByVal(PrimVal::Undef)) => { + match ty.sty { + ty::TyFnDef(ty_def_id, _) => { + Some(ty_def_id) == self.tcx().lang_items().box_free_fn() + } + _ => false, + } + } + _ => false, + } _ => false, } } diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index d3cc952759058..b5b8f8d7e78b0 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -24,6 +24,7 @@ use rustc::middle::const_val::ConstVal; use rustc::middle::region; use rustc::ty::{self, Ty}; use rustc::mir::*; +use rustc::mir::interpret::{Value, PrimVal}; use syntax::ast; use syntax_pos::Span; @@ -203,7 +204,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { ty: this.hir.tcx().types.u32, literal: Literal::Value { value: this.hir.tcx().mk_const(ty::Const { - val: ConstVal::Integral(ConstInt::U32(0)), + val: if this.hir.tcx().sess.opts.debugging_opts.miri { + ConstVal::Value(Value::ByVal(PrimVal::Bytes(0))) + } else { + ConstVal::Integral(ConstInt::U32(0)) + }, ty: this.hir.tcx().types.u32 }), }, @@ -401,7 +406,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { Literal::Value { value: self.hir.tcx().mk_const(ty::Const { - val: ConstVal::Integral(val), + val: if self.hir.tcx().sess.opts.debugging_opts.miri { + ConstVal::Value(Value::ByVal(PrimVal::Bytes(val.to_u128_unchecked()))) + } else { + ConstVal::Integral(val) + }, ty }) } @@ -439,7 +448,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { Literal::Value { value: self.hir.tcx().mk_const(ty::Const { - val: ConstVal::Integral(val), + val: if self.hir.tcx().sess.opts.debugging_opts.miri { + ConstVal::Value(Value::ByVal(PrimVal::Bytes( + val.to_u128_unchecked() + ))) + } else { + ConstVal::Integral(val) + }, ty }) } diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 58ce572ae8d88..229e33dcd7862 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -354,7 +354,7 @@ enum TestKind<'tcx> { // test the branches of enum SwitchInt { switch_ty: Ty<'tcx>, - options: Vec<&'tcx ty::Const<'tcx>>, + options: Vec, indices: FxHashMap<&'tcx ty::Const<'tcx>, usize>, }, diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index bdcbfc0bdd85e..fafdee5b1e1b9 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -24,6 +24,7 @@ use rustc::middle::const_val::ConstVal; use rustc::ty::{self, Ty}; use rustc::ty::util::IntTypeExt; use rustc::mir::*; +use rustc::mir::interpret::{Value, PrimVal}; use rustc::hir::RangeEnd; use syntax_pos::Span; use std::cmp::Ordering; @@ -112,7 +113,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { test_place: &Place<'tcx>, candidate: &Candidate<'pat, 'tcx>, switch_ty: Ty<'tcx>, - options: &mut Vec<&'tcx ty::Const<'tcx>>, + options: &mut Vec, indices: &mut FxHashMap<&'tcx ty::Const<'tcx>, usize>) -> bool { @@ -128,7 +129,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { indices.entry(value) .or_insert_with(|| { - options.push(value); + options.push(value.val.to_u128().expect("switching on int")); options.len() - 1 }); true @@ -231,7 +232,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let tcx = self.hir.tcx(); for (idx, discr) in adt_def.discriminants(tcx).enumerate() { target_blocks.place_back() <- if variants.contains(idx) { - values.push(discr); + values.push(discr.to_u128_unchecked()); *(targets.place_back() <- self.cfg.start_new_block()) } else { if otherwise_block.is_none() { @@ -266,9 +267,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { assert!(options.len() > 0 && options.len() <= 2); let (true_bb, false_bb) = (self.cfg.start_new_block(), self.cfg.start_new_block()); - let ret = match options[0].val { - ConstVal::Bool(true) => vec![true_bb, false_bb], - ConstVal::Bool(false) => vec![false_bb, true_bb], + let ret = match options[0] { + 1 => vec![true_bb, false_bb], + 0 => vec![false_bb, true_bb], v => span_bug!(test.span, "expected boolean value but got {:?}", v) }; (ret, TerminatorKind::if_(self.hir.tcx(), Operand::Copy(place.clone()), @@ -282,13 +283,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { .map(|_| self.cfg.start_new_block()) .chain(Some(otherwise)) .collect(); - let values: Vec<_> = options.iter().map(|v| - v.val.to_const_int().expect("switching on integral") - ).collect(); (targets.clone(), TerminatorKind::SwitchInt { discr: Operand::Copy(place.clone()), switch_ty, - values: From::from(values), + values: options.clone().into(), targets, }) }; @@ -300,14 +298,49 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let tcx = self.hir.tcx(); let mut val = Operand::Copy(place.clone()); + let bytes = match value.val { + ConstVal::ByteStr(bytes) => Some(bytes.data), + ConstVal::Value(Value::ByVal(PrimVal::Ptr(p))) => { + let is_array_ptr = ty + .builtin_deref(true, ty::NoPreference) + .and_then(|t| t.ty.builtin_index()) + .map_or(false, |t| t == self.hir.tcx().types.u8); + if is_array_ptr { + self.hir + .tcx() + .interpret_interner + .borrow() + .get_alloc(p.alloc_id.0) + .map(|alloc| &alloc.bytes[..]) + } else { + None + } + }, + _ => None, + }; // If we're using b"..." as a pattern, we need to insert an // unsizing coercion, as the byte string has the type &[u8; N]. // // We want to do this even when the scrutinee is a reference to an // array, so we can call `<[u8]>::eq` rather than having to find an // `<[u8; N]>::eq`. - let (expect, val) = if let ConstVal::ByteStr(bytes) = value.val { - let array_ty = tcx.mk_array(tcx.types.u8, bytes.data.len() as u64); + let expect = if let Some(bytes) = bytes { + let tcx = self.hir.tcx(); + + // Unsize the place to &[u8], too, if necessary. + if let ty::TyRef(region, mt) = ty.sty { + if let ty::TyArray(_, _) = mt.ty.sty { + ty = tcx.mk_imm_ref(region, tcx.mk_slice(tcx.types.u8)); + let val_slice = self.temp(ty, test.span); + self.cfg.push_assign(block, source_info, &val_slice, + Rvalue::Cast(CastKind::Unsize, val, ty)); + val = Operand::Move(val_slice); + } + } + + assert!(ty.is_slice()); + + let array_ty = tcx.mk_array(tcx.types.u8, bytes.len() as u64); let array_ref = tcx.mk_imm_ref(tcx.types.re_static, array_ty); let array = self.literal_operand(test.span, array_ref, Literal::Value { value @@ -324,11 +357,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // Use PartialEq::eq for &str and &[u8] slices, instead of BinOp::Eq. let fail = self.cfg.start_new_block(); - let ty = expect.ty(&self.local_decls, tcx); - if let ty::TyRef(_, mt) = ty.sty { - assert!(ty.is_slice()); + let str_or_bytestr = ty + .builtin_deref(true, ty::NoPreference) + .and_then(|tam| match tam.ty.sty { + ty::TyStr => Some(tam.ty), + ty::TySlice(inner) if inner == self.hir.tcx().types.u8 => Some(tam.ty), + _ => None, + }); + if let Some(ty) = str_or_bytestr { let eq_def_id = self.hir.tcx().lang_items().eq_trait().unwrap(); - let ty = mt.ty; let (mty, method) = self.hir.trait_method(eq_def_id, "eq", ty, &[ty]); let bool_ty = self.hir.bool_ty(); diff --git a/src/librustc_mir/build/misc.rs b/src/librustc_mir/build/misc.rs index a3350cb1671d2..efb367201189b 100644 --- a/src/librustc_mir/build/misc.rs +++ b/src/librustc_mir/build/misc.rs @@ -16,6 +16,7 @@ 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; @@ -62,7 +63,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { ty::TyChar => { Literal::Value { value: self.hir.tcx().mk_const(ty::Const { - val: ConstVal::Char('\0'), + val: if self.hir.tcx().sess.opts.debugging_opts.miri { + ConstVal::Value(Value::ByVal(PrimVal::Bytes(0))) + } else { + ConstVal::Char('\0') + }, ty }) } @@ -83,7 +88,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { Literal::Value { value: self.hir.tcx().mk_const(ty::Const { - val: ConstVal::Integral(val), + val: if self.hir.tcx().sess.opts.debugging_opts.miri { + ConstVal::Value(Value::ByVal(PrimVal::Bytes(0))) + } else { + ConstVal::Integral(val) + }, ty }) } @@ -104,7 +113,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { Literal::Value { value: self.hir.tcx().mk_const(ty::Const { - val: ConstVal::Integral(val), + val: if self.hir.tcx().sess.opts.debugging_opts.miri { + ConstVal::Value(Value::ByVal(PrimVal::Bytes(0))) + } else { + ConstVal::Integral(val) + }, ty }) } diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index 00ab2e4599528..198e55358e7ee 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -10,7 +10,6 @@ use hair::*; use rustc_data_structures::indexed_vec::Idx; -use rustc_const_math::ConstInt; use hair::cx::Cx; use hair::cx::block; use hair::cx::to_ref::ToRef; @@ -18,6 +17,7 @@ use rustc::hir::def::{Def, CtorKind}; use rustc::middle::const_val::ConstVal; use rustc::ty::{self, AdtKind, VariantDef, Ty}; use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow, AutoBorrowMutability}; +use rustc::mir::interpret::{Value, PrimVal}; use rustc::ty::cast::CastKind as TyCastKind; use rustc::hir; use rustc::hir::def_id::LocalDefId; @@ -100,7 +100,7 @@ fn apply_adjustment<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, ExprKind::Deref { arg: expr.to_ref() } } Adjust::Deref(Some(deref)) => { - let call = deref.method_call(cx.tcx, expr.ty); + let call = deref.method_call(cx.tcx(), expr.ty); expr = Expr { temp_lifetime, @@ -314,7 +314,9 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, } } - hir::ExprLit(..) => ExprKind::Literal { literal: cx.const_eval_literal(expr) }, + hir::ExprLit(ref lit) => ExprKind::Literal { + literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, false), + }, hir::ExprBinary(op, ref lhs, ref rhs) => { if cx.tables().is_method_call(expr) { @@ -400,9 +402,10 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, if cx.tables().is_method_call(expr) { overloaded_operator(cx, expr, vec![arg.to_ref()]) } else { - // FIXME runtime-overflow - if let hir::ExprLit(_) = arg.node { - ExprKind::Literal { literal: cx.const_eval_literal(expr) } + if let hir::ExprLit(ref lit) = arg.node { + ExprKind::Literal { + literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, true), + } } else { ExprKind::Unary { op: UnOp::Neg, @@ -509,8 +512,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, let def_id = cx.tcx.hir.body_owner_def_id(count); let substs = Substs::identity_for_item(cx.tcx.global_tcx(), def_id); let count = match cx.tcx.at(c.span).const_eval(cx.param_env.and((def_id, substs))) { - Ok(&ty::Const { val: ConstVal::Integral(ConstInt::Usize(u)), .. }) => u, - Ok(other) => bug!("constant evaluation of repeat count yielded {:?}", other), + Ok(cv) => cv.val.unwrap_usize(cx.tcx), Err(s) => cx.fatal_const_eval_err(&s, c.span, "expression") }; @@ -634,8 +636,8 @@ fn method_callee<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, span: expr.span, kind: ExprKind::Literal { literal: Literal::Value { - value: cx.tcx.mk_const(ty::Const { - val: ConstVal::Function(def_id, substs), + value: cx.tcx().mk_const(ty::Const { + val: const_fn(cx.tcx, def_id, substs), ty }), }, @@ -675,6 +677,28 @@ fn convert_arm<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, arm: &'tcx hir::Arm) } } +fn const_fn<'a, 'gcx, 'tcx>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, + def_id: DefId, + substs: &'tcx Substs<'tcx>, +) -> ConstVal<'tcx> { + if tcx.sess.opts.debugging_opts.miri { + /* + let inst = ty::Instance::new(def_id, substs); + let ptr = tcx + .interpret_interner + .borrow_mut() + .create_fn_alloc(inst); + let ptr = MemoryPointer::new(AllocId(ptr), 0); + ConstVal::Value(Value::ByVal(PrimVal::Ptr(ptr))) + */ + // ZST function type + ConstVal::Value(Value::ByVal(PrimVal::Undef)) + } else { + ConstVal::Function(def_id, substs) + } +} + fn convert_path_expr<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, expr: &'tcx hir::Expr, def: Def) @@ -688,7 +712,7 @@ fn convert_path_expr<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, Def::VariantCtor(def_id, CtorKind::Fn) => ExprKind::Literal { literal: Literal::Value { value: cx.tcx.mk_const(ty::Const { - val: ConstVal::Function(def_id, substs), + val: const_fn(cx.tcx.global_tcx(), def_id, substs), ty: cx.tables().node_id_to_type(expr.hir_id) }), }, diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index 44c413561175e..7ebed0bbddbf5 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -17,7 +17,6 @@ use hair::*; use rustc::middle::const_val::{ConstEvalErr, ConstVal}; -use rustc_const_eval::ConstContext; use rustc_data_structures::indexed_vec::Idx; use rustc::hir::def_id::{DefId, LOCAL_CRATE}; use rustc::hir::map::blocks::FnLikeNode; @@ -32,6 +31,7 @@ use syntax::symbol::Symbol; use rustc::hir; use rustc_const_math::{ConstInt, ConstUsize}; use rustc_data_structures::sync::Lrc; +use rustc::mir::interpret::{Value, PrimVal}; #[derive(Clone)] pub struct Cx<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { @@ -119,7 +119,11 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { Ok(val) => { Literal::Value { value: self.tcx.mk_const(ty::Const { - val: ConstVal::Integral(ConstInt::Usize(val)), + val: if self.tcx.sess.opts.debugging_opts.miri { + ConstVal::Value(Value::ByVal(PrimVal::Bytes(value as u128))) + } else { + ConstVal::Integral(ConstInt::Usize(val)) + }, ty: self.tcx.types.usize }) } @@ -139,7 +143,11 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { pub fn true_literal(&mut self) -> Literal<'tcx> { Literal::Value { value: self.tcx.mk_const(ty::Const { - val: ConstVal::Bool(true), + val: if self.tcx.sess.opts.debugging_opts.miri { + ConstVal::Value(Value::ByVal(PrimVal::Bytes(1))) + } else { + ConstVal::Bool(true) + }, ty: self.tcx.types.bool }) } @@ -148,20 +156,161 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { pub fn false_literal(&mut self) -> Literal<'tcx> { Literal::Value { value: self.tcx.mk_const(ty::Const { - val: ConstVal::Bool(false), + val: if self.tcx.sess.opts.debugging_opts.miri { + ConstVal::Value(Value::ByVal(PrimVal::Bytes(0))) + } else { + ConstVal::Bool(false) + }, ty: self.tcx.types.bool }) } } - pub fn const_eval_literal(&mut self, e: &hir::Expr) -> Literal<'tcx> { + pub fn const_eval_literal( + &mut self, + lit: &'tcx ast::LitKind, + ty: Ty<'tcx>, + sp: Span, + neg: bool, + ) -> Literal<'tcx> { let tcx = self.tcx.global_tcx(); - let const_cx = ConstContext::new(tcx, - self.param_env.and(self.identity_substs), - self.tables()); - match const_cx.eval(tcx.hir.expect_expr(e.id)) { - Ok(value) => Literal::Value { value }, - Err(s) => self.fatal_const_eval_err(&s, e.span, "expression") + + 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 + tcx.sess.span_fatal(sp, "could not evaluate float literal (see issue #31407)"); + }) + }; + + 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; + + let lit = match *lit { + LitKind::Str(ref s, _) => Ok(Str(s.as_str())), + LitKind::ByteStr(ref data) => { + let data: &'tcx [u8] = data; + Ok(ByteStr(ByteArray { data })) + }, + LitKind::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::Float(n, fty) => { + let mut f = parse_float(&n.as_str(), fty); + if neg { + f = -f; + } + Ok(ConstVal::Float(f)) + } + LitKind::FloatUnsuffixed(n) => { + let fty = match ty.sty { + ty::TyFloat(fty) => fty, + _ => bug!() + }; + let mut f = parse_float(&n.as_str(), fty); + if neg { + f = -f; + } + Ok(ConstVal::Float(f)) + } + LitKind::Bool(b) => Ok(Bool(b)), + LitKind::Char(c) => Ok(Char(c)), + }; + + match lit { + Ok(value) => Literal::Value { value: self.tcx.mk_const(ty::Const { + val: value, + ty, + }) }, + Err(kind) => self.fatal_const_eval_err(&ConstEvalErr { + span: sp, + kind, + }, sp, "expression") } } @@ -203,7 +352,12 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { return (method_ty, Literal::Value { value: self.tcx.mk_const(ty::Const { - val: ConstVal::Function(item.def_id, substs), + 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) + }, ty: method_ty }), }); diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index bc555368f0f5f..ec8215fb64c38 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -13,14 +13,13 @@ 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}; +use super::{Place, EvalContext, StackPopCleanup, ValTy, HasMemory}; use rustc_const_math::ConstInt; use std::fmt; use std::error::Error; - pub fn mk_eval_cx<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>, @@ -45,7 +44,7 @@ pub fn eval_body<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>, param_env: ty::ParamEnv<'tcx>, -) -> EvalResult<'tcx, (Pointer, Ty<'tcx>)> { +) -> EvalResult<'tcx, (Value, Pointer, Ty<'tcx>)> { debug!("eval_body: {:?}, {:?}", instance, param_env); let limits = super::ResourceLimits::default(); let mut ecx = EvalContext::new(tcx, param_env, limits, CompileTimeEvaluator, ()); @@ -82,7 +81,13 @@ pub fn eval_body<'a, 'tcx>( while ecx.step()? {} } let alloc = tcx.interpret_interner.borrow().get_cached(cid).expect("global not cached"); - Ok((MemoryPointer::new(alloc, 0).into(), instance_ty)) + 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)) } pub fn eval_body_as_integer<'a, 'tcx>( @@ -90,11 +95,9 @@ pub fn eval_body_as_integer<'a, 'tcx>( param_env: ty::ParamEnv<'tcx>, instance: Instance<'tcx>, ) -> EvalResult<'tcx, ConstInt> { - let ptr_ty = eval_body(tcx, instance, param_env); - let (ptr, ty) = ptr_ty?; - let ecx = mk_eval_cx(tcx, instance, param_env)?; - let prim = match ecx.try_read_value(ptr, ecx.layout_of(ty)?.align, ty)? { - Some(Value::ByVal(prim)) => prim.to_bytes()?, + 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}; @@ -133,7 +136,7 @@ pub struct CompileTimeEvaluator; impl<'tcx> Into> for ConstEvalError { fn into(self) -> EvalError<'tcx> { - EvalErrorKind::MachineError(Box::new(self)).into() + EvalErrorKind::MachineError(self.to_string()).into() } } @@ -193,7 +196,6 @@ impl<'tcx> super::Machine<'tcx> for CompileTimeEvaluator { let mir = match ecx.load_mir(instance.def) { Ok(mir) => mir, Err(EvalError { kind: EvalErrorKind::NoMirFor(path), .. }) => { - // some simple things like `malloc` might get accepted in the future return Err( ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path)) .into(), @@ -302,6 +304,70 @@ impl<'tcx> super::Machine<'tcx> for CompileTimeEvaluator { } } +pub fn const_val_field<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + key: ty::ParamEnvAnd<'tcx, (ty::Instance<'tcx>, mir::Field, Value, Ty<'tcx>)>, +) -> ::rustc::middle::const_val::EvalResult<'tcx> { + trace!("const_val_field: {:#?}", key); + match const_val_field_inner(tcx, key) { + Ok((field, ty)) => Ok(tcx.mk_const(ty::Const { + val: ConstVal::Value(field), + ty, + })), + Err(err) => Err(ConstEvalErr { + span: tcx.def_span(key.value.0.def_id()), + kind: err.into(), + }), + } +} + +fn const_val_field_inner<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + key: ty::ParamEnvAnd<'tcx, (ty::Instance<'tcx>, mir::Field, Value, Ty<'tcx>)>, +) -> ::rustc::mir::interpret::EvalResult<'tcx, (Value, Ty<'tcx>)> { + trace!("const_val_field: {:#?}", key); + let (instance, field, value, ty) = key.value; + let mut ecx = mk_eval_cx(tcx, instance, key.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::ByRef(ptr, align) => { + let place = Place::from_primval_ptr(ptr, align); + let layout = ecx.layout_of(ty)?; + let (place, layout) = ecx.place_field(place, field, layout)?; + let (ptr, align) = place.to_ptr_align(); + (Value::ByRef(ptr, align), layout.ty) + } + }; + if let Value::ByRef(ptr, align) = field { + if let Some(val) = ecx.try_read_value(ptr, align, ty)? { + field = val; + } + } + Ok((field, ty)) +} + +pub fn const_discr<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + key: ty::ParamEnvAnd<'tcx, (ty::Instance<'tcx>, Value, Ty<'tcx>)>, +) -> EvalResult<'tcx, u128> { + trace!("const_discr: {:#?}", key); + let (instance, value, ty) = key.value; + let mut ecx = mk_eval_cx(tcx, instance, key.param_env).unwrap(); + let (ptr, align) = match value { + Value::ByValPair(..) | Value::ByVal(_) => { + let layout = ecx.layout_of(ty)?; + use super::MemoryKind; + let ptr = ecx.memory.allocate(layout.size.bytes(), layout.align, Some(MemoryKind::Stack))?; + let ptr: Pointer = ptr.into(); + ecx.write_value_to_ptr(value, ptr, layout.align, ty)?; + (ptr, layout.align) + }, + Value::ByRef(ptr, align) => (ptr, align), + }; + let place = Place::from_primval_ptr(ptr, align); + ecx.read_discriminant_value(place, ty) +} + pub fn const_eval_provider<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, key: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>, @@ -340,35 +406,48 @@ pub fn const_eval_provider<'a, 'tcx>( return Err(ConstEvalErr { span: body.value.span, kind: TypeckError }) } + + 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); - if tcx.sess.opts.debugging_opts.miri { - let instance = ty::Instance::new(def_id, substs); - 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) - }, - (_, Err(err)) => Err(err), - (Ok((miri_val, 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_val, layout.align); - check_ctfe_against_miri(&mut ecx, miri_place, miri_ty, ctfe.val); - Ok(ctfe) - } + 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, + })), + (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) } - } else { - old_result } } @@ -451,7 +530,7 @@ fn check_ctfe_against_miri<'a, 'tcx>( } }, TyArray(elem_ty, n) => { - let n = n.val.to_const_int().unwrap().to_u64().unwrap(); + 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) diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index e9e8ccd03b10f..f37fb3072b584 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -601,7 +601,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { Repeat(ref operand, _) => { let (elem_ty, length) = match dest_ty.sty { - ty::TyArray(elem_ty, n) => (elem_ty, n.val.to_const_int().unwrap().to_u64().unwrap()), + ty::TyArray(elem_ty, n) => (elem_ty, n.val.unwrap_u64()), _ => { bug!( "tried to assign array-repeat to non-array type {:?}", @@ -1386,7 +1386,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { let ptr = self.into_ptr(src)?; // u64 cast is from usize to u64, which is always good let valty = ValTy { - value: ptr.to_value_with_len(length.val.to_const_int().unwrap().to_u64().unwrap() ), + value: ptr.to_value_with_len(length.val.unwrap_u64() ), ty: dest_ty, }; self.write_value(valty, dest) diff --git a/src/librustc_mir/interpret/mod.rs b/src/librustc_mir/interpret/mod.rs index fee62c8a82e2f..a6ebdd45968fc 100644 --- a/src/librustc_mir/interpret/mod.rs +++ b/src/librustc_mir/interpret/mod.rs @@ -18,6 +18,6 @@ pub use self::place::{Place, PlaceExtra}; pub use self::memory::{Memory, MemoryKind, HasMemory}; -pub use self::const_eval::{eval_body_as_integer, eval_body, CompileTimeEvaluator, const_eval_provider}; +pub use self::const_eval::{eval_body_as_integer, eval_body, CompileTimeEvaluator, const_eval_provider, const_val_field, const_discr}; pub use self::machine::Machine; diff --git a/src/librustc_mir/interpret/operator.rs b/src/librustc_mir/interpret/operator.rs index 6ab1aec38b863..b20540b00ceae 100644 --- a/src/librustc_mir/interpret/operator.rs +++ b/src/librustc_mir/interpret/operator.rs @@ -248,10 +248,15 @@ pub fn unary_op<'tcx>( (Not, I64) => !(bytes as i64) as u128, (Not, I128) => !(bytes as i128) as u128, + (Neg, I8) if bytes == i8::min_value() as u128 => return err!(OverflowingMath), (Neg, I8) => -(bytes as i8) as u128, + (Neg, I16) if bytes == i16::min_value() as u128 => return err!(OverflowingMath), (Neg, I16) => -(bytes as i16) as u128, + (Neg, I32) if bytes == i32::min_value() as u128 => return err!(OverflowingMath), (Neg, I32) => -(bytes as i32) as u128, + (Neg, I64) if bytes == i64::min_value() as u128 => return err!(OverflowingMath), (Neg, I64) => -(bytes as i64) as u128, + (Neg, I128) if bytes == i128::min_value() as u128 => return err!(OverflowingMath), (Neg, I128) => -(bytes as i128) as u128, (Neg, F32) => (-bytes_to_f32(bytes)).bits, diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 701b7a07ac988..c5e4eeab86709 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -71,7 +71,7 @@ impl<'tcx> Place { pub(super) fn elem_ty_and_len(self, ty: Ty<'tcx>) -> (Ty<'tcx>, u64) { match ty.sty { - ty::TyArray(elem, n) => (elem, n.val.to_const_int().unwrap().to_u64().unwrap() as u64), + ty::TyArray(elem, n) => (elem, n.val.unwrap_u64() as u64), ty::TySlice(elem) => { match self { @@ -115,6 +115,29 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } } + pub fn read_field( + &self, + base: Value, + field: mir::Field, + base_ty: Ty<'tcx>, + ) -> EvalResult<'tcx, Option<(Value, Ty<'tcx>)>> { + let base_layout = self.layout_of(base_ty)?; + let field_index = field.index(); + let field = base_layout.field(self, field_index)?; + let offset = base_layout.fields.offset(field_index); + match base { + // the field covers the entire type + Value::ByValPair(..) | + Value::ByVal(_) if offset.bytes() == 0 && field.size == base_layout.size => Ok(Some((base, field.ty))), + // split fat pointers, 2 element tuples, ... + Value::ByValPair(a, b) if base_layout.fields.count() == 2 => { + let val = [a, b][field_index]; + Ok(Some((Value::ByVal(val), field.ty))) + }, + _ => Ok(None), + } + } + fn try_read_place_projection( &mut self, proj: &mir::PlaceProjection<'tcx>, @@ -126,23 +149,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { }; let base_ty = self.place_ty(&proj.base); match proj.elem { - Field(field, _) => { - let base_layout = self.layout_of(base_ty)?; - let field_index = field.index(); - let field = base_layout.field(&self, field_index)?; - let offset = base_layout.fields.offset(field_index); - match base { - // the field covers the entire type - Value::ByValPair(..) | - Value::ByVal(_) if offset.bytes() == 0 && field.size == base_layout.size => Ok(Some(base)), - // split fat pointers, 2 element tuples, ... - Value::ByValPair(a, b) if base_layout.fields.count() == 2 => { - let val = [a, b][field_index]; - Ok(Some(Value::ByVal(val))) - }, - _ => Ok(None), - } - }, + Field(field, _) => Ok(self.read_field(base, field, base_ty)?.map(|(f, _)| f)), // The NullablePointer cases should work fine, need to take care for normal enums Downcast(..) | Subslice { .. } | diff --git a/src/librustc_mir/interpret/terminator/mod.rs b/src/librustc_mir/interpret/terminator/mod.rs index 606bda51edb1f..c18cf6d9f96b9 100644 --- a/src/librustc_mir/interpret/terminator/mod.rs +++ b/src/librustc_mir/interpret/terminator/mod.rs @@ -45,8 +45,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { // Branch to the `otherwise` case by default, if no match is found. let mut target_block = targets[targets.len() - 1]; - for (index, const_int) in values.iter().enumerate() { - let prim = PrimVal::Bytes(const_int.to_u128_unchecked()); + for (index, &const_int) in values.iter().enumerate() { + let prim = PrimVal::Bytes(const_int); if discr_prim.to_bytes()? == prim.to_bytes()? { target_block = targets[index]; break; diff --git a/src/librustc_mir/monomorphize/item.rs b/src/librustc_mir/monomorphize/item.rs index 38b8ffc6b9c80..1f7f1237ba789 100644 --- a/src/librustc_mir/monomorphize/item.rs +++ b/src/librustc_mir/monomorphize/item.rs @@ -314,7 +314,7 @@ impl<'a, 'tcx> DefPathBasedNames<'a, 'tcx> { output.push('['); self.push_type_name(inner_type, output); write!(output, "; {}", - len.val.to_const_int().unwrap().to_u64().unwrap()).unwrap(); + len.val.unwrap_u64()).unwrap(); output.push(']'); }, ty::TySlice(inner_type) => { diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index e6ebdd3d6c167..11dded1d3f075 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -17,6 +17,7 @@ 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::mir::interpret::{Value, PrimVal}; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; @@ -303,7 +304,7 @@ fn build_clone_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, match self_ty.sty { _ if is_copy => builder.copy_shim(), ty::TyArray(ty, len) => { - let len = len.val.to_const_int().unwrap().to_u64().unwrap(); + let len = len.val.unwrap_u64(); builder.array_shim(dest, src, ty, len) } ty::TyClosure(def_id, substs) => { @@ -443,7 +444,12 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { ty: func_ty, literal: Literal::Value { value: tcx.mk_const(ty::Const { - val: ConstVal::Function(self.def_id, substs), + val: if tcx.sess.opts.debugging_opts.miri { + // ZST function type + ConstVal::Value(Value::ByVal(PrimVal::Undef)) + } else { + ConstVal::Function(self.def_id, substs) + }, ty: func_ty }), }, @@ -501,13 +507,20 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { } fn make_usize(&self, value: u64) -> Box> { - let value = ConstUsize::new(value, self.tcx.sess.target.usize_ty).unwrap(); box Constant { span: self.span, ty: self.tcx.types.usize, literal: Literal::Value { value: self.tcx.mk_const(ty::Const { - val: ConstVal::Integral(ConstInt::Usize(value)), + val: if self.tcx.sess.opts.debugging_opts.miri { + ConstVal::Value(Value::ByVal(PrimVal::Bytes(value.into()))) + } else { + let value = ConstUsize::new( + value, + self.tcx.sess.target.usize_ty, + ).unwrap(); + ConstVal::Integral(ConstInt::Usize(value)) + }, ty: self.tcx.types.usize, }) } @@ -739,8 +752,12 @@ fn build_call_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty, literal: Literal::Value { value: tcx.mk_const(ty::Const { - val: ConstVal::Function(def_id, - Substs::identity_for_item(tcx, def_id)), + 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)) + }, ty }), }, diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index 04ebaa031fe5a..ec3edb1e0680e 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -80,6 +80,7 @@ use transform::simplify; use transform::no_landing_pads::no_landing_pads; use dataflow::{do_dataflow, DebugFormatted, state_for_location}; use dataflow::{MaybeStorageLive, HaveBeenBorrowedLocals}; +use rustc::mir::interpret::{Value, PrimVal}; pub struct StateTransform; @@ -181,7 +182,11 @@ impl<'a, 'tcx> TransformVisitor<'a, 'tcx> { ty: self.tcx.types.u32, literal: Literal::Value { value: self.tcx.mk_const(ty::Const { - val: ConstVal::Integral(ConstInt::U32(state_disc)), + 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)) + }, ty: self.tcx.types.u32 }), }, @@ -534,7 +539,7 @@ fn insert_switch<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let switch = TerminatorKind::SwitchInt { discr: Operand::Copy(transform.make_field(transform.state_field, tcx.types.u32)), switch_ty: tcx.types.u32, - values: Cow::from(cases.iter().map(|&(i, _)| ConstInt::U32(i)).collect::>()), + values: Cow::from(cases.iter().map(|&(i, _)| i.into()).collect::>()), targets: cases.iter().map(|&(_, d)| d).chain(once(default_block)).collect(), }; diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs index 64c702b99cdb3..b8a0e0f89073d 100644 --- a/src/librustc_mir/transform/inline.rs +++ b/src/librustc_mir/transform/inline.rs @@ -207,6 +207,13 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { return false; } + // Do not inline {u,i}128 lang items, trans const eval depends + // on detecting calls to these lang items and intercepting them + if tcx.is_binop_lang_item(callsite.callee).is_some() { + debug!(" not inlining 128bit integer lang item"); + return false; + } + let trans_fn_attrs = tcx.trans_fn_attrs(callsite.callee); let hinted = match trans_fn_attrs.inline { diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 8f5831270d6e5..88618122e4f11 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -690,7 +690,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { _ => false } } else if let ty::TyArray(_, len) = ty.sty { - len.val.to_const_int().unwrap().to_u64().unwrap() == 0 && + len.val.unwrap_u64() == 0 && self.mode == Mode::Fn } else { false diff --git a/src/librustc_mir/transform/simplify_branches.rs b/src/librustc_mir/transform/simplify_branches.rs index 41089f567bd71..ca7f573b58a47 100644 --- a/src/librustc_mir/transform/simplify_branches.rs +++ b/src/librustc_mir/transform/simplify_branches.rs @@ -40,10 +40,10 @@ impl MirPass for SimplifyBranches { TerminatorKind::SwitchInt { discr: Operand::Constant(box Constant { literal: Literal::Value { ref value }, .. }), ref values, ref targets, .. } => { - if let Some(ref constint) = value.val.to_const_int() { + if let Some(constint) = value.val.to_u128() { let (otherwise, targets) = targets.split_last().unwrap(); let mut ret = TerminatorKind::Goto { target: *otherwise }; - for (v, t) in values.iter().zip(targets.iter()) { + for (&v, t) in values.iter().zip(targets.iter()) { if v == constint { ret = TerminatorKind::Goto { target: *t }; break; diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs index e2feb0ed39054..77ef2c20117f1 100644 --- a/src/librustc_mir/util/elaborate_drops.rs +++ b/src/librustc_mir/util/elaborate_drops.rs @@ -11,13 +11,14 @@ use std::fmt; use rustc::hir; use rustc::mir::*; -use rustc::middle::const_val::{ConstInt, ConstVal}; +use rustc::middle::const_val::ConstVal; use rustc::middle::lang_items; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::subst::{Kind, Substs}; use rustc::ty::util::IntTypeExt; use rustc_data_structures::indexed_vec::Idx; use util::patch::MirPatch; +use rustc::mir::interpret::{Value, PrimVal}; use std::{iter, u32}; @@ -425,7 +426,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> variant_path, &adt.variants[variant_index], substs); - values.push(discr); + values.push(discr.to_u128().unwrap()); if let Unwind::To(unwind) = unwind { // We can't use the half-ladder from the original // drop ladder, because this breaks the @@ -480,7 +481,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> fn adt_switch_block(&mut self, adt: &'tcx ty::AdtDef, blocks: Vec, - values: &[ConstInt], + values: &[u128], succ: BasicBlock, unwind: Unwind) -> BasicBlock { @@ -803,7 +804,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> self.complete_drop(Some(DropFlagMode::Deep), succ, unwind) } ty::TyArray(ety, size) => self.open_drop_for_array( - ety, size.val.to_const_int().and_then(|v| v.to_u64())), + ety, size.val.to_u128().map(|i| i as u64)), ty::TySlice(ety) => self.open_drop_for_array(ety, None), _ => bug!("open drop from non-ADT `{:?}`", ty) @@ -949,7 +950,11 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> ty: self.tcx().types.usize, literal: Literal::Value { value: self.tcx().mk_const(ty::Const { - val: ConstVal::Integral(self.tcx().const_usize(val)), + 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)) + }, ty: self.tcx().types.usize }) } diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs index b93b759fdf869..8153c3c8493ff 100644 --- a/src/librustc_passes/consts.rs +++ b/src/librustc_passes/consts.rs @@ -129,6 +129,9 @@ impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> { } 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(_) => {} @@ -220,23 +223,24 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> { self.check_const_eval(lit); } PatKind::Range(ref start, ref end, RangeEnd::Excluded) => { - match self.const_cx().compare_lit_exprs(p.span, start, end) { - Ok(Ordering::Less) => {} - Ok(Ordering::Equal) | - Ok(Ordering::Greater) => { + 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(p.span, start, end) { - Ok(Ordering::Less) | - Ok(Ordering::Equal) => {} - Ok(Ordering::Greater) => { + 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, @@ -252,6 +256,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> { } err.emit(); } + Ok(None) => bug!("ranges must be char or int"), Err(ErrorReported) => {} } } @@ -308,7 +313,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> { self.promotable = false; } - if self.in_fn && self.promotable { + if self.in_fn && self.promotable && !self.tcx.sess.opts.debugging_opts.miri { match self.const_cx().eval(ex) { Ok(_) => {} Err(ConstEvalErr { kind: UnimplementedConstVal(_), .. }) | diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 3708f6f6ec4fc..314b8c59df5ab 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -195,7 +195,7 @@ pub fn unsized_info<'cx, 'tcx>(cx: &CodegenCx<'cx, 'tcx>, let (source, target) = cx.tcx.struct_lockstep_tails(source, target); match (&source.sty, &target.sty) { (&ty::TyArray(_, len), &ty::TySlice(_)) => { - C_usize(cx, len.val.to_const_int().unwrap().to_u64().unwrap()) + C_usize(cx, len.val.unwrap_u64()) } (&ty::TyDynamic(..), &ty::TyDynamic(..)) => { // For now, upcasts are limited to changes in marker diff --git a/src/librustc_trans/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs index 2c430d03c968e..0fe425fb7ea1e 100644 --- a/src/librustc_trans/debuginfo/metadata.rs +++ b/src/librustc_trans/debuginfo/metadata.rs @@ -276,7 +276,7 @@ fn fixed_vec_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, let upper_bound = match array_or_slice_type.sty { ty::TyArray(_, len) => { - len.val.to_const_int().unwrap().to_u64().unwrap() as c_longlong + len.val.unwrap_u64() as c_longlong } _ => -1 }; diff --git a/src/librustc_trans/debuginfo/type_names.rs b/src/librustc_trans/debuginfo/type_names.rs index 6490d109f2936..a88eb9ae35471 100644 --- a/src/librustc_trans/debuginfo/type_names.rs +++ b/src/librustc_trans/debuginfo/type_names.rs @@ -97,7 +97,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, ty::TyArray(inner_type, len) => { output.push('['); push_debuginfo_type_name(cx, inner_type, true, output); - output.push_str(&format!("; {}", len.val.to_const_int().unwrap().to_u64().unwrap())); + output.push_str(&format!("; {}", len.val.unwrap_u64())); output.push(']'); }, ty::TySlice(inner_type) => { diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs index f683703ce6d53..c88e39d7824c7 100644 --- a/src/librustc_trans/mir/analyze.rs +++ b/src/librustc_trans/mir/analyze.rs @@ -17,6 +17,7 @@ use rustc::middle::const_val::ConstVal; use rustc::mir::{self, Location, TerminatorKind, Literal}; use rustc::mir::visit::{Visitor, PlaceContext}; use rustc::mir::traversal; +use rustc::mir::interpret::{Value, PrimVal}; use rustc::ty; use rustc::ty::layout::LayoutOf; use type_of::LayoutLlvmExt; @@ -109,15 +110,26 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> { block: mir::BasicBlock, kind: &mir::TerminatorKind<'tcx>, location: Location) { - match *kind { + let check = match *kind { mir::TerminatorKind::Call { func: mir::Operand::Constant(box mir::Constant { literal: Literal::Value { - value: &ty::Const { val: ConstVal::Function(def_id, _), .. }, .. + value: &ty::Const { val, ty }, .. }, .. }), ref args, .. - } if Some(def_id) == self.fx.cx.tcx.lang_items().box_free_fn() => { + } => 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, + }, + _ => None, + } + _ => None, + }; + if let Some((def_id, args)) = check { + if Some(def_id) == self.cx.ccx.tcx().lang_items().box_free_fn() { // box_free(x) shares with `drop x` the property that it // is not guaranteed to be statically dominated by the // definition of x, so x must always be in an alloca. @@ -125,7 +137,6 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> { self.visit_place(place, PlaceContext::Drop, location); } } - _ => {} } self.super_terminator_kind(block, kind, location); diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index bb2a7840faee7..239300c1ecf4e 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -10,7 +10,7 @@ use llvm::{self, ValueRef, BasicBlockRef}; use rustc::middle::lang_items; -use rustc::middle::const_val::{ConstEvalErr, ConstInt, ErrKind}; +use rustc::middle::const_val::{ConstEvalErr, ErrKind}; use rustc::ty::{self, TypeFoldable}; use rustc::ty::layout::{self, LayoutOf}; use rustc::traits; @@ -196,17 +196,18 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { if switch_ty == bx.tcx().types.bool { let lltrue = llblock(self, targets[0]); let llfalse = llblock(self, targets[1]); - if let [ConstInt::U8(0)] = values[..] { + if let [0] = values[..] { bx.cond_br(discr.immediate(), llfalse, lltrue); } else { + assert_eq!(&values[..], &[1]); bx.cond_br(discr.immediate(), lltrue, llfalse); } } else { let (otherwise, targets) = targets.split_last().unwrap(); let switch = bx.switch(discr.immediate(), llblock(self, *otherwise), values.len()); - for (value, target) in values.iter().zip(targets) { - let val = Const::from_constint(bx.cx, value); + for (&value, target) in values.iter().zip(targets) { + let val = Const::from_bytes(bx.cx, value, switch_ty); let llbb = llblock(self, *target); bx.add_case(switch, val.llval, llbb) } diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index c853230b15ab7..5bd5f19a57c63 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -16,6 +16,7 @@ use rustc::hir::def_id::DefId; use rustc::infer::TransNormalize; use rustc::traits; use rustc::mir; +use rustc::mir::interpret::{Value as MiriValue, PrimVal}; use rustc::mir::tcx::PlaceTy; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc::ty::layout::{self, LayoutOf, Size}; @@ -38,6 +39,7 @@ use value::Value; use syntax_pos::Span; use syntax::ast; +use syntax::symbol::Symbol; use std::fmt; use std::ptr; @@ -81,12 +83,46 @@ impl<'a, 'tcx> Const<'tcx> { 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) | + ty::TyUint(ast::UintTy::U128) => C_uint_big(Type::i128(ccx), b), + ty::TyInt(i) => C_int(Type::int_from_ty(ccx, i), b as i128 as i64), + ty::TyUint(u) => C_uint(Type::uint_from_ty(ccx, u), b as u64), + ty::TyBool => { + assert!(b <= 1); + C_bool(ccx, b == 1) + }, + ty::TyChar => { + assert_eq!(b as u32 as u128, b); + let c = b as u32; + assert!(::std::char::from_u32(c).is_some()); + C_uint(Type::char(ccx), c as u64) + }, + ty::TyFloat(fty) => { + let llty = ccx.layout_of(ty).llvm_type(ccx); + let bits = match fty { + ast::FloatTy::F32 => C_u32(ccx, b as u32), + ast::FloatTy::F64 => C_u64(ccx, b as u64), + }; + consts::bitcast(bits, llty) + }, + ty::TyAdt(adt, _) if adt.is_enum() => { + use rustc::ty::util::IntTypeExt; + Const::from_bytes(ccx, b, adt.repr.discr_type().to_ty(ccx.tcx())).llval + }, + _ => bug!("from_bytes({}, {})", b, ty), + }; + Const { llval, ty } + } + /// Translate ConstVal into a LLVM constant value. pub fn from_constval(cx: &CodegenCx<'a, 'tcx>, cv: &ConstVal, ty: Ty<'tcx>) -> Const<'tcx> { let llty = cx.layout_of(ty).llvm_type(cx); + trace!("from_constval: {:#?}: {}", cv, ty); let val = match *cv { ConstVal::Float(v) => { let bits = match v.ty { @@ -108,7 +144,41 @@ impl<'a, 'tcx> Const<'tcx> { ConstVal::Unevaluated(..) => { bug!("MIR must not use `{:?}` (aggregates are expanded to MIR rvalues)", cv) } - ConstVal::Value(_) => unimplemented!(), + ConstVal::Value(MiriValue::ByRef(..)) => unimplemented!("{:#?}:{}", cv, ty), + ConstVal::Value(MiriValue::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(len))) => { + match ty.sty { + ty::TyRef(_, ref tam) => match tam.ty.sty { + ty::TyStr => {}, + _ => unimplemented!("non-str fat pointer: {:?}: {:?}", ptr, ty), + }, + _ => unimplemented!("non-str fat pointer: {:?}: {:?}", ptr, ty), + } + let alloc = ccx + .tcx() + .interpret_interner + .borrow() + .get_alloc(ptr.alloc_id.0) + .expect("miri alloc not found"); + assert_eq!(len as usize as u128, len); + let slice = &alloc.bytes[(ptr.offset as usize)..][..(len as usize)]; + let s = ::std::str::from_utf8(slice) + .expect("non utf8 str from miri"); + C_str_slice(ccx, Symbol::intern(s).as_str()) + }, + ConstVal::Value(MiriValue::ByValPair(..)) => unimplemented!(), + ConstVal::Value(MiriValue::ByVal(PrimVal::Bytes(b))) => + return Const::from_bytes(ccx, b, ty), + ConstVal::Value(MiriValue::ByVal(PrimVal::Undef)) => C_undef(llty), + ConstVal::Value(MiriValue::ByVal(PrimVal::Ptr(ptr))) => { + let alloc = ccx + .tcx() + .interpret_interner + .borrow() + .get_alloc(ptr.alloc_id.0) + .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") + } }; assert!(!ty.has_erasable_regions()); @@ -239,7 +309,7 @@ impl<'tcx> ConstPlace<'tcx> { pub fn len<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> ValueRef { match self.ty.sty { ty::TyArray(_, n) => { - C_usize(cx, n.val.to_const_int().unwrap().to_u64().unwrap()) + C_usize(cx, n.val.unwrap_u64()) } ty::TySlice(_) | ty::TyStr => { assert!(self.llextra != ptr::null_mut()); @@ -316,7 +386,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { let tcx = self.cx.tcx; let mut bb = mir::START_BLOCK; - // Make sure to evaluate all statemenets to + // Make sure to evaluate all statements to // report as many errors as we possibly can. let mut failure = Ok(()); @@ -392,6 +462,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { _ => span_bug!(span, "calling {:?} (of type {}) in constant", func, fn_ty) }; + trace!("trans const fn call {:?}, {:?}, {:#?}", func, fn_ty, args); let mut arg_vals = IndexVec::with_capacity(args.len()); for arg in args { @@ -419,7 +490,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { } _ => span_bug!(span, "{:?} in constant", terminator.kind) } - } else if let Some((op, is_checked)) = self.is_binop_lang_item(def_id) { + } else if let Some((op, is_checked)) = tcx.is_binop_lang_item(def_id) { (||{ assert_eq!(arg_vals.len(), 2); let rhs = arg_vals.pop().unwrap()?; @@ -470,37 +541,6 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { } } - fn is_binop_lang_item(&mut self, def_id: DefId) -> Option<(mir::BinOp, bool)> { - let tcx = self.cx.tcx; - let items = tcx.lang_items(); - let def_id = Some(def_id); - if items.i128_add_fn() == def_id { Some((mir::BinOp::Add, false)) } - else if items.u128_add_fn() == def_id { Some((mir::BinOp::Add, false)) } - else if items.i128_sub_fn() == def_id { Some((mir::BinOp::Sub, false)) } - else if items.u128_sub_fn() == def_id { Some((mir::BinOp::Sub, false)) } - else if items.i128_mul_fn() == def_id { Some((mir::BinOp::Mul, false)) } - else if items.u128_mul_fn() == def_id { Some((mir::BinOp::Mul, false)) } - else if items.i128_div_fn() == def_id { Some((mir::BinOp::Div, false)) } - else if items.u128_div_fn() == def_id { Some((mir::BinOp::Div, false)) } - else if items.i128_rem_fn() == def_id { Some((mir::BinOp::Rem, false)) } - else if items.u128_rem_fn() == def_id { Some((mir::BinOp::Rem, false)) } - else if items.i128_shl_fn() == def_id { Some((mir::BinOp::Shl, false)) } - else if items.u128_shl_fn() == def_id { Some((mir::BinOp::Shl, false)) } - else if items.i128_shr_fn() == def_id { Some((mir::BinOp::Shr, false)) } - else if items.u128_shr_fn() == def_id { Some((mir::BinOp::Shr, false)) } - else if items.i128_addo_fn() == def_id { Some((mir::BinOp::Add, true)) } - else if items.u128_addo_fn() == def_id { Some((mir::BinOp::Add, true)) } - else if items.i128_subo_fn() == def_id { Some((mir::BinOp::Sub, true)) } - else if items.u128_subo_fn() == def_id { Some((mir::BinOp::Sub, true)) } - else if items.i128_mulo_fn() == def_id { Some((mir::BinOp::Mul, true)) } - else if items.u128_mulo_fn() == def_id { Some((mir::BinOp::Mul, true)) } - else if items.i128_shlo_fn() == def_id { Some((mir::BinOp::Shl, true)) } - else if items.u128_shlo_fn() == def_id { Some((mir::BinOp::Shl, true)) } - else if items.i128_shro_fn() == def_id { Some((mir::BinOp::Shr, true)) } - else if items.u128_shro_fn() == def_id { Some((mir::BinOp::Shr, true)) } - else { None } - } - fn store(&mut self, dest: &mir::Place<'tcx>, value: Result, ConstEvalErr<'tcx>>, diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index 34ac44cec025a..b0cb7de824eb5 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -497,7 +497,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { if let mir::Place::Local(index) = *place { if let LocalRef::Operand(Some(op)) = self.locals[index] { if let ty::TyArray(_, n) = op.layout.ty.sty { - let n = n.val.to_const_int().unwrap().to_u64().unwrap(); + let n = n.val.unwrap_u64(); return common::C_usize(bx.cx, n); } } diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index a261c12bcdd94..eb02c05fd3957 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -413,7 +413,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let expected_ty = self.structurally_resolved_type(pat.span, expected); let (inner_ty, slice_ty) = match expected_ty.sty { ty::TyArray(inner_ty, size) => { - let size = size.val.to_const_int().unwrap().to_u64().unwrap(); + let size = size.val.unwrap_u64(); let min_len = before.len() as u64 + after.len() as u64; if slice.is_none() { if min_len != size { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 0f59973eab251..26b9649331025 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -4029,9 +4029,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }; if let Ok(count) = count { - let zero_or_one = count.val.to_const_int().and_then(|count| { - count.to_u64().map(|count| count <= 1) - }).unwrap_or(false); + let zero_or_one = count.val.to_u128().map_or(false, |count| count <= 1); if !zero_or_one { // For [foo, ..n] where n > 1, `foo` must have // Copy type: diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index 12791107ebb41..47a229cbd3b5b 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -447,10 +447,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { hir::BiBitOr => ("bitor", lang.bitor_trait()), hir::BiShl => ("shl", lang.shl_trait()), hir::BiShr => ("shr", lang.shr_trait()), - hir::BiLt => ("lt", lang.ord_trait()), - hir::BiLe => ("le", lang.ord_trait()), - hir::BiGe => ("ge", lang.ord_trait()), - hir::BiGt => ("gt", lang.ord_trait()), + hir::BiLt => ("lt", lang.partial_ord_trait()), + hir::BiLe => ("le", lang.partial_ord_trait()), + hir::BiGe => ("ge", lang.partial_ord_trait()), + hir::BiGt => ("gt", lang.partial_ord_trait()), hir::BiEq => ("eq", lang.eq_trait()), hir::BiNe => ("ne", lang.eq_trait()), hir::BiAnd | hir::BiOr => { diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 1a7d8bb56780e..5ed35e8203cec 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -37,8 +37,8 @@ use rustc::ty::{ToPredicate, ReprOptions}; use rustc::ty::{self, AdtKind, ToPolyTraitRef, Ty, TyCtxt}; use rustc::ty::maps::Providers; use rustc::ty::util::IntTypeExt; -use rustc::util::nodemap::FxHashSet; -use util::nodemap::FxHashMap; +use rustc::util::nodemap::{FxHashSet, FxHashMap}; +use rustc::mir::interpret::{Value, PrimVal}; use rustc_const_math::ConstInt; @@ -534,6 +534,18 @@ fn convert_enum_variant_types<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, match result { Ok(&ty::Const { val: ConstVal::Integral(x), .. }) => Some(x), + Ok(&ty::Const { + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))), + .. + }) => { + use syntax::attr::IntType; + Some(match repr_type { + IntType::SignedInt(int_type) => ConstInt::new_signed( + b as i128, int_type, tcx.sess.target.isize_ty).unwrap(), + IntType::UnsignedInt(uint_type) => ConstInt::new_unsigned( + b, uint_type, tcx.sess.target.usize_ty).unwrap(), + }) + } _ => None } } else if let Some(discr) = repr_type.disr_incr(tcx, prev_discr) { diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index d9bd96b0d769f..40385cabf5661 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -86,6 +86,7 @@ This API is completely unstable and subject to change. #![feature(refcell_replace_swap)] #![feature(rustc_diagnostic_macros)] #![feature(slice_patterns)] +#![feature(i128_type)] #[macro_use] extern crate log; #[macro_use] extern crate syntax; diff --git a/src/test/mir-opt/end_region_2.rs b/src/test/mir-opt/end_region_2.rs index d6084d5a6da92..3fb5621c8ae3b 100644 --- a/src/test/mir-opt/end_region_2.rs +++ b/src/test/mir-opt/end_region_2.rs @@ -49,7 +49,7 @@ fn main() { // _3 = &'23_1rs _2; // StorageLive(_5); // _5 = _2; -// switchInt(move _5) -> [0u8: bb5, otherwise: bb4]; +// switchInt(move _5) -> [false: bb5, otherwise: bb4]; // } // bb3: { // ... diff --git a/src/test/mir-opt/end_region_3.rs b/src/test/mir-opt/end_region_3.rs index 46548f1cce978..070bde8e3c3e6 100644 --- a/src/test/mir-opt/end_region_3.rs +++ b/src/test/mir-opt/end_region_3.rs @@ -51,7 +51,7 @@ fn main() { // _3 = &'26_1rs _1; // StorageLive(_5); // _5 = _1; -// switchInt(move _5) -> [0u8: bb5, otherwise: bb4]; +// switchInt(move _5) -> [false: bb5, otherwise: bb4]; // } // bb3: { // ... diff --git a/src/test/mir-opt/end_region_9.rs b/src/test/mir-opt/end_region_9.rs index 0f1d714cc6fd2..6d9a27eeeb456 100644 --- a/src/test/mir-opt/end_region_9.rs +++ b/src/test/mir-opt/end_region_9.rs @@ -72,7 +72,7 @@ fn main() { // bb4: { // StorageLive(_7); // _7 = _1; -// switchInt(move _7) -> [0u8: bb6, otherwise: bb5]; +// switchInt(move _7) -> [false: bb6, otherwise: bb5]; // } // bb5: { // _0 = (); diff --git a/src/test/mir-opt/end_region_cyclic.rs b/src/test/mir-opt/end_region_cyclic.rs index 2a82e2675b67d..83425a72f4598 100644 --- a/src/test/mir-opt/end_region_cyclic.rs +++ b/src/test/mir-opt/end_region_cyclic.rs @@ -103,7 +103,7 @@ fn query() -> bool { true } // _11 = const query() -> [return: bb6, unwind: bb3]; // } // bb6: { -// switchInt(move _11) -> [0u8: bb8, otherwise: bb7]; +// switchInt(move _11) -> [false: bb8, otherwise: bb7]; // } // bb7: { // _0 = (); diff --git a/src/test/mir-opt/issue-38669.rs b/src/test/mir-opt/issue-38669.rs index 3151c0643079c..a9eea26f46643 100644 --- a/src/test/mir-opt/issue-38669.rs +++ b/src/test/mir-opt/issue-38669.rs @@ -36,7 +36,7 @@ fn main() { // bb3: { // StorageLive(_4); // _4 = _1; -// switchInt(move _4) -> [0u8: bb5, otherwise: bb4]; +// switchInt(move _4) -> [false: bb5, otherwise: bb4]; // } // bb4: { // _0 = (); diff --git a/src/test/mir-opt/match_false_edges.rs b/src/test/mir-opt/match_false_edges.rs index ba1b54d59f69a..596bb4e115dfc 100644 --- a/src/test/mir-opt/match_false_edges.rs +++ b/src/test/mir-opt/match_false_edges.rs @@ -93,7 +93,7 @@ fn main() { // _7 = const guard() -> [return: bb10, unwind: bb1]; // } // bb10: { // end of guard -// switchInt(move _7) -> [0u8: bb11, otherwise: bb2]; +// switchInt(move _7) -> [false: bb11, otherwise: bb2]; // } // bb11: { // to pre_binding2 // falseEdges -> [real: bb5, imaginary: bb5]; @@ -157,7 +157,7 @@ fn main() { // _7 = const guard() -> [return: bb10, unwind: bb1]; // } // bb10: { // end of guard -// switchInt(move _7) -> [0u8: bb11, otherwise: bb2]; +// switchInt(move _7) -> [false: bb11, otherwise: bb2]; // } // bb11: { // to pre_binding2 // falseEdges -> [real: bb6, imaginary: bb5]; @@ -219,7 +219,7 @@ fn main() { // _9 = const guard() -> [return: bb10, unwind: bb1]; // } // bb10: { //end of guard -// switchInt(move _9) -> [0u8: bb11, otherwise: bb2]; +// switchInt(move _9) -> [false: bb11, otherwise: bb2]; // } // bb11: { // to pre_binding2 // falseEdges -> [real: bb5, imaginary: bb5]; @@ -239,8 +239,8 @@ fn main() { // _11 = const guard2(move _12) -> [return: bb14, unwind: bb1]; // } // bb14: { // end of guard2 -// StorageDead(_12); -// switchInt(move _11) -> [0u8: bb15, otherwise: bb3]; +// StorageDead(_11); +// switchInt(move _11) -> [false: bb15, otherwise: bb3]; // } // bb15: { // to pre_binding4 // falseEdges -> [real: bb7, imaginary: bb7]; diff --git a/src/test/mir-opt/nll/region-liveness-basic.rs b/src/test/mir-opt/nll/region-liveness-basic.rs index e9834305550c3..19d733d4f6b6a 100644 --- a/src/test/mir-opt/nll/region-liveness-basic.rs +++ b/src/test/mir-opt/nll/region-liveness-basic.rs @@ -41,7 +41,7 @@ fn main() { // | Live variables on entry to bb2[0]: [_1, _3] // _2 = &'_#2r _1[_3]; // | Live variables on entry to bb2[1]: [_2] -// switchInt(const true) -> [0u8: bb4, otherwise: bb3]; +// switchInt(const true) -> [false: bb4, otherwise: bb3]; // } // END rustc.main.nll.0.mir // START rustc.main.nll.0.mir diff --git a/src/test/mir-opt/simplify_if.rs b/src/test/mir-opt/simplify_if.rs index 35786643648eb..52d5892e6560b 100644 --- a/src/test/mir-opt/simplify_if.rs +++ b/src/test/mir-opt/simplify_if.rs @@ -17,7 +17,7 @@ fn main() { // END RUST SOURCE // START rustc.main.SimplifyBranches-initial.before.mir // bb0: { -// switchInt(const false) -> [0u8: bb3, otherwise: bb2]; +// switchInt(const false) -> [false: bb3, otherwise: bb2]; // } // END rustc.main.SimplifyBranches-initial.before.mir // START rustc.main.SimplifyBranches-initial.after.mir diff --git a/src/test/ui/const-eval-overflow-2.rs b/src/test/ui/const-eval-overflow-2.rs index 6b7f631ff4c8d..885edb55ed86c 100644 --- a/src/test/ui/const-eval-overflow-2.rs +++ b/src/test/ui/const-eval-overflow-2.rs @@ -19,8 +19,7 @@ use std::{u8, u16, u32, u64, usize}; const NEG_128: i8 = -128; const NEG_NEG_128: i8 = -NEG_128; -//~^ ERROR constant evaluation error -//~| attempt to negate with overflow +//~^ ERROR E0080 fn main() { match -128i8 { diff --git a/src/test/ui/const-eval-overflow-4.rs b/src/test/ui/const-eval-overflow-4.rs index 4423fdec33a85..a1b90f623cdae 100644 --- a/src/test/ui/const-eval-overflow-4.rs +++ b/src/test/ui/const-eval-overflow-4.rs @@ -21,8 +21,7 @@ use std::{u8, u16, u32, u64, usize}; const A_I8_T : [u32; (i8::MAX as i8 + 1i8) as usize] - //~^ ERROR constant evaluation error - //~| WARNING constant evaluation error + //~^ ERROR E0080 = [0; (i8::MAX as usize) + 1]; fn main() { diff --git a/src/test/ui/const-eval/issue-43197.rs b/src/test/ui/const-eval/issue-43197.rs index 85ab2a0052164..86c5e873df866 100644 --- a/src/test/ui/const-eval/issue-43197.rs +++ b/src/test/ui/const-eval/issue-43197.rs @@ -16,8 +16,6 @@ const fn foo(x: u32) -> u32 { fn main() { const X: u32 = 0-1; //~ ERROR constant evaluation error - //~^ WARN constant evaluation error const Y: u32 = foo(0-1); //~ ERROR constant evaluation error - //~^ WARN constant evaluation error println!("{} {}", X, Y); } diff --git a/src/test/ui/const-expr-addr-operator.rs b/src/test/ui/const-expr-addr-operator.rs index 24d4457f01d70..bfd6a4090649b 100644 --- a/src/test/ui/const-expr-addr-operator.rs +++ b/src/test/ui/const-expr-addr-operator.rs @@ -9,10 +9,11 @@ // except according to those terms. // Encountered while testing #44614. +// must-compile-successfully pub fn main() { // Constant of generic type (int) - const X: &'static u32 = &22; //~ ERROR constant evaluation error + const X: &'static u32 = &22; assert_eq!(0, match &22 { X => 0, _ => 1, diff --git a/src/test/ui/const-fn-error.rs b/src/test/ui/const-fn-error.rs index ac1c2fe5432de..dc1526a7079d4 100644 --- a/src/test/ui/const-fn-error.rs +++ b/src/test/ui/const-fn-error.rs @@ -13,17 +13,18 @@ const X : usize = 2; const fn f(x: usize) -> usize { - let mut sum = 0; //~ ERROR blocks in constant functions are limited - for i in 0..x { //~ ERROR calls in constant functions - //~| ERROR constant function contains unimplemented + let mut sum = 0; + //~^ ERROR E0016 + for i in 0..x { + //~^ ERROR E0015 + //~| ERROR E0019 sum += i; } - sum //~ ERROR E0080 - //~| non-constant path in constant + sum } #[allow(unused_variables)] fn main() { let a : [i32; f(X)]; - //~^ WARNING constant evaluation error: non-constant path + //~^ ERROR E0080 } diff --git a/src/test/ui/const-fn-error.stderr b/src/test/ui/const-fn-error.stderr index e738a5e4ff308..26c238992ab60 100644 --- a/src/test/ui/const-fn-error.stderr +++ b/src/test/ui/const-fn-error.stderr @@ -1,3 +1,4 @@ +<<<<<<< HEAD warning: constant evaluation error: non-constant path in constant expression --> $DIR/const-fn-error.rs:27:19 | @@ -10,6 +11,12 @@ error[E0016]: blocks in constant functions are limited to items and tail express --> $DIR/const-fn-error.rs:16:19 | LL | let mut sum = 0; //~ ERROR blocks in constant functions are limited +======= +error[E0016]: blocks in constant functions are limited to items and tail expressions + --> $DIR/const-fn-error.rs:16:19 + | +16 | let mut sum = 0; +>>>>>>> Produce instead of pointers | ^ error[E0015]: calls in constant functions are limited to constant functions, struct and enum constructors @@ -25,6 +32,7 @@ LL | for i in 0..x { //~ ERROR calls in constant functions | ^^^^ error[E0080]: constant evaluation error +<<<<<<< HEAD --> $DIR/const-fn-error.rs:21:5 | LL | sum //~ ERROR E0080 @@ -35,6 +43,12 @@ note: for constant expression here | LL | let a : [i32; f(X)]; | ^^^^^^^^^^^ +======= + --> $DIR/const-fn-error.rs:28:19 + | +28 | let a : [i32; f(X)]; + | ^^^^ miri failed: machine error: Cannot evaluate within constants: "calling non-const fn `>::into_iter`" +>>>>>>> Produce instead of pointers error: aborting due to 4 previous errors diff --git a/src/test/ui/const-len-underflow-separate-spans.rs b/src/test/ui/const-len-underflow-separate-spans.rs index 823cc988947cb..7582d0efa812e 100644 --- a/src/test/ui/const-len-underflow-separate-spans.rs +++ b/src/test/ui/const-len-underflow-separate-spans.rs @@ -15,9 +15,8 @@ const ONE: usize = 1; const TWO: usize = 2; const LEN: usize = ONE - TWO; -//~^ ERROR constant evaluation error [E0080] -//~| WARN attempt to subtract with overflow fn main() { let a: [i8; LEN] = unimplemented!(); +//~^ ERROR E0080 } diff --git a/src/test/ui/const-pattern-not-const-evaluable.rs b/src/test/ui/const-pattern-not-const-evaluable.rs index 263c0bdc64c6f..09b24d1ffa208 100644 --- a/src/test/ui/const-pattern-not-const-evaluable.rs +++ b/src/test/ui/const-pattern-not-const-evaluable.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// must-compile-successfully + #![feature(const_fn)] #[derive(PartialEq, Eq)] @@ -20,8 +22,6 @@ use Cake::*; struct Pair(A, B); const BOO: Pair = Pair(Marmor, BlackForest); -//~^ ERROR: constant evaluation error [E0080] -//~| unimplemented constant expression: tuple struct constructors const FOO: Cake = BOO.1; const fn foo() -> Cake { diff --git a/src/test/ui/feature-gate-const-indexing.rs b/src/test/ui/feature-gate-const-indexing.rs index 0d61878cd8073..eb5f746774cf1 100644 --- a/src/test/ui/feature-gate-const-indexing.rs +++ b/src/test/ui/feature-gate-const-indexing.rs @@ -8,10 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// must-compile-successfully fn main() { const ARR: [i32; 6] = [42, 43, 44, 45, 46, 47]; const IDX: usize = 3; const VAL: i32 = ARR[IDX]; - const BLUB: [i32; (ARR[0] - 41) as usize] = [5]; //~ ERROR constant evaluation error + const BLUB: [i32; (ARR[0] - 41) as usize] = [5]; } diff --git a/src/test/ui/issue-38875/issue_38875.rs b/src/test/ui/issue-38875/issue_38875.rs index 42e3c05a38c7e..24cd20a84a9fe 100644 --- a/src/test/ui/issue-38875/issue_38875.rs +++ b/src/test/ui/issue-38875/issue_38875.rs @@ -9,6 +9,7 @@ // except according to those terms. // aux-build:issue_38875_b.rs +// must-compile-successfully extern crate issue_38875_b; diff --git a/src/test/ui/union/union-const-eval.rs b/src/test/ui/union/union-const-eval.rs index a4c969ba20c46..aeafb45e6a552 100644 --- a/src/test/ui/union/union-const-eval.rs +++ b/src/test/ui/union/union-const-eval.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// must-compile-successfully + union U { a: usize, b: usize, @@ -16,9 +18,6 @@ union U { const C: U = U { a: 10 }; fn main() { - unsafe { - let a: [u8; C.a]; // OK - let b: [u8; C.b]; //~ ERROR constant evaluation error - //~| WARNING constant evaluation error - } + let a: [u8; unsafe { C.a }]; + let b: [u8; unsafe { C.b }]; } From e97089dae33d5b228bb1b50ae12e96b13ab499b3 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 27 Dec 2017 21:32:01 +0100 Subject: [PATCH 004/110] Move librustc_const_eval to librustc_mir --- src/doc/rustc-ux-guidelines.md | 1 - src/librustc/dep_graph/dep_node.rs | 1 - src/librustc/ty/maps/config.rs | 1 - src/librustc_const_eval/Cargo.toml | 19 - src/librustc_const_eval/diagnostics.rs | 571 ------------------ src/librustc_driver/Cargo.toml | 1 - src/librustc_driver/driver.rs | 3 +- src/librustc_driver/lib.rs | 2 - src/librustc_lint/Cargo.toml | 2 +- src/librustc_lint/lib.rs | 2 +- src/librustc_lint/types.rs | 2 +- src/librustc_mir/Cargo.toml | 2 +- src/librustc_mir/build/mod.rs | 2 +- .../const_eval}/_match.rs | 6 +- .../const_eval}/check_match.rs | 8 +- .../const_eval}/eval.rs | 0 src/librustc_mir/const_eval/mod.rs | 18 + .../const_eval}/pattern.rs | 29 +- src/librustc_mir/diagnostics.rs | 550 +++++++++++++++++ src/librustc_mir/hair/mod.rs | 2 +- src/librustc_mir/interpret/const_eval.rs | 34 +- src/librustc_mir/lib.rs | 6 +- src/librustc_passes/Cargo.toml | 2 +- src/librustc_passes/consts.rs | 2 +- src/librustc_passes/lib.rs | 2 +- 25 files changed, 626 insertions(+), 642 deletions(-) delete mode 100644 src/librustc_const_eval/Cargo.toml delete mode 100644 src/librustc_const_eval/diagnostics.rs rename src/{librustc_const_eval => librustc_mir/const_eval}/_match.rs (99%) rename src/{librustc_const_eval => librustc_mir/const_eval}/check_match.rs (99%) rename src/{librustc_const_eval => librustc_mir/const_eval}/eval.rs (100%) create mode 100644 src/librustc_mir/const_eval/mod.rs rename src/{librustc_const_eval => librustc_mir/const_eval}/pattern.rs (97%) diff --git a/src/doc/rustc-ux-guidelines.md b/src/doc/rustc-ux-guidelines.md index 323d49e469120..b62762ef69e01 100644 --- a/src/doc/rustc-ux-guidelines.md +++ b/src/doc/rustc-ux-guidelines.md @@ -64,7 +64,6 @@ for details on how to format and write long error codes. [librustc](https://github.com/rust-lang/rust/blob/master/src/librustc/diagnostics.rs), [libsyntax](https://github.com/rust-lang/rust/blob/master/src/libsyntax/diagnostics.rs), [librustc_borrowck](https://github.com/rust-lang/rust/blob/master/src/librustc_borrowck/diagnostics.rs), - [librustc_const_eval](https://github.com/rust-lang/rust/blob/master/src/librustc_const_eval/diagnostics.rs), [librustc_metadata](https://github.com/rust-lang/rust/blob/master/src/librustc_metadata/diagnostics.rs), [librustc_mir](https://github.com/rust-lang/rust/blob/master/src/librustc_mir/diagnostics.rs), [librustc_passes](https://github.com/rust-lang/rust/blob/master/src/librustc_passes/diagnostics.rs), diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 15e1d38be6950..ed46296389da4 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -63,7 +63,6 @@ use hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX}; use hir::map::DefPathHash; use hir::{HirId, ItemLocalId}; -use mir; use ich::Fingerprint; use ty::{TyCtxt, Instance, InstanceDef, ParamEnv, ParamEnvAnd, PolyTraitRef, Ty}; diff --git a/src/librustc/ty/maps/config.rs b/src/librustc/ty/maps/config.rs index ef1c8a8d4fa44..d880b022e2f18 100644 --- a/src/librustc/ty/maps/config.rs +++ b/src/librustc/ty/maps/config.rs @@ -13,7 +13,6 @@ use hir::def_id::{CrateNum, DefId, DefIndex}; use ty::{self, Ty, TyCtxt}; use ty::maps::queries; use ty::subst::Substs; -use mir; use std::hash::Hash; use syntax_pos::symbol::InternedString; diff --git a/src/librustc_const_eval/Cargo.toml b/src/librustc_const_eval/Cargo.toml deleted file mode 100644 index 53b8402ab2ad5..0000000000000 --- a/src/librustc_const_eval/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_const_eval" -version = "0.0.0" - -[lib] -name = "rustc_const_eval" -path = "lib.rs" -crate-type = ["dylib"] - -[dependencies] -arena = { path = "../libarena" } -log = "0.4" -rustc = { path = "../librustc" } -rustc_const_math = { path = "../librustc_const_math" } -rustc_data_structures = { path = "../librustc_data_structures" } -rustc_errors = { path = "../librustc_errors" } -syntax = { path = "../libsyntax" } -syntax_pos = { path = "../libsyntax_pos" } diff --git a/src/librustc_const_eval/diagnostics.rs b/src/librustc_const_eval/diagnostics.rs deleted file mode 100644 index d01b3c45f7fd1..0000000000000 --- a/src/librustc_const_eval/diagnostics.rs +++ /dev/null @@ -1,571 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(non_snake_case)] - -// Error messages for EXXXX errors. -// Each message should start and end with a new line, and be wrapped to 80 characters. -// In vim you can `:set tw=80` and use `gq` to wrap paragraphs. Use `:set tw=0` to disable. -register_long_diagnostics! { - -E0001: r##" -#### Note: this error code is no longer emitted by the compiler. - -This error suggests that the expression arm corresponding to the noted pattern -will never be reached as for all possible values of the expression being -matched, one of the preceding patterns will match. - -This means that perhaps some of the preceding patterns are too general, this -one is too specific or the ordering is incorrect. - -For example, the following `match` block has too many arms: - -``` -match Some(0) { - Some(bar) => {/* ... */} - x => {/* ... */} // This handles the `None` case - _ => {/* ... */} // All possible cases have already been handled -} -``` - -`match` blocks have their patterns matched in order, so, for example, putting -a wildcard arm above a more specific arm will make the latter arm irrelevant. - -Ensure the ordering of the match arm is correct and remove any superfluous -arms. -"##, - -E0002: r##" -#### Note: this error code is no longer emitted by the compiler. - -This error indicates that an empty match expression is invalid because the type -it is matching on is non-empty (there exist values of this type). In safe code -it is impossible to create an instance of an empty type, so empty match -expressions are almost never desired. This error is typically fixed by adding -one or more cases to the match expression. - -An example of an empty type is `enum Empty { }`. So, the following will work: - -``` -enum Empty {} - -fn foo(x: Empty) { - match x { - // empty - } -} -``` - -However, this won't: - -```compile_fail -fn foo(x: Option) { - match x { - // empty - } -} -``` -"##, - -E0003: r##" -#### Note: this error code is no longer emitted by the compiler. - -Not-a-Number (NaN) values cannot be compared for equality and hence can never -match the input to a match expression. So, the following will not compile: - -```compile_fail -const NAN: f32 = 0.0 / 0.0; - -let number = 0.1f32; - -match number { - NAN => { /* ... */ }, - _ => {} -} -``` - -To match against NaN values, you should instead use the `is_nan()` method in a -guard, like so: - -``` -let number = 0.1f32; - -match number { - x if x.is_nan() => { /* ... */ } - _ => {} -} -``` -"##, - -E0004: r##" -This error indicates that the compiler cannot guarantee a matching pattern for -one or more possible inputs to a match expression. Guaranteed matches are -required in order to assign values to match expressions, or alternatively, -determine the flow of execution. Erroneous code example: - -```compile_fail,E0004 -enum Terminator { - HastaLaVistaBaby, - TalkToMyHand, -} - -let x = Terminator::HastaLaVistaBaby; - -match x { // error: non-exhaustive patterns: `HastaLaVistaBaby` not covered - Terminator::TalkToMyHand => {} -} -``` - -If you encounter this error you must alter your patterns so that every possible -value of the input type is matched. For types with a small number of variants -(like enums) you should probably cover all cases explicitly. Alternatively, the -underscore `_` wildcard pattern can be added after all other patterns to match -"anything else". Example: - -``` -enum Terminator { - HastaLaVistaBaby, - TalkToMyHand, -} - -let x = Terminator::HastaLaVistaBaby; - -match x { - Terminator::TalkToMyHand => {} - Terminator::HastaLaVistaBaby => {} -} - -// or: - -match x { - Terminator::TalkToMyHand => {} - _ => {} -} -``` -"##, - -E0005: r##" -Patterns used to bind names must be irrefutable, that is, they must guarantee -that a name will be extracted in all cases. Erroneous code example: - -```compile_fail,E0005 -let x = Some(1); -let Some(y) = x; -// error: refutable pattern in local binding: `None` not covered -``` - -If you encounter this error you probably need to use a `match` or `if let` to -deal with the possibility of failure. Example: - -``` -let x = Some(1); - -match x { - Some(y) => { - // do something - }, - None => {} -} - -// or: - -if let Some(y) = x { - // do something -} -``` -"##, - -E0007: r##" -This error indicates that the bindings in a match arm would require a value to -be moved into more than one location, thus violating unique ownership. Code -like the following is invalid as it requires the entire `Option` to be -moved into a variable called `op_string` while simultaneously requiring the -inner `String` to be moved into a variable called `s`. - -```compile_fail,E0007 -let x = Some("s".to_string()); - -match x { - op_string @ Some(s) => {}, // error: cannot bind by-move with sub-bindings - None => {}, -} -``` - -See also the error E0303. -"##, - -E0008: r##" -Names bound in match arms retain their type in pattern guards. As such, if a -name is bound by move in a pattern, it should also be moved to wherever it is -referenced in the pattern guard code. Doing so however would prevent the name -from being available in the body of the match arm. Consider the following: - -```compile_fail,E0008 -match Some("hi".to_string()) { - Some(s) if s.len() == 0 => {}, // use s. - _ => {}, -} -``` - -The variable `s` has type `String`, and its use in the guard is as a variable of -type `String`. The guard code effectively executes in a separate scope to the -body of the arm, so the value would be moved into this anonymous scope and -therefore becomes unavailable in the body of the arm. - -The problem above can be solved by using the `ref` keyword. - -``` -match Some("hi".to_string()) { - Some(ref s) if s.len() == 0 => {}, - _ => {}, -} -``` - -Though this example seems innocuous and easy to solve, the problem becomes clear -when it encounters functions which consume the value: - -```compile_fail,E0008 -struct A{} - -impl A { - fn consume(self) -> usize { - 0 - } -} - -fn main() { - let a = Some(A{}); - match a { - Some(y) if y.consume() > 0 => {} - _ => {} - } -} -``` - -In this situation, even the `ref` keyword cannot solve it, since borrowed -content cannot be moved. This problem cannot be solved generally. If the value -can be cloned, here is a not-so-specific solution: - -``` -#[derive(Clone)] -struct A{} - -impl A { - fn consume(self) -> usize { - 0 - } -} - -fn main() { - let a = Some(A{}); - match a{ - Some(ref y) if y.clone().consume() > 0 => {} - _ => {} - } -} -``` - -If the value will be consumed in the pattern guard, using its clone will not -move its ownership, so the code works. -"##, - -E0009: r##" -In a pattern, all values that don't implement the `Copy` trait have to be bound -the same way. The goal here is to avoid binding simultaneously by-move and -by-ref. - -This limitation may be removed in a future version of Rust. - -Erroneous code example: - -```compile_fail,E0009 -struct X { x: (), } - -let x = Some((X { x: () }, X { x: () })); -match x { - Some((y, ref z)) => {}, // error: cannot bind by-move and by-ref in the - // same pattern - None => panic!() -} -``` - -You have two solutions: - -Solution #1: Bind the pattern's values the same way. - -``` -struct X { x: (), } - -let x = Some((X { x: () }, X { x: () })); -match x { - Some((ref y, ref z)) => {}, - // or Some((y, z)) => {} - None => panic!() -} -``` - -Solution #2: Implement the `Copy` trait for the `X` structure. - -However, please keep in mind that the first solution should be preferred. - -``` -#[derive(Clone, Copy)] -struct X { x: (), } - -let x = Some((X { x: () }, X { x: () })); -match x { - Some((y, ref z)) => {}, - None => panic!() -} -``` -"##, - -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 -directly within a pattern. - -The `static` keyword, on the other hand, guarantees a fixed location in memory. -This does not always mean that the value is constant. For example, a global -mutex can be declared `static` as well. - -If you want to match against a `static`, consider using a guard instead: - -``` -static FORTY_TWO: i32 = 42; - -match Some(42) { - Some(x) if x == FORTY_TWO => {} - _ => {} -} -``` -"##, - -E0162: r##" -An if-let pattern attempts to match the pattern, and enters the body if the -match was successful. If the match is irrefutable (when it cannot fail to -match), use a regular `let`-binding instead. For instance: - -```compile_fail,E0162 -struct Irrefutable(i32); -let irr = Irrefutable(0); - -// This fails to compile because the match is irrefutable. -if let Irrefutable(x) = irr { - // This body will always be executed. - // ... -} -``` - -Try this instead: - -``` -struct Irrefutable(i32); -let irr = Irrefutable(0); - -let Irrefutable(x) = irr; -println!("{}", x); -``` -"##, - -E0165: r##" -A while-let pattern attempts to match the pattern, and enters the body if the -match was successful. If the match is irrefutable (when it cannot fail to -match), use a regular `let`-binding inside a `loop` instead. For instance: - -```compile_fail,E0165 -struct Irrefutable(i32); -let irr = Irrefutable(0); - -// This fails to compile because the match is irrefutable. -while let Irrefutable(x) = irr { - // ... -} -``` - -Try this instead: - -```no_run -struct Irrefutable(i32); -let irr = Irrefutable(0); - -loop { - let Irrefutable(x) = irr; - // ... -} -``` -"##, - -E0170: r##" -Enum variants are qualified by default. For example, given this type: - -``` -enum Method { - GET, - POST, -} -``` - -You would match it using: - -``` -enum Method { - GET, - POST, -} - -let m = Method::GET; - -match m { - Method::GET => {}, - Method::POST => {}, -} -``` - -If you don't qualify the names, the code will bind new variables named "GET" and -"POST" instead. This behavior is likely not what you want, so `rustc` warns when -that happens. - -Qualified names are good practice, and most code works well with them. But if -you prefer them unqualified, you can import the variants into scope: - -``` -use Method::*; -enum Method { GET, POST } -# fn main() {} -``` - -If you want others to be able to import variants from your module directly, use -`pub use`: - -``` -pub use Method::*; -pub enum Method { GET, POST } -# fn main() {} -``` -"##, - - -E0297: r##" -#### Note: this error code is no longer emitted by the compiler. - -Patterns used to bind names must be irrefutable. That is, they must guarantee -that a name will be extracted in all cases. Instead of pattern matching the -loop variable, consider using a `match` or `if let` inside the loop body. For -instance: - -```compile_fail,E0005 -let xs : Vec> = vec![Some(1), None]; - -// This fails because `None` is not covered. -for Some(x) in xs { - // ... -} -``` - -Match inside the loop instead: - -``` -let xs : Vec> = vec![Some(1), None]; - -for item in xs { - match item { - Some(x) => {}, - None => {}, - } -} -``` - -Or use `if let`: - -``` -let xs : Vec> = vec![Some(1), None]; - -for item in xs { - if let Some(x) = item { - // ... - } -} -``` -"##, - -E0301: r##" -Mutable borrows are not allowed in pattern guards, because matching cannot have -side effects. Side effects could alter the matched object or the environment -on which the match depends in such a way, that the match would not be -exhaustive. For instance, the following would not match any arm if mutable -borrows were allowed: - -```compile_fail,E0301 -match Some(()) { - None => { }, - option if option.take().is_none() => { - /* impossible, option is `Some` */ - }, - Some(_) => { } // When the previous match failed, the option became `None`. -} -``` -"##, - -E0302: r##" -Assignments are not allowed in pattern guards, because matching cannot have -side effects. Side effects could alter the matched object or the environment -on which the match depends in such a way, that the match would not be -exhaustive. For instance, the following would not match any arm if assignments -were allowed: - -```compile_fail,E0302 -match Some(()) { - None => { }, - option if { option = None; false } => { }, - Some(_) => { } // When the previous match failed, the option became `None`. -} -``` -"##, - -E0303: r##" -In certain cases it is possible for sub-bindings to violate memory safety. -Updates to the borrow checker in a future version of Rust may remove this -restriction, but for now patterns must be rewritten without sub-bindings. - -Before: - -```compile_fail,E0303 -match Some("hi".to_string()) { - ref op_string_ref @ Some(s) => {}, - None => {}, -} -``` - -After: - -``` -match Some("hi".to_string()) { - Some(ref s) => { - let op_string_ref = &Some(s); - // ... - }, - None => {}, -} -``` - -The `op_string_ref` binding has type `&Option<&String>` in both cases. - -See also https://github.com/rust-lang/rust/issues/14587 -"##, - -} - - -register_diagnostics! { -// E0298, // cannot compare constants -// E0299, // mismatched types between arms -// E0471, // constant evaluation error (in pattern) -} diff --git a/src/librustc_driver/Cargo.toml b/src/librustc_driver/Cargo.toml index 802fe61d6f35b..6a1d9e5653428 100644 --- a/src/librustc_driver/Cargo.toml +++ b/src/librustc_driver/Cargo.toml @@ -17,7 +17,6 @@ rustc = { path = "../librustc" } rustc_allocator = { path = "../librustc_allocator" } rustc_back = { path = "../librustc_back" } rustc_borrowck = { path = "../librustc_borrowck" } -rustc_const_eval = { path = "../librustc_const_eval" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_errors = { path = "../librustc_errors" } rustc_incremental = { path = "../librustc_incremental" } diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 571cc46bc6413..54457eacbf2e6 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -37,7 +37,7 @@ use rustc_privacy; use rustc_plugin::registry::Registry; use rustc_plugin as plugin; use rustc_passes::{self, ast_validation, loops, consts, hir_stats}; -use rustc_const_eval::{self, check_match}; +use rustc_mir::const_eval::check_match; use super::Compilation; use serialize::json; @@ -942,7 +942,6 @@ pub fn default_provide(providers: &mut ty::maps::Providers) { ty::provide(providers); traits::provide(providers); reachable::provide(providers); - rustc_const_eval::provide(providers); rustc_passes::provide(providers); middle::region::provide(providers); cstore::provide(providers); diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 4b496fe3db65b..4953b85f994b3 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -35,7 +35,6 @@ extern crate rustc; extern crate rustc_allocator; extern crate rustc_back; extern crate rustc_borrowck; -extern crate rustc_const_eval; extern crate rustc_data_structures; extern crate rustc_errors as errors; extern crate rustc_passes; @@ -1566,7 +1565,6 @@ pub fn diagnostics_registry() -> errors::registry::Registry { // FIXME: need to figure out a way to get these back in here // all_errors.extend_from_slice(get_trans(sess).diagnostics()); all_errors.extend_from_slice(&rustc_trans_utils::DIAGNOSTICS); - all_errors.extend_from_slice(&rustc_const_eval::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_metadata::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_passes::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_plugin::DIAGNOSTICS); diff --git a/src/librustc_lint/Cargo.toml b/src/librustc_lint/Cargo.toml index 9fee2d54e4752..5ff891202dbc5 100644 --- a/src/librustc_lint/Cargo.toml +++ b/src/librustc_lint/Cargo.toml @@ -12,6 +12,6 @@ test = false [dependencies] log = "0.4" rustc = { path = "../librustc" } -rustc_const_eval = { path = "../librustc_const_eval" } +rustc_mir = { path = "../librustc_mir"} syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 9dc6d977851b1..79f3ff9f19f65 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -39,7 +39,7 @@ extern crate syntax; extern crate rustc; #[macro_use] extern crate log; -extern crate rustc_const_eval; +extern crate rustc_mir; extern crate syntax_pos; use rustc::lint; diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index f400ce42a9046..926aa2a724125 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -15,7 +15,7 @@ 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_const_eval::ConstContext; +use rustc_mir::const_eval::ConstContext; use rustc::mir::interpret::{Value, PrimVal}; use util::nodemap::FxHashSet; use lint::{LateContext, LintContext, LintArray}; diff --git a/src/librustc_mir/Cargo.toml b/src/librustc_mir/Cargo.toml index ea05a513f7e7d..90a0f18aba367 100644 --- a/src/librustc_mir/Cargo.toml +++ b/src/librustc_mir/Cargo.toml @@ -9,13 +9,13 @@ path = "lib.rs" crate-type = ["dylib"] [dependencies] +arena = { path = "../libarena" } bitflags = "1.0" graphviz = { path = "../libgraphviz" } log = "0.4" log_settings = "0.1.1" rustc = { path = "../librustc" } rustc_back = { path = "../librustc_back" } -rustc_const_eval = { path = "../librustc_const_eval" } rustc_const_math = { path = "../librustc_const_math" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_errors = { path = "../librustc_errors" } diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index a325cfe3eaae3..0f4d1e5bb0eca 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -21,7 +21,7 @@ use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::subst::Substs; use rustc::util::nodemap::NodeMap; use rustc_back::PanicStrategy; -use rustc_const_eval::pattern::{BindingMode, PatternKind}; +use const_eval::pattern::{BindingMode, PatternKind}; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; use shim; use std::mem; diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_mir/const_eval/_match.rs similarity index 99% rename from src/librustc_const_eval/_match.rs rename to src/librustc_mir/const_eval/_match.rs index 9e9eb4a81d035..9ebbc11162812 100644 --- a/src/librustc_const_eval/_match.rs +++ b/src/librustc_mir/const_eval/_match.rs @@ -13,15 +13,15 @@ use self::Usefulness::*; use self::WitnessPreference::*; use rustc::middle::const_val::ConstVal; -use eval::{compare_const_vals}; +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; -use pattern::{FieldPattern, Pattern, PatternKind}; -use pattern::{PatternFoldable, PatternFolder}; +use const_eval::pattern::{FieldPattern, Pattern, PatternKind}; +use const_eval::pattern::{PatternFoldable, PatternFolder}; use rustc::hir::def_id::DefId; use rustc::hir::RangeEnd; diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_mir/const_eval/check_match.rs similarity index 99% rename from src/librustc_const_eval/check_match.rs rename to src/librustc_mir/const_eval/check_match.rs index 6f7143c185cb3..3621d80df1de4 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_mir/const_eval/check_match.rs @@ -8,11 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use _match::{MatchCheckCtxt, Matrix, expand_pattern, is_useful}; -use _match::Usefulness::*; -use _match::WitnessPreference::*; +use const_eval::_match::{MatchCheckCtxt, Matrix, expand_pattern, is_useful}; +use const_eval::_match::Usefulness::*; +use const_eval::_match::WitnessPreference::*; -use pattern::{Pattern, PatternContext, PatternError, PatternKind}; +use const_eval::pattern::{Pattern, PatternContext, PatternError, PatternKind}; use rustc::middle::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor}; use rustc::middle::expr_use_visitor::{LoanCause, MutateMode}; diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_mir/const_eval/eval.rs similarity index 100% rename from src/librustc_const_eval/eval.rs rename to src/librustc_mir/const_eval/eval.rs diff --git a/src/librustc_mir/const_eval/mod.rs b/src/librustc_mir/const_eval/mod.rs new file mode 100644 index 0000000000000..27356c2b0821f --- /dev/null +++ b/src/librustc_mir/const_eval/mod.rs @@ -0,0 +1,18 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! constant evaluation on the HIR and code to validate patterns/matches + +mod eval; +mod _match; +pub mod check_match; +pub mod pattern; + +pub use self::eval::*; diff --git a/src/librustc_const_eval/pattern.rs b/src/librustc_mir/const_eval/pattern.rs similarity index 97% rename from src/librustc_const_eval/pattern.rs rename to src/librustc_mir/const_eval/pattern.rs index a2daf22c3b45b..a1109dbfdd1e0 100644 --- a/src/librustc_const_eval/pattern.rs +++ b/src/librustc_mir/const_eval/pattern.rs @@ -8,7 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use eval; +use const_eval::eval; +use interpret::{const_val_field, const_discr}; use rustc::middle::const_val::{ConstEvalErr, ConstVal, ConstAggregate}; use rustc::mir::{Field, BorrowKind, Mutability}; @@ -693,7 +694,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { return match expr.node { hir::ExprLit(ref lit) => { let ty = self.tables.expr_ty(expr); - match ::eval::lit_to_const(&lit.node, self.tcx, ty, false) { + match super::eval::lit_to_const(&lit.node, self.tcx, ty, false) { Ok(value) => PatternKind::Constant { value: self.tcx.mk_const(ty::Const { ty, @@ -716,7 +717,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { hir::ExprLit(ref lit) => lit, _ => span_bug!(expr.span, "not a literal: {:?}", expr), }; - match ::eval::lit_to_const(&lit.node, self.tcx, ty, true) { + match super::eval::lit_to_const(&lit.node, self.tcx, ty, true) { Ok(value) => PatternKind::Constant { value: self.tcx.mk_const(ty::Const { ty, @@ -782,9 +783,9 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { ty::TyAdt(adt_def, substs) if adt_def.is_enum() => { match cv.val { ConstVal::Value(val) => { - let discr = self.tcx.const_discr(self.param_env.and(( - instance, val, cv.ty - ))).unwrap(); + let discr = const_discr( + self.tcx, self.param_env, instance, val, cv.ty + ).unwrap(); let variant_index = adt_def .discriminants(self.tcx) .position(|var| var.to_u128_unchecked() == discr) @@ -801,8 +802,8 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { .map(|(i, _)| { let field = Field::new(i); let val = match cv.val { - ConstVal::Value(miri) => self.tcx.const_val_field( - self.param_env.and((instance, field, miri, cv.ty)), + ConstVal::Value(miri) => const_val_field( + self.tcx, self.param_env, instance, field, miri, cv.ty, ).unwrap(), _ => bug!("{:#?} is not a valid tuple", cv), }; @@ -844,8 +845,8 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { ConstVal::Aggregate(ConstAggregate::Struct(consts)) => { consts.iter().find(|&&(name, _)| name == f.name).unwrap().1 }, - ConstVal::Value(miri) => self.tcx.const_val_field( - self.param_env.and((instance, field, miri, cv.ty)), + ConstVal::Value(miri) => const_val_field( + self.tcx, self.param_env, instance, field, miri, cv.ty, ).unwrap(), _ => bug!("{:#?} is not a valid tuple", cv), }; @@ -862,8 +863,8 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { let field = Field::new(i); let val = match cv.val { ConstVal::Aggregate(ConstAggregate::Tuple(consts)) => consts[i], - ConstVal::Value(miri) => self.tcx.const_val_field( - self.param_env.and((instance, field, miri, cv.ty)), + ConstVal::Value(miri) => const_val_field( + self.tcx, self.param_env, instance, field, miri, cv.ty, ).unwrap(), _ => bug!("{:#?} is not a valid tuple", cv), }; @@ -882,8 +883,8 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { let val = match cv.val { ConstVal::Aggregate(ConstAggregate::Array(consts)) => consts[i], ConstVal::Aggregate(ConstAggregate::Repeat(cv, _)) => cv, - ConstVal::Value(miri) => self.tcx.const_val_field( - self.param_env.and((instance, field, miri, cv.ty)), + ConstVal::Value(miri) => const_val_field( + self.tcx, self.param_env, instance, field, miri, cv.ty, ).unwrap(), _ => bug!("{:#?} is not a valid tuple", cv), }; diff --git a/src/librustc_mir/diagnostics.rs b/src/librustc_mir/diagnostics.rs index 3491faf9cdac0..d71298fb1a6f5 100644 --- a/src/librustc_mir/diagnostics.rs +++ b/src/librustc_mir/diagnostics.rs @@ -12,6 +12,553 @@ register_long_diagnostics! { + +E0001: r##" +#### Note: this error code is no longer emitted by the compiler. + +This error suggests that the expression arm corresponding to the noted pattern +will never be reached as for all possible values of the expression being +matched, one of the preceding patterns will match. + +This means that perhaps some of the preceding patterns are too general, this +one is too specific or the ordering is incorrect. + +For example, the following `match` block has too many arms: + +``` +match Some(0) { + Some(bar) => {/* ... */} + x => {/* ... */} // This handles the `None` case + _ => {/* ... */} // All possible cases have already been handled +} +``` + +`match` blocks have their patterns matched in order, so, for example, putting +a wildcard arm above a more specific arm will make the latter arm irrelevant. + +Ensure the ordering of the match arm is correct and remove any superfluous +arms. +"##, + +E0002: r##" +#### Note: this error code is no longer emitted by the compiler. + +This error indicates that an empty match expression is invalid because the type +it is matching on is non-empty (there exist values of this type). In safe code +it is impossible to create an instance of an empty type, so empty match +expressions are almost never desired. This error is typically fixed by adding +one or more cases to the match expression. + +An example of an empty type is `enum Empty { }`. So, the following will work: + +``` +enum Empty {} + +fn foo(x: Empty) { + match x { + // empty + } +} +``` + +However, this won't: + +```compile_fail +fn foo(x: Option) { + match x { + // empty + } +} +``` +"##, + +E0003: r##" +#### Note: this error code is no longer emitted by the compiler. + +Not-a-Number (NaN) values cannot be compared for equality and hence can never +match the input to a match expression. So, the following will not compile: + +```compile_fail +const NAN: f32 = 0.0 / 0.0; + +let number = 0.1f32; + +match number { + NAN => { /* ... */ }, + _ => {} +} +``` + +To match against NaN values, you should instead use the `is_nan()` method in a +guard, like so: + +``` +let number = 0.1f32; + +match number { + x if x.is_nan() => { /* ... */ } + _ => {} +} +``` +"##, + +E0004: r##" +This error indicates that the compiler cannot guarantee a matching pattern for +one or more possible inputs to a match expression. Guaranteed matches are +required in order to assign values to match expressions, or alternatively, +determine the flow of execution. Erroneous code example: + +```compile_fail,E0004 +enum Terminator { + HastaLaVistaBaby, + TalkToMyHand, +} + +let x = Terminator::HastaLaVistaBaby; + +match x { // error: non-exhaustive patterns: `HastaLaVistaBaby` not covered + Terminator::TalkToMyHand => {} +} +``` + +If you encounter this error you must alter your patterns so that every possible +value of the input type is matched. For types with a small number of variants +(like enums) you should probably cover all cases explicitly. Alternatively, the +underscore `_` wildcard pattern can be added after all other patterns to match +"anything else". Example: + +``` +enum Terminator { + HastaLaVistaBaby, + TalkToMyHand, +} + +let x = Terminator::HastaLaVistaBaby; + +match x { + Terminator::TalkToMyHand => {} + Terminator::HastaLaVistaBaby => {} +} + +// or: + +match x { + Terminator::TalkToMyHand => {} + _ => {} +} +``` +"##, + +E0005: r##" +Patterns used to bind names must be irrefutable, that is, they must guarantee +that a name will be extracted in all cases. Erroneous code example: + +```compile_fail,E0005 +let x = Some(1); +let Some(y) = x; +// error: refutable pattern in local binding: `None` not covered +``` + +If you encounter this error you probably need to use a `match` or `if let` to +deal with the possibility of failure. Example: + +``` +let x = Some(1); + +match x { + Some(y) => { + // do something + }, + None => {} +} + +// or: + +if let Some(y) = x { + // do something +} +``` +"##, + +E0007: r##" +This error indicates that the bindings in a match arm would require a value to +be moved into more than one location, thus violating unique ownership. Code +like the following is invalid as it requires the entire `Option` to be +moved into a variable called `op_string` while simultaneously requiring the +inner `String` to be moved into a variable called `s`. + +```compile_fail,E0007 +let x = Some("s".to_string()); + +match x { + op_string @ Some(s) => {}, // error: cannot bind by-move with sub-bindings + None => {}, +} +``` + +See also the error E0303. +"##, + +E0008: r##" +Names bound in match arms retain their type in pattern guards. As such, if a +name is bound by move in a pattern, it should also be moved to wherever it is +referenced in the pattern guard code. Doing so however would prevent the name +from being available in the body of the match arm. Consider the following: + +```compile_fail,E0008 +match Some("hi".to_string()) { + Some(s) if s.len() == 0 => {}, // use s. + _ => {}, +} +``` + +The variable `s` has type `String`, and its use in the guard is as a variable of +type `String`. The guard code effectively executes in a separate scope to the +body of the arm, so the value would be moved into this anonymous scope and +therefore becomes unavailable in the body of the arm. + +The problem above can be solved by using the `ref` keyword. + +``` +match Some("hi".to_string()) { + Some(ref s) if s.len() == 0 => {}, + _ => {}, +} +``` + +Though this example seems innocuous and easy to solve, the problem becomes clear +when it encounters functions which consume the value: + +```compile_fail,E0008 +struct A{} + +impl A { + fn consume(self) -> usize { + 0 + } +} + +fn main() { + let a = Some(A{}); + match a { + Some(y) if y.consume() > 0 => {} + _ => {} + } +} +``` + +In this situation, even the `ref` keyword cannot solve it, since borrowed +content cannot be moved. This problem cannot be solved generally. If the value +can be cloned, here is a not-so-specific solution: + +``` +#[derive(Clone)] +struct A{} + +impl A { + fn consume(self) -> usize { + 0 + } +} + +fn main() { + let a = Some(A{}); + match a{ + Some(ref y) if y.clone().consume() > 0 => {} + _ => {} + } +} +``` + +If the value will be consumed in the pattern guard, using its clone will not +move its ownership, so the code works. +"##, + +E0009: r##" +In a pattern, all values that don't implement the `Copy` trait have to be bound +the same way. The goal here is to avoid binding simultaneously by-move and +by-ref. + +This limitation may be removed in a future version of Rust. + +Erroneous code example: + +```compile_fail,E0009 +struct X { x: (), } + +let x = Some((X { x: () }, X { x: () })); +match x { + Some((y, ref z)) => {}, // error: cannot bind by-move and by-ref in the + // same pattern + None => panic!() +} +``` + +You have two solutions: + +Solution #1: Bind the pattern's values the same way. + +``` +struct X { x: (), } + +let x = Some((X { x: () }, X { x: () })); +match x { + Some((ref y, ref z)) => {}, + // or Some((y, z)) => {} + None => panic!() +} +``` + +Solution #2: Implement the `Copy` trait for the `X` structure. + +However, please keep in mind that the first solution should be preferred. + +``` +#[derive(Clone, Copy)] +struct X { x: (), } + +let x = Some((X { x: () }, X { x: () })); +match x { + Some((y, ref z)) => {}, + None => panic!() +} +``` +"##, + +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 +directly within a pattern. + +The `static` keyword, on the other hand, guarantees a fixed location in memory. +This does not always mean that the value is constant. For example, a global +mutex can be declared `static` as well. + +If you want to match against a `static`, consider using a guard instead: + +``` +static FORTY_TWO: i32 = 42; + +match Some(42) { + Some(x) if x == FORTY_TWO => {} + _ => {} +} +``` +"##, + +E0162: r##" +An if-let pattern attempts to match the pattern, and enters the body if the +match was successful. If the match is irrefutable (when it cannot fail to +match), use a regular `let`-binding instead. For instance: + +```compile_fail,E0162 +struct Irrefutable(i32); +let irr = Irrefutable(0); + +// This fails to compile because the match is irrefutable. +if let Irrefutable(x) = irr { + // This body will always be executed. + // ... +} +``` + +Try this instead: + +``` +struct Irrefutable(i32); +let irr = Irrefutable(0); + +let Irrefutable(x) = irr; +println!("{}", x); +``` +"##, + +E0165: r##" +A while-let pattern attempts to match the pattern, and enters the body if the +match was successful. If the match is irrefutable (when it cannot fail to +match), use a regular `let`-binding inside a `loop` instead. For instance: + +```compile_fail,E0165 +struct Irrefutable(i32); +let irr = Irrefutable(0); + +// This fails to compile because the match is irrefutable. +while let Irrefutable(x) = irr { + // ... +} +``` + +Try this instead: + +```no_run +struct Irrefutable(i32); +let irr = Irrefutable(0); + +loop { + let Irrefutable(x) = irr; + // ... +} +``` +"##, + +E0170: r##" +Enum variants are qualified by default. For example, given this type: + +``` +enum Method { + GET, + POST, +} +``` + +You would match it using: + +``` +enum Method { + GET, + POST, +} + +let m = Method::GET; + +match m { + Method::GET => {}, + Method::POST => {}, +} +``` + +If you don't qualify the names, the code will bind new variables named "GET" and +"POST" instead. This behavior is likely not what you want, so `rustc` warns when +that happens. + +Qualified names are good practice, and most code works well with them. But if +you prefer them unqualified, you can import the variants into scope: + +``` +use Method::*; +enum Method { GET, POST } +# fn main() {} +``` + +If you want others to be able to import variants from your module directly, use +`pub use`: + +``` +pub use Method::*; +pub enum Method { GET, POST } +# fn main() {} +``` +"##, + + +E0297: r##" +#### Note: this error code is no longer emitted by the compiler. + +Patterns used to bind names must be irrefutable. That is, they must guarantee +that a name will be extracted in all cases. Instead of pattern matching the +loop variable, consider using a `match` or `if let` inside the loop body. For +instance: + +```compile_fail,E0005 +let xs : Vec> = vec![Some(1), None]; + +// This fails because `None` is not covered. +for Some(x) in xs { + // ... +} +``` + +Match inside the loop instead: + +``` +let xs : Vec> = vec![Some(1), None]; + +for item in xs { + match item { + Some(x) => {}, + None => {}, + } +} +``` + +Or use `if let`: + +``` +let xs : Vec> = vec![Some(1), None]; + +for item in xs { + if let Some(x) = item { + // ... + } +} +``` +"##, + +E0301: r##" +Mutable borrows are not allowed in pattern guards, because matching cannot have +side effects. Side effects could alter the matched object or the environment +on which the match depends in such a way, that the match would not be +exhaustive. For instance, the following would not match any arm if mutable +borrows were allowed: + +```compile_fail,E0301 +match Some(()) { + None => { }, + option if option.take().is_none() => { + /* impossible, option is `Some` */ + }, + Some(_) => { } // When the previous match failed, the option became `None`. +} +``` +"##, + +E0302: r##" +Assignments are not allowed in pattern guards, because matching cannot have +side effects. Side effects could alter the matched object or the environment +on which the match depends in such a way, that the match would not be +exhaustive. For instance, the following would not match any arm if assignments +were allowed: + +```compile_fail,E0302 +match Some(()) { + None => { }, + option if { option = None; false } => { }, + Some(_) => { } // When the previous match failed, the option became `None`. +} +``` +"##, + +E0303: r##" +In certain cases it is possible for sub-bindings to violate memory safety. +Updates to the borrow checker in a future version of Rust may remove this +restriction, but for now patterns must be rewritten without sub-bindings. + +Before: + +```compile_fail,E0303 +match Some("hi".to_string()) { + ref op_string_ref @ Some(s) => {}, + None => {}, +} +``` + +After: + +``` +match Some("hi".to_string()) { + Some(ref s) => { + let op_string_ref = &Some(s); + // ... + }, + None => {}, +} +``` + +The `op_string_ref` binding has type `&Option<&String>` in both cases. + +See also https://github.com/rust-lang/rust/issues/14587 +"##, + E0010: r##" The value of statics and constants must be known at compile time, and they live for the entire lifetime of a program. Creating a boxed value allocates memory on @@ -1771,6 +2318,9 @@ b.resume(); } register_diagnostics! { +// E0298, // cannot compare constants +// E0299, // mismatched types between arms +// E0471, // constant evaluation error (in pattern) // E0385, // {} in an aliasable location E0493, // destructors cannot be evaluated at compile-time E0524, // two closures require unique access to `..` at the same time diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs index 09a31f9ab8fa5..329365c4415e8 100644 --- a/src/librustc_mir/hair/mod.rs +++ b/src/librustc_mir/hair/mod.rs @@ -27,7 +27,7 @@ use self::cx::Cx; pub mod cx; -pub use rustc_const_eval::pattern::{BindingMode, Pattern, PatternKind, FieldPattern}; +pub use const_eval::pattern::{BindingMode, Pattern, PatternKind, FieldPattern}; #[derive(Copy, Clone, Debug)] pub enum LintLevel { diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index ec8215fb64c38..f5408c7381859 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -5,7 +5,7 @@ use rustc::hir::def_id::DefId; use rustc::mir; use rustc::middle::const_val::ErrKind::{CheckMatchError, TypeckError}; use rustc::middle::const_val::{ConstEvalErr, ConstVal}; -use rustc_const_eval::{lookup_const_by_id, ConstContext}; +use const_eval::{lookup_const_by_id, ConstContext}; use rustc::mir::Field; use rustc_data_structures::indexed_vec::Idx; @@ -306,16 +306,19 @@ impl<'tcx> super::Machine<'tcx> for CompileTimeEvaluator { pub fn const_val_field<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, - key: ty::ParamEnvAnd<'tcx, (ty::Instance<'tcx>, mir::Field, Value, Ty<'tcx>)>, + param_env: ty::ParamEnv<'tcx>, + instance: ty::Instance<'tcx>, + field: mir::Field, + val: Value, + ty: Ty<'tcx>, ) -> ::rustc::middle::const_val::EvalResult<'tcx> { - trace!("const_val_field: {:#?}", key); - match const_val_field_inner(tcx, key) { + match const_val_field_inner(tcx, param_env, instance, field, val, ty) { Ok((field, ty)) => Ok(tcx.mk_const(ty::Const { val: ConstVal::Value(field), ty, })), Err(err) => Err(ConstEvalErr { - span: tcx.def_span(key.value.0.def_id()), + span: tcx.def_span(instance.def_id()), kind: err.into(), }), } @@ -323,11 +326,14 @@ pub fn const_val_field<'a, 'tcx>( fn const_val_field_inner<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, - key: ty::ParamEnvAnd<'tcx, (ty::Instance<'tcx>, mir::Field, Value, Ty<'tcx>)>, + param_env: ty::ParamEnv<'tcx>, + instance: ty::Instance<'tcx>, + field: mir::Field, + value: Value, + ty: Ty<'tcx>, ) -> ::rustc::mir::interpret::EvalResult<'tcx, (Value, Ty<'tcx>)> { - trace!("const_val_field: {:#?}", key); - let (instance, field, value, ty) = key.value; - let mut ecx = mk_eval_cx(tcx, instance, key.param_env).unwrap(); + 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::ByRef(ptr, align) => { @@ -348,11 +354,13 @@ fn const_val_field_inner<'a, 'tcx>( pub fn const_discr<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, - key: ty::ParamEnvAnd<'tcx, (ty::Instance<'tcx>, Value, Ty<'tcx>)>, + param_env: ty::ParamEnv<'tcx>, + instance: ty::Instance<'tcx>, + value: Value, + ty: Ty<'tcx>, ) -> EvalResult<'tcx, u128> { - trace!("const_discr: {:#?}", key); - let (instance, value, ty) = key.value; - let mut ecx = mk_eval_cx(tcx, instance, key.param_env).unwrap(); + trace!("const_discr: {:?}, {:?}, {:?}", instance, value, ty); + let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap(); let (ptr, align) = match value { Value::ByValPair(..) | Value::ByVal(_) => { let layout = ecx.layout_of(ty)?; diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 8c15d1cf8b03a..d25caa588faf1 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -16,6 +16,8 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![deny(warnings)] +#![feature(slice_patterns)] +#![feature(from_ref)] #![feature(box_patterns)] #![feature(box_syntax)] #![feature(catch_expr)] @@ -38,6 +40,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(nonzero)] #![feature(underscore_lifetimes)] +extern crate arena; #[macro_use] extern crate bitflags; #[macro_use] extern crate log; @@ -52,7 +55,6 @@ extern crate syntax; extern crate syntax_pos; extern crate rustc_back; extern crate rustc_const_math; -extern crate rustc_const_eval; extern crate core; // for NonZero extern crate log_settings; extern crate rustc_apfloat; @@ -69,6 +71,7 @@ pub mod transform; pub mod util; pub mod interpret; pub mod monomorphize; +pub mod const_eval; use rustc::ty::maps::Providers; @@ -77,6 +80,7 @@ pub fn provide(providers: &mut Providers) { shim::provide(providers); transform::provide(providers); providers.const_eval = interpret::const_eval_provider; + providers.check_match = const_eval::check_match::check_match; } __build_diagnostic_array! { librustc_mir, DIAGNOSTICS } diff --git a/src/librustc_passes/Cargo.toml b/src/librustc_passes/Cargo.toml index e87e976eac3eb..4bab24ae1392c 100644 --- a/src/librustc_passes/Cargo.toml +++ b/src/librustc_passes/Cargo.toml @@ -11,7 +11,7 @@ crate-type = ["dylib"] [dependencies] log = "0.4" rustc = { path = "../librustc" } -rustc_const_eval = { path = "../librustc_const_eval" } +rustc_mir = { path = "../librustc_mir"} rustc_const_math = { path = "../librustc_const_math" } rustc_data_structures = { path = "../librustc_data_structures" } syntax = { path = "../libsyntax" } diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs index 8153c3c8493ff..420e8c4aad2c2 100644 --- a/src/librustc_passes/consts.rs +++ b/src/librustc_passes/consts.rs @@ -25,7 +25,7 @@ // by borrowck::gather_loans use rustc::ty::cast::CastKind; -use rustc_const_eval::ConstContext; +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}; diff --git a/src/librustc_passes/lib.rs b/src/librustc_passes/lib.rs index 8bd9598288770..a51bee9149951 100644 --- a/src/librustc_passes/lib.rs +++ b/src/librustc_passes/lib.rs @@ -23,7 +23,7 @@ #[macro_use] extern crate rustc; -extern crate rustc_const_eval; +extern crate rustc_mir; extern crate rustc_const_math; extern crate rustc_data_structures; From 0b9db5716f31d1df62214edd63ba26c368f9c6bf Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 16 Jan 2018 09:26:37 +0100 Subject: [PATCH 005/110] Resolve variant field access --- src/librustc_mir/const_eval/pattern.rs | 8 ++++---- src/librustc_mir/interpret/const_eval.rs | 12 +++++++++--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/librustc_mir/const_eval/pattern.rs b/src/librustc_mir/const_eval/pattern.rs index a1109dbfdd1e0..48822c506aef2 100644 --- a/src/librustc_mir/const_eval/pattern.rs +++ b/src/librustc_mir/const_eval/pattern.rs @@ -803,7 +803,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { let field = Field::new(i); let val = match cv.val { ConstVal::Value(miri) => const_val_field( - self.tcx, self.param_env, instance, 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), }; @@ -846,7 +846,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { consts.iter().find(|&&(name, _)| name == f.name).unwrap().1 }, ConstVal::Value(miri) => const_val_field( - self.tcx, self.param_env, instance, field, miri, cv.ty, + self.tcx, self.param_env, instance, None, field, miri, cv.ty, ).unwrap(), _ => bug!("{:#?} is not a valid tuple", cv), }; @@ -864,7 +864,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { let val = match cv.val { ConstVal::Aggregate(ConstAggregate::Tuple(consts)) => consts[i], ConstVal::Value(miri) => const_val_field( - self.tcx, self.param_env, instance, field, miri, cv.ty, + self.tcx, self.param_env, instance, None, field, miri, cv.ty, ).unwrap(), _ => bug!("{:#?} is not a valid tuple", cv), }; @@ -884,7 +884,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { 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, field, miri, cv.ty, + self.tcx, self.param_env, instance, None, field, miri, cv.ty, ).unwrap(), _ => bug!("{:#?} is not a valid tuple", cv), }; diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index f5408c7381859..3a9149b44bba7 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -13,7 +13,7 @@ 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}; +use super::{Place, EvalContext, StackPopCleanup, ValTy, HasMemory, PlaceExtra}; use rustc_const_math::ConstInt; @@ -308,11 +308,12 @@ pub fn const_val_field<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, param_env: ty::ParamEnv<'tcx>, instance: ty::Instance<'tcx>, + variant: Option, field: mir::Field, val: Value, ty: Ty<'tcx>, ) -> ::rustc::middle::const_val::EvalResult<'tcx> { - match const_val_field_inner(tcx, param_env, instance, field, val, ty) { + match const_val_field_inner(tcx, param_env, instance, variant, field, val, ty) { Ok((field, ty)) => Ok(tcx.mk_const(ty::Const { val: ConstVal::Value(field), ty, @@ -328,6 +329,7 @@ fn const_val_field_inner<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, param_env: ty::ParamEnv<'tcx>, instance: ty::Instance<'tcx>, + variant: Option, field: mir::Field, value: Value, ty: Ty<'tcx>, @@ -337,7 +339,11 @@ fn const_val_field_inner<'a, 'tcx>( let (mut field, ty) = match value { Value::ByValPair(..) | Value::ByVal(_) => ecx.read_field(value, field, ty)?.expect("const_val_field on non-field"), Value::ByRef(ptr, align) => { - let place = Place::from_primval_ptr(ptr, align); + let place = Place::Ptr { + ptr, + align, + extra: variant.map_or(PlaceExtra::None, PlaceExtra::DowncastVariant), + }; let layout = ecx.layout_of(ty)?; let (place, layout) = ecx.place_field(place, field, layout)?; let (ptr, align) = place.to_ptr_align(); From 28572d2c1fae5ab4c6bcade0c1a9fa485da0ec61 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 16 Jan 2018 09:28:27 +0100 Subject: [PATCH 006/110] Nuke the entire ctfe from orbit, it's the only way to be sure --- src/librustc/ich/impls_ty.rs | 43 -- src/librustc/lint/builtin.rs | 14 + src/librustc/middle/const_val.rs | 38 +- src/librustc/mir/mod.rs | 26 +- src/librustc/ty/context.rs | 21 +- src/librustc/ty/error.rs | 4 - src/librustc/ty/flags.rs | 28 +- src/librustc/ty/mod.rs | 7 - src/librustc/ty/relate.rs | 4 - src/librustc/ty/structural_impls.rs | 69 +- src/librustc/ty/util.rs | 1 - src/librustc/ty/walk.rs | 28 +- src/librustc/ty/wf.rs | 26 +- src/librustc/util/ppaux.rs | 4 - src/librustc_driver/driver.rs | 7 + src/librustc_lint/builtin.rs | 72 -- src/librustc_lint/lib.rs | 2 - src/librustc_lint/types.rs | 87 +-- src/librustc_metadata/decoder.rs | 15 +- src/librustc_metadata/encoder.rs | 13 +- .../borrow_check/nll/type_check/mod.rs | 8 +- src/librustc_mir/build/expr/as_rvalue.rs | 22 +- src/librustc_mir/build/matches/test.rs | 3 +- src/librustc_mir/build/misc.rs | 55 +- src/librustc_mir/const_eval/_match.rs | 46 +- src/librustc_mir/const_eval/check.rs | 167 +++++ src/librustc_mir/const_eval/eval.rs | 707 +++--------------- src/librustc_mir/const_eval/mod.rs | 1 + src/librustc_mir/const_eval/pattern.rs | 215 +++--- src/librustc_mir/diagnostics.rs | 36 + src/librustc_mir/hair/cx/expr.rs | 34 +- src/librustc_mir/hair/cx/mod.rs | 172 +---- src/librustc_mir/interpret/const_eval.rs | 397 ++-------- src/librustc_mir/interpret/eval_context.rs | 33 +- src/librustc_mir/interpret/mod.rs | 2 +- src/librustc_mir/interpret/place.rs | 8 +- src/librustc_mir/shim.rs | 26 +- src/librustc_mir/transform/elaborate_drops.rs | 3 +- src/librustc_mir/transform/generator.rs | 9 +- .../transform/simplify_branches.rs | 8 +- src/librustc_mir/util/elaborate_drops.rs | 6 +- src/librustc_passes/consts.rs | 113 +-- src/librustc_passes/diagnostics.rs | 35 - src/librustc_trans/mir/analyze.rs | 1 - src/librustc_trans/mir/constant.rs | 45 +- src/librustc_typeck/collect.rs | 1 - src/test/compile-fail/issue-31109.rs | 3 +- src/test/compile-fail/issue-39559-2.rs | 6 +- src/test/compile-fail/issue-41255.rs | 18 +- .../compile-fail/lint-exceeding-bitshifts.rs | 2 +- src/test/compile-fail/lint-type-overflow2.rs | 1 - .../rfc1445/match-forbidden-without-eq.rs | 3 +- src/test/compile-fail/thread-local-in-ctfe.rs | 2 - 53 files changed, 630 insertions(+), 2067 deletions(-) create mode 100644 src/librustc_mir/const_eval/check.rs diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 4d802491c96ed..08a42e61ea000 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -338,53 +338,10 @@ for ::middle::const_val::ConstVal<'gcx> { hcx: &mut StableHashingContext<'gcx>, hasher: &mut StableHasher) { 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); diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index b68b7dc6c0672..a951265d458bf 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -20,6 +20,12 @@ use session::Session; use session::config::Epoch; use syntax::codemap::Span; +declare_lint! { + pub EXCEEDING_BITSHIFTS, + Deny, + "shift exceeds the type's number of bits" +} + declare_lint! { pub CONST_ERR, Warn, @@ -263,6 +269,12 @@ declare_lint! { Epoch::Epoch2018 } +declare_lint! { + pub ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, + Warn, + "floating-point literals cannot be used in patterns" +} + /// Does nothing as a lint pass, but registers some `Lint`s /// which are used by other parts of the compiler. #[derive(Copy, Clone)] @@ -271,6 +283,8 @@ pub struct HardwiredLints; impl LintPass for HardwiredLints { fn get_lints(&self) -> LintArray { lint_array!( + ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, + EXCEEDING_BITSHIFTS, UNUSED_IMPORTS, UNUSED_EXTERN_CRATES, UNUSED_QUALIFICATIONS, diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index cf322010e05c3..dd99305809c28 100644 --- a/src/librustc/middle/const_val.rs +++ b/src/librustc/middle/const_val.rs @@ -18,9 +18,7 @@ use mir::interpret::{Value, PrimVal}; use graphviz::IntoCow; use errors::DiagnosticBuilder; -use serialize::{self, Encodable, Encoder, Decodable, Decoder}; -use syntax::symbol::InternedString; -use syntax::ast; +use serialize; use syntax_pos::Span; use std::borrow::Cow; @@ -29,17 +27,7 @@ pub type EvalResult<'tcx> = Result<&'tcx ty::Const<'tcx>, ConstEvalErr<'tcx>>; #[derive(Copy, Clone, Debug, Hash, RustcEncodable, RustcDecodable, Eq, PartialEq)] pub enum ConstVal<'tcx> { - Integral(ConstInt), - Float(ConstFloat), - Str(InternedString), - ByteStr(ByteArray<'tcx>), - Bool(bool), - Char(char), - Variant(DefId), - Function(DefId, &'tcx Substs<'tcx>), - Aggregate(ConstAggregate<'tcx>), Unevaluated(DefId, &'tcx Substs<'tcx>), - /// A miri value, currently only produced if --miri is enabled Value(Value), } @@ -50,32 +38,9 @@ pub struct ByteArray<'tcx> { impl<'tcx> serialize::UseSpecializedDecodable for ByteArray<'tcx> {} -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] -pub enum ConstAggregate<'tcx> { - Struct(&'tcx [(ast::Name, &'tcx ty::Const<'tcx>)]), - Tuple(&'tcx [&'tcx ty::Const<'tcx>]), - Array(&'tcx [&'tcx ty::Const<'tcx>]), - Repeat(&'tcx ty::Const<'tcx>, u64), -} - -impl<'tcx> Encodable for ConstAggregate<'tcx> { - fn encode(&self, _: &mut S) -> Result<(), S::Error> { - bug!("should never encode ConstAggregate::{:?}", self) - } -} - -impl<'tcx> Decodable for ConstAggregate<'tcx> { - fn decode(_: &mut D) -> Result { - bug!("should never decode ConstAggregate") - } -} - impl<'tcx> ConstVal<'tcx> { pub fn to_u128(&self) -> Option { match *self { - ConstVal::Integral(i) => i.to_u128(), - ConstVal::Bool(b) => Some(b as u128), - ConstVal::Char(ch) => Some(ch as u32 as u128), ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))) => { Some(b) }, @@ -93,7 +58,6 @@ impl<'tcx> ConstVal<'tcx> { } pub fn unwrap_usize<'a, 'gcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> ConstUsize { match *self { - ConstVal::Integral(ConstInt::Usize(i)) => i, ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))) => { assert_eq!(b as u64 as u128, b); match ConstUsize::new(b as u64, tcx.sess.target.usize_ty) { diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index d35cbd0027fb6..d899435141819 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -33,7 +33,6 @@ use ty::TypeAndMut; use util::ppaux; use std::slice; use hir::{self, InlineAsm}; -use std::ascii; use std::borrow::{Cow}; use std::cell::Ref; use std::fmt::{self, Debug, Formatter, Write}; @@ -1539,12 +1538,8 @@ impl<'tcx> Operand<'tcx> { ty, literal: Literal::Value { value: tcx.mk_const(ty::Const { - val: if tcx.sess.opts.debugging_opts.miri { - // ZST function type - ConstVal::Value(Value::ByVal(PrimVal::Undef)) - } else { - ConstVal::Function(def_id, substs) - }, + // ZST function type + val: ConstVal::Value(Value::ByVal(PrimVal::Undef)), ty }) }, @@ -1877,21 +1872,6 @@ impl<'tcx> Debug for Literal<'tcx> { fn fmt_const_val(fmt: &mut W, const_val: &ty::Const) -> fmt::Result { use middle::const_val::ConstVal::*; match const_val.val { - Float(f) => write!(fmt, "{:?}", f), - Integral(n) => write!(fmt, "{}", n), - Str(s) => write!(fmt, "{:?}", s), - ByteStr(bytes) => { - let escaped: String = bytes.data - .iter() - .flat_map(|&ch| ascii::escape_default(ch).map(|c| c as char)) - .collect(); - write!(fmt, "b\"{}\"", escaped) - } - Bool(b) => write!(fmt, "{:?}", b), - Char(c) => write!(fmt, "{:?}", c), - Variant(def_id) | - Function(def_id, _) => write!(fmt, "{}", item_path_str(def_id)), - Aggregate(_) => bug!("`ConstVal::{:?}` should not be in MIR", const_val), Unevaluated(..) => write!(fmt, "{:?}", const_val), Value(val) => print_miri_value(val, const_val.ty, fmt), } @@ -1918,7 +1898,7 @@ fn print_miri_value(value: Value, ty: Ty, f: &mut W) -> fmt::Result { let alloc = tcx .interpret_interner .borrow() - .get_alloc(ptr.alloc_id.0) + .get_alloc(ptr.alloc_id) .expect("miri alloc not found"); assert_eq!(len as usize as u128, len); let slice = &alloc.bytes[(ptr.offset as usize)..][..(len as usize)]; diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 17926eeec0020..ebd78467c3b9e 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -54,7 +54,7 @@ use rustc_data_structures::stable_hasher::{HashStable, hash_stable_hashmap, StableHasher, StableHasherResult, StableVec}; use arena::{TypedArena, DroplessArena}; -use rustc_const_math::{ConstInt, ConstUsize}; +use rustc_const_math::ConstUsize; use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::sync::Lrc; use std::any::Any; @@ -909,7 +909,7 @@ pub struct InterpretInterner<'tcx> { /// Reverse map of `alloc_cache` /// /// Multiple globals may share the same memory - global_cache: FxHashMap>>, + global_cache: FxHashMap>>, /// The AllocId to assign to the next new regular allocation. /// Always incremented, never gets smaller. @@ -959,20 +959,17 @@ impl<'tcx> InterpretInterner<'tcx> { pub fn cache( &mut self, global_id: interpret::GlobalId<'tcx>, - ptr: interpret::AllocId, + alloc_id: interpret::AllocId, ) { - if let interpret::PrimVal::Ptr(ptr) = ptr.primval { - assert!(ptr.offset == 0); - } - self.global_cache.entry(ptr).or_default().push(global_id); - if let Some(old) = self.alloc_cache.insert(global_id, ptr) { + self.global_cache.entry(alloc_id).or_default().push(global_id); + if let Some(old) = self.alloc_cache.insert(global_id, alloc_id) { bug!("tried to cache {:?}, but was already existing as {:#?}", global_id, old); } } pub fn get_globals( &self, - ptr: interpret::Pointer, + ptr: interpret::AllocId, ) -> &[interpret::GlobalId<'tcx>] { match self.global_cache.get(&ptr) { Some(v) => v, @@ -2099,11 +2096,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { pub fn mk_array_const_usize(self, ty: Ty<'tcx>, n: ConstUsize) -> Ty<'tcx> { self.mk_ty(TyArray(ty, self.mk_const(ty::Const { - val: if self.sess.opts.debugging_opts.miri { - ConstVal::Value(Value::ByVal(PrimVal::Bytes(n.as_u64().into()))) - } else { - ConstVal::Integral(ConstInt::Usize(n)) - }, + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(n.as_u64().into()))), ty: self.types.usize }))) } diff --git a/src/librustc/ty/error.rs b/src/librustc/ty/error.rs index 07920c58271cd..5dc78d84da835 100644 --- a/src/librustc/ty/error.rs +++ b/src/librustc/ty/error.rs @@ -19,8 +19,6 @@ use syntax::ast; use errors::DiagnosticBuilder; use syntax_pos::Span; -use rustc_const_math::ConstInt; - use hir; #[derive(Clone, Copy, Debug)] @@ -188,8 +186,6 @@ impl<'a, 'gcx, 'lcx, 'tcx> ty::TyS<'tcx> { ty::TyForeign(def_id) => format!("extern type `{}`", tcx.item_path_str(def_id)), ty::TyArray(_, n) => { match n.val { - ConstVal::Integral(ConstInt::Usize(n)) => - format!("array of {} elements", n), ConstVal::Value(Value::ByVal(PrimVal::Bytes(n))) => format!("array of {} elements", n), _ => "array".to_string(), diff --git a/src/librustc/ty/flags.rs b/src/librustc/ty/flags.rs index 60bf4afc2fc88..f067789771c59 100644 --- a/src/librustc/ty/flags.rs +++ b/src/librustc/ty/flags.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use middle::const_val::{ConstVal, ConstAggregate}; +use middle::const_val::ConstVal; use ty::subst::Substs; use ty::{self, Ty, TypeFlags, TypeFoldable}; @@ -218,31 +218,7 @@ impl FlagComputation { fn add_const(&mut self, constant: &ty::Const) { self.add_ty(constant.ty); match constant.val { - ConstVal::Integral(_) | - ConstVal::Float(_) | - ConstVal::Str(_) | - ConstVal::ByteStr(_) | - ConstVal::Bool(_) | - ConstVal::Char(_) | - ConstVal::Value(_) | - ConstVal::Variant(_) => {} - ConstVal::Function(_, substs) => { - self.add_substs(substs); - } - ConstVal::Aggregate(ConstAggregate::Struct(fields)) => { - for &(_, v) in fields { - self.add_const(v); - } - } - ConstVal::Aggregate(ConstAggregate::Tuple(fields)) | - ConstVal::Aggregate(ConstAggregate::Array(fields)) => { - for v in fields { - self.add_const(v); - } - } - ConstVal::Aggregate(ConstAggregate::Repeat(v, _)) => { - self.add_const(v); - } + ConstVal::Value(_) => {} ConstVal::Unevaluated(_, substs) => { self.add_flags(TypeFlags::HAS_PROJECTION); self.add_substs(substs); diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 1577e78d81e22..8c955bf340e8d 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1836,9 +1836,6 @@ impl<'a, 'gcx, 'tcx> AdtDef { if let VariantDiscr::Explicit(expr_did) = v.discr { let substs = Substs::identity_for_item(tcx.global_tcx(), expr_did); match tcx.const_eval(param_env.and((expr_did, substs))) { - Ok(&ty::Const { val: ConstVal::Integral(v), .. }) => { - discr = v; - } Ok(&ty::Const { val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))), .. @@ -1889,10 +1886,6 @@ impl<'a, 'gcx, 'tcx> AdtDef { ty::VariantDiscr::Explicit(expr_did) => { let substs = Substs::identity_for_item(tcx.global_tcx(), expr_did); match tcx.const_eval(param_env.and((expr_did, substs))) { - Ok(&ty::Const { val: ConstVal::Integral(v), .. }) => { - explicit_value = v; - break; - } Ok(&ty::Const { val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))), .. diff --git a/src/librustc/ty/relate.rs b/src/librustc/ty/relate.rs index bac78508993a5..2cbe9da88b51b 100644 --- a/src/librustc/ty/relate.rs +++ b/src/librustc/ty/relate.rs @@ -483,7 +483,6 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R, assert_eq!(sz_b.ty, tcx.types.usize); let to_u64 = |x: &'tcx ty::Const<'tcx>| -> Result { match x.val { - ConstVal::Integral(x) => Ok(x.to_u64().unwrap()), ConstVal::Value(Value::ByVal(prim)) => Ok(prim.to_u64().unwrap()), ConstVal::Unevaluated(def_id, substs) => { // FIXME(eddyb) get the right param_env. @@ -491,9 +490,6 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R, match tcx.lift_to_global(&substs) { Some(substs) => { match tcx.const_eval(param_env.and((def_id, substs))) { - Ok(&ty::Const { val: ConstVal::Integral(x), .. }) => { - return Ok(x.to_u64().unwrap()); - } Ok(&ty::Const { val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))), .. diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 80250949b0b4a..ad66cbd9eab3b 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -13,7 +13,7 @@ //! hand, though we've recently added some macros (e.g., //! `BraceStructLiftImpl!`) to help with the tedium. -use middle::const_val::{self, ConstVal, ConstAggregate, ConstEvalErr}; +use middle::const_val::{self, ConstVal, ConstEvalErr}; use ty::{self, Lift, Ty, TyCtxt}; use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use rustc_data_structures::accumulate_vec::AccumulateVec; @@ -1410,54 +1410,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::error::TypeError<'tcx> { impl<'tcx> TypeFoldable<'tcx> for ConstVal<'tcx> { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { match *self { - ConstVal::Integral(i) => ConstVal::Integral(i), - ConstVal::Float(f) => ConstVal::Float(f), - ConstVal::Str(s) => ConstVal::Str(s), - ConstVal::ByteStr(b) => ConstVal::ByteStr(b), - ConstVal::Bool(b) => ConstVal::Bool(b), - ConstVal::Char(c) => ConstVal::Char(c), ConstVal::Value(v) => ConstVal::Value(v), - ConstVal::Variant(def_id) => ConstVal::Variant(def_id), - ConstVal::Function(def_id, substs) => { - ConstVal::Function(def_id, substs.fold_with(folder)) - } - ConstVal::Aggregate(ConstAggregate::Struct(fields)) => { - let new_fields: Vec<_> = fields.iter().map(|&(name, v)| { - (name, v.fold_with(folder)) - }).collect(); - let fields = if new_fields == fields { - fields - } else { - folder.tcx().alloc_name_const_slice(&new_fields) - }; - ConstVal::Aggregate(ConstAggregate::Struct(fields)) - } - ConstVal::Aggregate(ConstAggregate::Tuple(fields)) => { - let new_fields: Vec<_> = fields.iter().map(|v| { - v.fold_with(folder) - }).collect(); - let fields = if new_fields == fields { - fields - } else { - folder.tcx().alloc_const_slice(&new_fields) - }; - ConstVal::Aggregate(ConstAggregate::Tuple(fields)) - } - ConstVal::Aggregate(ConstAggregate::Array(fields)) => { - let new_fields: Vec<_> = fields.iter().map(|v| { - v.fold_with(folder) - }).collect(); - let fields = if new_fields == fields { - fields - } else { - folder.tcx().alloc_const_slice(&new_fields) - }; - ConstVal::Aggregate(ConstAggregate::Array(fields)) - } - ConstVal::Aggregate(ConstAggregate::Repeat(v, count)) => { - let v = v.fold_with(folder); - ConstVal::Aggregate(ConstAggregate::Repeat(v, count)) - } ConstVal::Unevaluated(def_id, substs) => { ConstVal::Unevaluated(def_id, substs.fold_with(folder)) } @@ -1466,25 +1419,7 @@ impl<'tcx> TypeFoldable<'tcx> for ConstVal<'tcx> { fn super_visit_with>(&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), } } diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 6ad2901ce3a10..6ae0d520e54d3 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -765,7 +765,6 @@ impl<'a, 'gcx, 'tcx, W> TypeVisitor<'tcx> for TypeIdHasher<'a, 'gcx, 'tcx, W> TyArray(_, n) => { self.hash_discriminant_u8(&n.val); match n.val { - ConstVal::Integral(x) => self.hash(x.to_u64().unwrap()), ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))) => self.hash(b), ConstVal::Unevaluated(def_id, _) => self.def_id(def_id), _ => bug!("arrays should not have {:?} as length", n) diff --git a/src/librustc/ty/walk.rs b/src/librustc/ty/walk.rs index 4ef7706c45e3e..722fdfe773a98 100644 --- a/src/librustc/ty/walk.rs +++ b/src/librustc/ty/walk.rs @@ -11,7 +11,7 @@ //! An iterator over the type substructure. //! WARNING: this does not keep track of the region depth. -use middle::const_val::{ConstVal, ConstAggregate}; +use middle::const_val::ConstVal; use ty::{self, Ty}; use rustc_data_structures::small_vec::SmallVec; use rustc_data_structures::accumulate_vec::IntoIter as AccIntoIter; @@ -140,31 +140,7 @@ fn push_subtypes<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent_ty: Ty<'tcx>) { fn push_const<'tcx>(stack: &mut TypeWalkerStack<'tcx>, constant: &'tcx ty::Const<'tcx>) { match constant.val { - ConstVal::Integral(_) | - ConstVal::Float(_) | - ConstVal::Str(_) | - ConstVal::ByteStr(_) | - ConstVal::Bool(_) | - ConstVal::Char(_) | - ConstVal::Value(_) | - ConstVal::Variant(_) => {} - ConstVal::Function(_, substs) => { - stack.extend(substs.types().rev()); - } - ConstVal::Aggregate(ConstAggregate::Struct(fields)) => { - for &(_, v) in fields.iter().rev() { - push_const(stack, v); - } - } - ConstVal::Aggregate(ConstAggregate::Tuple(fields)) | - ConstVal::Aggregate(ConstAggregate::Array(fields)) => { - for v in fields.iter().rev() { - push_const(stack, v); - } - } - ConstVal::Aggregate(ConstAggregate::Repeat(v, _)) => { - push_const(stack, v); - } + ConstVal::Value(_) => {} ConstVal::Unevaluated(_, substs) => { stack.extend(substs.types().rev()); } diff --git a/src/librustc/ty/wf.rs b/src/librustc/ty/wf.rs index 443e0e857a7a1..49ae79ae9c92c 100644 --- a/src/librustc/ty/wf.rs +++ b/src/librustc/ty/wf.rs @@ -9,7 +9,7 @@ // except according to those terms. use hir::def_id::DefId; -use middle::const_val::{ConstVal, ConstAggregate}; +use middle::const_val::ConstVal; use infer::InferCtxt; use ty::subst::Substs; use traits; @@ -217,29 +217,7 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> { fn compute_const(&mut self, constant: &'tcx ty::Const<'tcx>) { self.require_sized(constant.ty, traits::ConstSized); match constant.val { - ConstVal::Integral(_) | - ConstVal::Float(_) | - ConstVal::Str(_) | - ConstVal::ByteStr(_) | - ConstVal::Bool(_) | - ConstVal::Char(_) | - ConstVal::Variant(_) | - ConstVal::Value(_) | - ConstVal::Function(..) => {} - ConstVal::Aggregate(ConstAggregate::Struct(fields)) => { - for &(_, v) in fields { - self.compute_const(v); - } - } - ConstVal::Aggregate(ConstAggregate::Tuple(fields)) | - ConstVal::Aggregate(ConstAggregate::Array(fields)) => { - for v in fields { - self.compute_const(v); - } - } - ConstVal::Aggregate(ConstAggregate::Repeat(v, _)) => { - self.compute_const(v); - } + ConstVal::Value(_) => {} ConstVal::Unevaluated(def_id, substs) => { let obligations = self.nominal_obligations(def_id, substs); self.out.extend(obligations); diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 63d1f1468251b..5e2792ee6410e 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -27,7 +27,6 @@ use std::cell::Cell; use std::fmt; use std::usize; -use rustc_const_math::ConstInt; use rustc_data_structures::indexed_vec::Idx; use syntax::abi::Abi; use syntax::ast::CRATE_NODE_ID; @@ -1166,9 +1165,6 @@ define_print! { TyArray(ty, sz) => { print!(f, cx, write("["), print(ty), write("; "))?; match sz.val { - ConstVal::Integral(ConstInt::Usize(sz)) => { - write!(f, "{}", sz)?; - } ConstVal::Value(Value::ByVal(PrimVal::Bytes(sz))) => { write!(f, "{}", sz)?; } diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 54457eacbf2e6..943de1211182c 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -1088,6 +1088,13 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(trans: &TransCrate, stability::check_unused_or_stable_features(tcx) }); + + time(time_passes, + "MIR linting", + || for def_id in tcx.body_owners() { + mir::const_eval::check::check(tcx, def_id) + }); + time(time_passes, "lint checking", || lint::check_crate(tcx)); return Ok(f(tcx, analysis, rx, tcx.sess.compile_status())); diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 62ac898337ca9..831d4fc755f8f 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -682,78 +682,6 @@ impl EarlyLintPass for DeprecatedAttr { } } -declare_lint! { - pub ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, - Warn, - "floating-point literals cannot be used in patterns" -} - -/// Checks for floating point literals in patterns. -#[derive(Clone)] -pub struct IllegalFloatLiteralPattern; - -impl LintPass for IllegalFloatLiteralPattern { - fn get_lints(&self) -> LintArray { - lint_array!(ILLEGAL_FLOATING_POINT_LITERAL_PATTERN) - } -} - -fn fl_lit_check_expr(cx: &EarlyContext, expr: &ast::Expr) { - use self::ast::{ExprKind, LitKind}; - match expr.node { - ExprKind::Lit(ref l) => { - match l.node { - LitKind::FloatUnsuffixed(..) | - LitKind::Float(..) => { - cx.span_lint(ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, - l.span, - "floating-point literals cannot be used in patterns"); - }, - _ => (), - } - } - // These may occur in patterns - // and can maybe contain float literals - ExprKind::Unary(_, ref f) => fl_lit_check_expr(cx, f), - // Other kinds of exprs can't occur in patterns so we don't have to check them - // (ast_validation will emit an error if they occur) - _ => (), - } -} - -impl EarlyLintPass for IllegalFloatLiteralPattern { - fn check_pat(&mut self, cx: &EarlyContext, pat: &ast::Pat) { - use self::ast::PatKind; - pat.walk(&mut |p| { - match p.node { - // Wildcard patterns and paths are uninteresting for the lint - PatKind::Wild | - PatKind::Path(..) => (), - - // The walk logic recurses inside these - PatKind::Ident(..) | - PatKind::Struct(..) | - PatKind::Tuple(..) | - PatKind::TupleStruct(..) | - PatKind::Ref(..) | - PatKind::Box(..) | - PatKind::Paren(..) | - PatKind::Slice(..) => (), - - // Extract the expressions and check them - PatKind::Lit(ref e) => fl_lit_check_expr(cx, e), - PatKind::Range(ref st, ref en, _) => { - fl_lit_check_expr(cx, st); - fl_lit_check_expr(cx, en); - }, - - PatKind::Mac(_) => bug!("lint must run post-expansion"), - } - true - }); - } -} - declare_lint! { pub UNUSED_DOC_COMMENT, Warn, diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 79f3ff9f19f65..e941f2e4e1c2a 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -43,7 +43,6 @@ extern crate rustc_mir; extern crate syntax_pos; use rustc::lint; -use rustc::middle; use rustc::session; use rustc::util; @@ -107,7 +106,6 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { UnusedParens, UnusedImportBraces, AnonymousParameters, - IllegalFloatLiteralPattern, UnusedDocComment, ); diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 926aa2a724125..be7f1152ea4a0 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -14,9 +14,6 @@ use rustc::hir::map as hir_map; use rustc::ty::subst::Substs; use rustc::ty::{self, AdtKind, Ty, TyCtxt}; use rustc::ty::layout::{self, LayoutOf}; -use middle::const_val::ConstVal; -use rustc_mir::const_eval::ConstContext; -use rustc::mir::interpret::{Value, PrimVal}; use util::nodemap::FxHashSet; use lint::{LateContext, LintContext, LintArray}; use lint::{LintPass, LateLintPass}; @@ -43,12 +40,6 @@ declare_lint! { "literal out of range for its type" } -declare_lint! { - EXCEEDING_BITSHIFTS, - Deny, - "shift exceeds the type's number of bits" -} - declare_lint! { VARIANT_SIZE_DIFFERENCES, Allow, @@ -70,8 +61,7 @@ impl TypeLimits { impl LintPass for TypeLimits { fn get_lints(&self) -> LintArray { lint_array!(UNUSED_COMPARISONS, - OVERFLOWING_LITERALS, - EXCEEDING_BITSHIFTS) + OVERFLOWING_LITERALS) } } @@ -90,59 +80,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { e.span, "comparison is useless due to type limits"); } - - if binop.node.is_shift() { - let opt_ty_bits = match cx.tables.node_id_to_type(l.hir_id).sty { - ty::TyInt(t) => Some(int_ty_bits(t, cx.sess().target.isize_ty)), - ty::TyUint(t) => Some(uint_ty_bits(t, cx.sess().target.usize_ty)), - _ => None, - }; - - if let Some(bits) = opt_ty_bits { - let exceeding = if let hir::ExprLit(ref lit) = r.node { - if let ast::LitKind::Int(shift, _) = lit.node { - shift as u64 >= bits - } else { - false - } - } else { - // HACK(eddyb) This might be quite inefficient. - // This would be better left to MIR constant propagation, - // perhaps even at trans time (like is the case already - // when the value being shifted is *also* constant). - let parent_item = cx.tcx.hir.get_parent(e.id); - let parent_def_id = cx.tcx.hir.local_def_id(parent_item); - let substs = Substs::identity_for_item(cx.tcx, parent_def_id); - let const_cx = ConstContext::new(cx.tcx, - cx.param_env.and(substs), - cx.tables); - match const_cx.eval(&r) { - Ok(&ty::Const { val: ConstVal::Integral(i), .. }) => { - i.is_negative() || - i.to_u64() - .map(|i| i >= bits) - .unwrap_or(true) - } - Ok(&ty::Const { - val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))), - ty, - }) => { - if ty.is_signed() { - (b as i128) < 0 - } else { - b >= bits as u128 - } - } - _ => false, - } - }; - if exceeding { - cx.span_lint(EXCEEDING_BITSHIFTS, - e.span, - "bitshift exceeds the type's number of bits"); - } - }; - } } hir::ExprLit(ref lit) => { match cx.tables.node_id_to_type(e.hir_id).sty { @@ -301,28 +238,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { } } - fn int_ty_bits(int_ty: ast::IntTy, isize_ty: ast::IntTy) -> u64 { - match int_ty { - ast::IntTy::Isize => int_ty_bits(isize_ty, isize_ty), - ast::IntTy::I8 => 8, - ast::IntTy::I16 => 16 as u64, - ast::IntTy::I32 => 32, - ast::IntTy::I64 => 64, - ast::IntTy::I128 => 128, - } - } - - fn uint_ty_bits(uint_ty: ast::UintTy, usize_ty: ast::UintTy) -> u64 { - match uint_ty { - ast::UintTy::Usize => uint_ty_bits(usize_ty, usize_ty), - ast::UintTy::U8 => 8, - ast::UintTy::U16 => 16, - ast::UintTy::U32 => 32, - ast::UintTy::U64 => 64, - ast::UintTy::U128 => 128, - } - } - fn check_limits(cx: &LateContext, binop: hir::BinOp, l: &hir::Expr, diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 1663cab0a592e..77c3eed9e44d2 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -281,8 +281,7 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx let pos = self.position(); match usize::decode(self)? { ::std::usize::MAX => { - let id = interpret_interner().reserve(); - let alloc_id = interpret::AllocId(id); + let alloc_id = interpret_interner().reserve(); trace!("creating alloc id {:?} at {}", alloc_id, pos); // insert early to allow recursive allocs self.interpret_alloc_cache.insert(pos, alloc_id); @@ -290,18 +289,12 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx let allocation = interpret::Allocation::decode(self)?; trace!("decoded alloc {:?} {:#?}", alloc_id, allocation); let allocation = self.tcx.unwrap().intern_const_alloc(allocation); - interpret_interner().intern_at_reserved(id, allocation); + interpret_interner().intern_at_reserved(alloc_id, allocation); let num = usize::decode(self)?; - let ptr = interpret::Pointer { - primval: interpret::PrimVal::Ptr(interpret::MemoryPointer { - alloc_id, - offset: 0, - }), - }; for _ in 0..num { let glob = interpret::GlobalId::decode(self)?; - interpret_interner().cache(glob, ptr); + interpret_interner().cache(glob, alloc_id); } Ok(alloc_id) @@ -310,7 +303,7 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx trace!("creating fn alloc id at {}", pos); let instance = ty::Instance::decode(self)?; trace!("decoded fn alloc instance: {:?}", instance); - let id = interpret::AllocId(interpret_interner().create_fn_alloc(instance)); + let id = interpret_interner().create_fn_alloc(instance); trace!("created fn alloc id: {:?}", id); self.interpret_alloc_cache.insert(pos, id); Ok(id) diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 1e1baba2bac5e..f5631f5fab91f 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -199,26 +199,21 @@ impl<'a, 'tcx> SpecializedEncoder for EncodeContext<'a, 'tcx // point to itself. self.interpret_alloc_shorthands.insert(*alloc_id, start); let interpret_interner = self.tcx.interpret_interner.borrow(); - if let Some(alloc) = interpret_interner.get_alloc(alloc_id.0) { + if let Some(alloc) = interpret_interner.get_alloc(*alloc_id) { trace!("encoding {:?} with {:#?}", alloc_id, alloc); usize::max_value().encode(self)?; alloc.encode(self)?; - let globals = interpret_interner.get_globals(interpret::Pointer { - primval: interpret::PrimVal::Ptr(interpret::MemoryPointer { - alloc_id: *alloc_id, - offset: 0, - }), - }); + let globals = interpret_interner.get_globals(*alloc_id); globals.len().encode(self)?; for glob in globals { glob.encode(self)?; } - } else if let Some(fn_instance) = interpret_interner.get_fn(alloc_id.0) { + } else if let Some(fn_instance) = interpret_interner.get_fn(*alloc_id) { trace!("encoding {:?} with {:#?}", alloc_id, fn_instance); (usize::max_value() - 1).encode(self)?; fn_instance.encode(self)?; } else { - bug!("alloc id without corresponding allocation: {}", alloc_id.0); + bug!("alloc id without corresponding allocation: {}", alloc_id); } Ok(()) } diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 5955d0ca59adf..182ed52961929 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -260,12 +260,11 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { // would be lost if we just look at the normalized // value. let did = match value.val { - ConstVal::Function(def_id, ..) => Some(def_id), ConstVal::Value(Value::ByVal(PrimVal::Ptr(p))) => { self.tcx() .interpret_interner .borrow() - .get_fn(p.alloc_id.0) + .get_fn(p.alloc_id) .map(|instance| instance.def_id()) }, ConstVal::Value(Value::ByVal(PrimVal::Undef)) => { @@ -1044,11 +1043,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { }, .. }) => match val { - ConstVal::Function(def_id, _) => { - Some(def_id) == self.tcx().lang_items().box_free_fn() - }, ConstVal::Value(Value::ByVal(PrimVal::Ptr(p))) => { - let inst = self.tcx().interpret_interner.borrow().get_fn(p.alloc_id.0); + let inst = self.tcx().interpret_interner.borrow().get_fn(p.alloc_id); inst.map_or(false, |inst| { Some(inst.def_id()) == self.tcx().lang_items().box_free_fn() }) diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index b5b8f8d7e78b0..ca6e7c2c41580 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -204,11 +204,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { ty: this.hir.tcx().types.u32, literal: Literal::Value { value: this.hir.tcx().mk_const(ty::Const { - val: if this.hir.tcx().sess.opts.debugging_opts.miri { - ConstVal::Value(Value::ByVal(PrimVal::Bytes(0))) - } else { - ConstVal::Integral(ConstInt::U32(0)) - }, + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(0))), ty: this.hir.tcx().types.u32 }), }, @@ -406,11 +402,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { Literal::Value { value: self.hir.tcx().mk_const(ty::Const { - val: if self.hir.tcx().sess.opts.debugging_opts.miri { - ConstVal::Value(Value::ByVal(PrimVal::Bytes(val.to_u128_unchecked()))) - } else { - ConstVal::Integral(val) - }, + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(val.to_u128_unchecked()))), ty }) } @@ -448,13 +440,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { Literal::Value { value: self.hir.tcx().mk_const(ty::Const { - val: if self.hir.tcx().sess.opts.debugging_opts.miri { - ConstVal::Value(Value::ByVal(PrimVal::Bytes( - val.to_u128_unchecked() - ))) - } else { - ConstVal::Integral(val) - }, + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes( + val.to_u128_unchecked() + ))), ty }) } diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index fafdee5b1e1b9..30c70ac30a70b 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -299,7 +299,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let mut val = Operand::Copy(place.clone()); let bytes = match value.val { - ConstVal::ByteStr(bytes) => Some(bytes.data), ConstVal::Value(Value::ByVal(PrimVal::Ptr(p))) => { let is_array_ptr = ty .builtin_deref(true, ty::NoPreference) @@ -310,7 +309,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { .tcx() .interpret_interner .borrow() - .get_alloc(p.alloc_id.0) + .get_alloc(p.alloc_id) .map(|alloc| &alloc.bytes[..]) } else { None diff --git a/src/librustc_mir/build/misc.rs b/src/librustc_mir/build/misc.rs index efb367201189b..78c7004ef6034 100644 --- a/src/librustc_mir/build/misc.rs +++ b/src/librustc_mir/build/misc.rs @@ -13,13 +13,11 @@ use build::Builder; -use rustc_const_math::{ConstInt, ConstUsize, ConstIsize}; use rustc::middle::const_val::ConstVal; use rustc::ty::{self, Ty}; use rustc::mir::interpret::{Value, PrimVal}; use rustc::mir::*; -use syntax::ast; use syntax_pos::{Span, DUMMY_SP}; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { @@ -63,61 +61,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { ty::TyChar => { Literal::Value { value: self.hir.tcx().mk_const(ty::Const { - val: if self.hir.tcx().sess.opts.debugging_opts.miri { - ConstVal::Value(Value::ByVal(PrimVal::Bytes(0))) - } else { - ConstVal::Char('\0') - }, + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(0))), ty }) } } - ty::TyUint(ity) => { - let val = match ity { - ast::UintTy::U8 => ConstInt::U8(0), - ast::UintTy::U16 => ConstInt::U16(0), - ast::UintTy::U32 => ConstInt::U32(0), - ast::UintTy::U64 => ConstInt::U64(0), - ast::UintTy::U128 => ConstInt::U128(0), - ast::UintTy::Usize => { - let uint_ty = self.hir.tcx().sess.target.usize_ty; - let val = ConstUsize::new(0, uint_ty).unwrap(); - ConstInt::Usize(val) - } - }; - - 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 }) } diff --git a/src/librustc_mir/const_eval/_match.rs b/src/librustc_mir/const_eval/_match.rs index 9ebbc11162812..2057b0a5b4f7c 100644 --- a/src/librustc_mir/const_eval/_match.rs +++ b/src/librustc_mir/const_eval/_match.rs @@ -15,8 +15,6 @@ use self::WitnessPreference::*; use rustc::middle::const_val::ConstVal; use const_eval::eval::{compare_const_vals}; -use rustc_const_math::ConstInt; - use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::indexed_vec::Idx; @@ -182,20 +180,6 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { let tcx = self.tcx; self.byte_array_map.entry(pat).or_insert_with(|| { match pat.kind { - box PatternKind::Constant { - value: &ty::Const { val: ConstVal::ByteStr(b), .. } - } => { - b.data.iter().map(|&b| &*pattern_arena.alloc(Pattern { - ty: tcx.types.u8, - span: pat.span, - kind: box PatternKind::Constant { - value: tcx.mk_const(ty::Const { - val: ConstVal::Integral(ConstInt::U8(b)), - ty: tcx.types.u8 - }) - } - })).collect() - } box PatternKind::Constant { value: &ty::Const { val: ConstVal::Value(b), ty } } => { @@ -209,7 +193,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { let alloc = tcx .interpret_interner .borrow() - .get_alloc(ptr.alloc_id.0) + .get_alloc(ptr.alloc_id) .unwrap(); assert_eq!(ptr.offset, 0); // FIXME: check length @@ -458,11 +442,7 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, ty::TyBool => { [true, false].iter().map(|&b| { ConstantValue(cx.tcx.mk_const(ty::Const { - val: if cx.tcx.sess.opts.debugging_opts.miri { - ConstVal::Value(Value::ByVal(PrimVal::Bytes(b as u128))) - } else { - ConstVal::Bool(b) - }, + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b as u128))), ty: cx.tcx.types.bool })) }).collect() @@ -575,9 +555,6 @@ fn max_slice_length<'p, 'a: 'p, 'tcx: 'a, I>( for row in patterns { match *row.kind { - PatternKind::Constant { value: &ty::Const { val: ConstVal::ByteStr(b), .. } } => { - max_fixed_len = cmp::max(max_fixed_len, b.data.len() as u64); - } PatternKind::Constant { value: &ty::Const { val: ConstVal::Value(Value::ByVal(PrimVal::Ptr(ptr))), @@ -592,7 +569,7 @@ fn max_slice_length<'p, 'a: 'p, 'tcx: 'a, I>( let alloc = cx.tcx .interpret_interner .borrow() - .get_alloc(ptr.alloc_id.0) + .get_alloc(ptr.alloc_id) .unwrap(); max_fixed_len = cmp::max(max_fixed_len, alloc.bytes.len() as u64); } @@ -971,7 +948,6 @@ fn slice_pat_covered_by_constructor(tcx: TyCtxt, _span: Span, suffix: &[Pattern]) -> Result { let data: &[u8] = match *ctor { - ConstantValue(&ty::Const { val: ConstVal::ByteStr(b), .. }) => b.data, ConstantValue(&ty::Const { val: ConstVal::Value( Value::ByVal(PrimVal::Ptr(ptr)) ), ty }) => { @@ -983,7 +959,7 @@ fn slice_pat_covered_by_constructor(tcx: TyCtxt, _span: Span, tcx .interpret_interner .borrow() - .get_alloc(ptr.alloc_id.0) + .get_alloc(ptr.alloc_id) .unwrap() .bytes .as_ref() @@ -1002,11 +978,6 @@ fn slice_pat_covered_by_constructor(tcx: TyCtxt, _span: Span, { match pat.kind { box PatternKind::Constant { value } => match value.val { - ConstVal::Integral(ConstInt::U8(u)) => { - if u != *ch { - return Ok(false); - } - }, ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))) => { assert_eq!(b as u8 as u128, b); if b as u8 != *ch { @@ -1120,13 +1091,6 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>( PatternKind::Constant { value } => { match *constructor { Slice(..) => match value.val { - ConstVal::ByteStr(b) => { - if wild_patterns.len() == b.data.len() { - Some(cx.lower_byte_str_pattern(pat)) - } else { - None - } - } ConstVal::Value(Value::ByVal(PrimVal::Ptr(ptr))) => { let is_array_ptr = value.ty .builtin_deref(true, ty::NoPreference) @@ -1136,7 +1100,7 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>( let data_len = cx.tcx .interpret_interner .borrow() - .get_alloc(ptr.alloc_id.0) + .get_alloc(ptr.alloc_id) .unwrap() .bytes .len(); diff --git a/src/librustc_mir/const_eval/check.rs b/src/librustc_mir/const_eval/check.rs new file mode 100644 index 0000000000000..70435c16ff72e --- /dev/null +++ b/src/librustc_mir/const_eval/check.rs @@ -0,0 +1,167 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , 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 { + 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"); + } + } + _ => {} + } + } +} diff --git a/src/librustc_mir/const_eval/eval.rs b/src/librustc_mir/const_eval/eval.rs index 58fe40d12be84..370b8681ba648 100644 --- a/src/librustc_mir/const_eval/eval.rs +++ b/src/librustc_mir/const_eval/eval.rs @@ -9,41 +9,18 @@ // except according to those terms. use rustc::middle::const_val::ConstVal::*; -use rustc::middle::const_val::ConstAggregate::*; use rustc::middle::const_val::ErrKind::*; -use rustc::middle::const_val::{ByteArray, ConstVal, ConstEvalErr, EvalResult, ErrKind}; +use rustc::middle::const_val::{ConstVal, ErrKind}; -use rustc::hir::map::blocks::FnLikeNode; -use rustc::hir::def::{Def, CtorKind}; use rustc::hir::def_id::DefId; use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::util::IntTypeExt; -use rustc::ty::subst::{Substs, Subst}; -use rustc::util::common::ErrorReported; -use rustc::util::nodemap::NodeMap; +use rustc::ty::subst::Substs; -use syntax::abi::Abi; use syntax::ast; -use syntax::attr; -use rustc::hir::{self, Expr}; use std::cmp::Ordering; use rustc_const_math::*; -macro_rules! signal { - ($e:expr, $exn:expr) => { - return Err(ConstEvalErr { span: $e.span, kind: $exn }) - } -} - -macro_rules! math { - ($e:expr, $op:expr) => { - match $op { - Ok(val) => val, - Err(e) => signal!($e, ErrKind::from(e)), - } - } -} /// * `DefId` is the id of the constant. /// * `Substs` is the monomorphized substitutions for the expression. @@ -58,591 +35,94 @@ pub fn lookup_const_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ).map(|instance| (instance.def_id(), instance.substs)) } -pub struct ConstContext<'a, 'tcx: 'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, - tables: &'a ty::TypeckTables<'tcx>, - param_env: ty::ParamEnv<'tcx>, - substs: &'tcx Substs<'tcx>, - fn_args: Option>> -} - -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, 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::>(); - 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::, _>>()?; - 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::, _>>()?)))) - } - 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::, _>>()?; - 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, 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) @@ -657,41 +137,26 @@ pub fn compare_const_vals(a: &ConstVal, b: &ConstVal, ty: Ty) -> Option 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, 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)) - } -} diff --git a/src/librustc_mir/const_eval/mod.rs b/src/librustc_mir/const_eval/mod.rs index 27356c2b0821f..f47dc61c27c4d 100644 --- a/src/librustc_mir/const_eval/mod.rs +++ b/src/librustc_mir/const_eval/mod.rs @@ -14,5 +14,6 @@ mod eval; mod _match; pub mod check_match; pub mod pattern; +pub mod check; pub use self::eval::*; diff --git a/src/librustc_mir/const_eval/pattern.rs b/src/librustc_mir/const_eval/pattern.rs index 48822c506aef2..48438e7dbfd58 100644 --- a/src/librustc_mir/const_eval/pattern.rs +++ b/src/librustc_mir/const_eval/pattern.rs @@ -8,10 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use const_eval::eval; use interpret::{const_val_field, const_discr}; -use rustc::middle::const_val::{ConstEvalErr, ConstVal, ConstAggregate}; +use rustc::middle::const_val::{ConstEvalErr, ErrKind, ConstVal}; use rustc::mir::{Field, BorrowKind, Mutability}; use rustc::mir::interpret::{Value, PrimVal}; use rustc::ty::{self, TyCtxt, AdtDef, Ty, Region}; @@ -19,6 +18,7 @@ use rustc::ty::subst::{Substs, Kind}; use rustc::hir::{self, PatKind, RangeEnd}; use rustc::hir::def::{Def, CtorKind}; use rustc::hir::pat_util::EnumerateAndAdjustIterator; +use const_eval::eval::compare_const_vals; use rustc_data_structures::indexed_vec::Idx; @@ -114,16 +114,7 @@ pub enum PatternKind<'tcx> { fn print_const_val(value: &ty::Const, f: &mut fmt::Formatter) -> fmt::Result { match value.val { - ConstVal::Float(ref x) => write!(f, "{}", x), - ConstVal::Integral(ref i) => write!(f, "{}", i), - ConstVal::Str(ref s) => write!(f, "{:?}", &s[..]), - ConstVal::ByteStr(b) => write!(f, "{:?}", b.data), - ConstVal::Bool(b) => write!(f, "{:?}", b), - ConstVal::Char(c) => write!(f, "{:?}", c), ConstVal::Value(v) => print_miri_value(v, value.ty, f), - ConstVal::Variant(_) | - ConstVal::Function(..) | - ConstVal::Aggregate(_) | ConstVal::Unevaluated(..) => bug!("{:?} not printable in a pattern", value) } } @@ -366,10 +357,27 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { PatKind::Lit(ref value) => self.lower_lit(value), - PatKind::Range(ref lo, ref hi, end) => { - match (self.lower_lit(lo), self.lower_lit(hi)) { + PatKind::Range(ref lo_expr, ref hi_expr, end) => { + match (self.lower_lit(lo_expr), self.lower_lit(hi_expr)) { (PatternKind::Constant { value: lo }, PatternKind::Constant { value: hi }) => { + use std::cmp::Ordering; + match (end, compare_const_vals(&lo.val, &hi.val, ty).unwrap()) { + (RangeEnd::Excluded, Ordering::Less) => {}, + (RangeEnd::Excluded, _) => span_err!( + self.tcx.sess, + lo_expr.span, + E0579, + "lower range bound must be less than upper", + ), + (RangeEnd::Included, Ordering::Greater) => { + struct_span_err!(self.tcx.sess, lo_expr.span, E0030, + "lower range bound must be less than or equal to upper") + .span_label(lo_expr.span, "lower bound larger than upper bound") + .emit(); + }, + (RangeEnd::Included, _) => {} + } PatternKind::Range { lo, hi, end } } _ => PatternKind::Wild @@ -487,7 +495,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { pattern: self.lower_pattern(field), }) .collect(); - self.lower_variant_or_leaf(def, ty, subpatterns) + self.lower_variant_or_leaf(def, pat.span, ty, subpatterns) } PatKind::Struct(ref qpath, ref fields, _) => { @@ -519,7 +527,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { }) .collect(); - self.lower_variant_or_leaf(def, ty, subpatterns) + self.lower_variant_or_leaf(def, pat.span, ty, subpatterns) } }; @@ -610,6 +618,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { fn lower_variant_or_leaf( &mut self, def: Def, + span: Span, ty: Ty<'tcx>, subpatterns: Vec>) -> PatternKind<'tcx> @@ -640,7 +649,13 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { PatternKind::Leaf { subpatterns: subpatterns } } - _ => bug!() + _ => { + self.errors.push(PatternError::ConstEval(ConstEvalErr { + span, + kind: ErrKind::NonConstPath, + })); + PatternKind::Wild + } } } @@ -660,18 +675,13 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { let substs = self.tables.node_substs(id); match self.tcx.at(span).const_eval(self.param_env.and((def_id, substs))) { Ok(value) => { - if self.tcx.sess.opts.debugging_opts.miri { - if let ConstVal::Value(_) = value.val {} else { - panic!("const eval produced non-miri value: {:#?}", value); - } - } let instance = ty::Instance::resolve( self.tcx, self.param_env, def_id, substs, ).unwrap(); - return self.const_to_pat(instance, value, span) + return self.const_to_pat(instance, value, id, span) }, Err(e) => { self.errors.push(PatternError::ConstEval(e)); @@ -679,7 +689,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } } } - _ => self.lower_variant_or_leaf(def, ty, vec![]), + _ => self.lower_variant_or_leaf(def, span, ty, vec![]), }; Pattern { @@ -690,68 +700,51 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } fn lower_lit(&mut self, expr: &'tcx hir::Expr) -> PatternKind<'tcx> { - if self.tcx.sess.opts.debugging_opts.miri { - return match expr.node { - hir::ExprLit(ref lit) => { - let ty = self.tables.expr_ty(expr); - match super::eval::lit_to_const(&lit.node, self.tcx, ty, false) { - Ok(value) => PatternKind::Constant { - value: self.tcx.mk_const(ty::Const { - ty, - val: value, - }), - }, - Err(e) => { - self.errors.push(PatternError::ConstEval(ConstEvalErr { - span: lit.span, - kind: e, - })); - PatternKind::Wild - }, - } - }, - hir::ExprPath(ref qpath) => *self.lower_path(qpath, expr.hir_id, expr.span).kind, - hir::ExprUnary(hir::UnNeg, ref expr) => { - let ty = self.tables.expr_ty(expr); - let lit = match expr.node { - hir::ExprLit(ref lit) => lit, - _ => span_bug!(expr.span, "not a literal: {:?}", expr), - }; - match super::eval::lit_to_const(&lit.node, self.tcx, ty, true) { - Ok(value) => PatternKind::Constant { - value: self.tcx.mk_const(ty::Const { - ty, - val: value, - }), - }, - Err(e) => { - self.errors.push(PatternError::ConstEval(ConstEvalErr { - span: lit.span, - kind: e, - })); - PatternKind::Wild - }, - } + match expr.node { + hir::ExprLit(ref lit) => { + let ty = self.tables.expr_ty(expr); + match super::eval::lit_to_const(&lit.node, self.tcx, ty, false) { + Ok(val) => { + let instance = ty::Instance::new( + self.tables.local_id_root.expect("literal outside any scope"), + self.substs, + ); + let cv = self.tcx.mk_const(ty::Const { val, ty }); + *self.const_to_pat(instance, cv, expr.hir_id, lit.span).kind + }, + Err(e) => { + self.errors.push(PatternError::ConstEval(ConstEvalErr { + span: lit.span, + kind: e, + })); + PatternKind::Wild + }, } - _ => span_bug!(expr.span, "not a literal: {:?}", expr), - } - } - let const_cx = eval::ConstContext::new(self.tcx, - self.param_env.and(self.substs), - self.tables); - match const_cx.eval(expr) { - Ok(value) => { - if let ConstVal::Variant(def_id) = value.val { - let ty = self.tables.expr_ty(expr); - self.lower_variant_or_leaf(Def::Variant(def_id), ty, vec![]) - } else { - PatternKind::Constant { value } + }, + hir::ExprPath(ref qpath) => *self.lower_path(qpath, expr.hir_id, expr.span).kind, + hir::ExprUnary(hir::UnNeg, ref expr) => { + let ty = self.tables.expr_ty(expr); + let lit = match expr.node { + hir::ExprLit(ref lit) => lit, + _ => span_bug!(expr.span, "not a literal: {:?}", expr), + }; + match super::eval::lit_to_const(&lit.node, self.tcx, ty, true) { + Ok(value) => PatternKind::Constant { + value: self.tcx.mk_const(ty::Const { + ty, + val: value, + }), + }, + Err(e) => { + self.errors.push(PatternError::ConstEval(ConstEvalErr { + span: lit.span, + kind: e, + })); + PatternKind::Wild + }, } } - Err(e) => { - self.errors.push(PatternError::ConstEval(e)); - PatternKind::Wild - } + _ => span_bug!(expr.span, "not a literal: {:?}", expr), } } @@ -759,14 +752,23 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { &self, instance: ty::Instance<'tcx>, cv: &'tcx ty::Const<'tcx>, + id: hir::HirId, span: Span, ) -> Pattern<'tcx> { debug!("const_to_pat: cv={:#?}", cv); let kind = match cv.ty.sty { ty::TyFloat(_) => { - self.tcx.sess.span_err(span, "floating point constants cannot be used in patterns"); - PatternKind::Wild - } + let id = self.tcx.hir.hir_to_node_id(id); + self.tcx.lint_node( + ::rustc::lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, + id, + span, + "floating-point types cannot be used in patterns", + ); + PatternKind::Constant { + value: cv, + } + }, ty::TyAdt(adt_def, _) if adt_def.is_union() => { // Matching on union fields is unsafe, we can't hide it in constants self.tcx.sess.span_err(span, "cannot use unions in constant patterns"); @@ -803,30 +805,18 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { let field = Field::new(i); let val = match cv.val { ConstVal::Value(miri) => const_val_field( - self.tcx, self.param_env, instance, Some(variant_index), field, miri, cv.ty, + self.tcx, self.param_env, instance, + Some(variant_index), field, miri, cv.ty, ).unwrap(), _ => bug!("{:#?} is not a valid tuple", cv), }; FieldPattern { field, - pattern: self.const_to_pat(instance, val, span), + pattern: self.const_to_pat(instance, val, id, span), } }).collect(), } }, - ConstVal::Variant(var_did) => { - let variant_index = adt_def - .variants - .iter() - .position(|var| var.did == var_did) - .unwrap(); - PatternKind::Variant { - adt_def, - substs, - variant_index, - subpatterns: Vec::new(), - } - } _ => return Pattern { span, ty: cv.ty, @@ -839,12 +829,9 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { ty::TyAdt(adt_def, _) => { let struct_var = adt_def.struct_variant(); PatternKind::Leaf { - subpatterns: struct_var.fields.iter().enumerate().map(|(i, f)| { + subpatterns: struct_var.fields.iter().enumerate().map(|(i, _)| { let field = Field::new(i); let val = match cv.val { - ConstVal::Aggregate(ConstAggregate::Struct(consts)) => { - consts.iter().find(|&&(name, _)| name == f.name).unwrap().1 - }, ConstVal::Value(miri) => const_val_field( self.tcx, self.param_env, instance, None, field, miri, cv.ty, ).unwrap(), @@ -852,7 +839,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { }; FieldPattern { field, - pattern: self.const_to_pat(instance, val, span), + pattern: self.const_to_pat(instance, val, id, span), } }).collect() } @@ -862,7 +849,6 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { subpatterns: (0..fields.len()).map(|i| { let field = Field::new(i); let val = match cv.val { - ConstVal::Aggregate(ConstAggregate::Tuple(consts)) => consts[i], ConstVal::Value(miri) => const_val_field( self.tcx, self.param_env, instance, None, field, miri, cv.ty, ).unwrap(), @@ -870,29 +856,26 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { }; FieldPattern { field, - pattern: self.const_to_pat(instance, val, span), + pattern: self.const_to_pat(instance, val, id, span), } }).collect() } } ty::TyArray(_, n) => { - PatternKind::Leaf { - subpatterns: (0..n.val.unwrap_u64()).map(|i| { + PatternKind::Array { + prefix: (0..n.val.unwrap_u64()).map(|i| { let i = i as usize; let field = Field::new(i); let val = match cv.val { - ConstVal::Aggregate(ConstAggregate::Array(consts)) => consts[i], - ConstVal::Aggregate(ConstAggregate::Repeat(cv, _)) => cv, ConstVal::Value(miri) => const_val_field( self.tcx, self.param_env, instance, None, field, miri, cv.ty, ).unwrap(), _ => bug!("{:#?} is not a valid tuple", cv), }; - FieldPattern { - field, - pattern: self.const_to_pat(instance, val, span), - } - }).collect() + self.const_to_pat(instance, val, id, span) + }).collect(), + slice: None, + suffix: Vec::new(), } } _ => { diff --git a/src/librustc_mir/diagnostics.rs b/src/librustc_mir/diagnostics.rs index d71298fb1a6f5..f253327664597 100644 --- a/src/librustc_mir/diagnostics.rs +++ b/src/librustc_mir/diagnostics.rs @@ -325,6 +325,24 @@ match x { ``` "##, +E0030: r##" +When matching against a range, the compiler verifies that the range is +non-empty. Range patterns include both end-points, so this is equivalent to +requiring the start of the range to be less than or equal to the end of the +range. + +For example: + +```compile_fail +match 5u32 { + // This range is ok, albeit pointless. + 1 ... 1 => {} + // This range is empty, and the compiler can tell. + 1000 ... 5 => {} +} +``` +"##, + E0158: r##" `const` and `static` mean different things. A `const` is a compile-time constant, an alias for a literal value. This property means you can match it @@ -2160,6 +2178,24 @@ fn main() { ``` "##, +E0579: r##" +When matching against an exclusive range, the compiler verifies that the range +is non-empty. Exclusive range patterns include the start point but not the end +point, so this is equivalent to requiring the start of the range to be less +than the end of the range. + +For example: + +```compile_fail +match 5u32 { + // This range is ok, albeit pointless. + 1 .. 2 => {} + // This range is empty, and the compiler can tell. + 5 .. 5 => {} +} +``` +"##, + E0595: r##" Closures cannot mutate immutable captured variables. diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index 198e55358e7ee..e65f8f3f683f1 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -637,7 +637,7 @@ fn method_callee<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, kind: ExprKind::Literal { literal: Literal::Value { value: cx.tcx().mk_const(ty::Const { - val: const_fn(cx.tcx, def_id, substs), + val: ConstVal::Value(Value::ByVal(PrimVal::Undef)), ty }), }, @@ -677,28 +677,6 @@ fn convert_arm<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, arm: &'tcx hir::Arm) } } -fn const_fn<'a, 'gcx, 'tcx>( - tcx: TyCtxt<'a, 'gcx, 'tcx>, - def_id: DefId, - substs: &'tcx Substs<'tcx>, -) -> ConstVal<'tcx> { - if tcx.sess.opts.debugging_opts.miri { - /* - let inst = ty::Instance::new(def_id, substs); - let ptr = tcx - .interpret_interner - .borrow_mut() - .create_fn_alloc(inst); - let ptr = MemoryPointer::new(AllocId(ptr), 0); - ConstVal::Value(Value::ByVal(PrimVal::Ptr(ptr))) - */ - // ZST function type - ConstVal::Value(Value::ByVal(PrimVal::Undef)) - } else { - ConstVal::Function(def_id, substs) - } -} - fn convert_path_expr<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, expr: &'tcx hir::Expr, def: Def) @@ -706,13 +684,13 @@ fn convert_path_expr<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, let substs = cx.tables().node_substs(expr.hir_id); match def { // A regular function, constructor function or a constant. - Def::Fn(def_id) | - Def::Method(def_id) | - Def::StructCtor(def_id, CtorKind::Fn) | - Def::VariantCtor(def_id, CtorKind::Fn) => ExprKind::Literal { + Def::Fn(_) | + Def::Method(_) | + Def::StructCtor(_, CtorKind::Fn) | + Def::VariantCtor(_, CtorKind::Fn) => ExprKind::Literal { literal: Literal::Value { value: cx.tcx.mk_const(ty::Const { - val: const_fn(cx.tcx.global_tcx(), def_id, substs), + val: ConstVal::Value(Value::ByVal(PrimVal::Undef)), ty: cx.tables().node_id_to_type(expr.hir_id) }), }, diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index 7ebed0bbddbf5..6fda4703d1cbc 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -25,7 +25,7 @@ use rustc::infer::InferCtxt; use rustc::ty::subst::Subst; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::subst::Substs; -use syntax::ast; +use syntax::ast::{self, LitKind}; use syntax::attr; use syntax::symbol::Symbol; use rustc::hir; @@ -119,11 +119,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { Ok(val) => { Literal::Value { value: self.tcx.mk_const(ty::Const { - val: if self.tcx.sess.opts.debugging_opts.miri { - ConstVal::Value(Value::ByVal(PrimVal::Bytes(value as u128))) - } else { - ConstVal::Integral(ConstInt::Usize(val)) - }, + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(val.as_u64() as u128))), ty: self.tcx.types.usize }) } @@ -143,11 +139,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { pub fn true_literal(&mut self) -> Literal<'tcx> { Literal::Value { value: self.tcx.mk_const(ty::Const { - val: if self.tcx.sess.opts.debugging_opts.miri { - ConstVal::Value(Value::ByVal(PrimVal::Bytes(1))) - } else { - ConstVal::Bool(true) - }, + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(1))), ty: self.tcx.types.bool }) } @@ -156,11 +148,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { pub fn false_literal(&mut self) -> Literal<'tcx> { Literal::Value { value: self.tcx.mk_const(ty::Const { - val: if self.tcx.sess.opts.debugging_opts.miri { - ConstVal::Value(Value::ByVal(PrimVal::Bytes(0))) - } else { - ConstVal::Bool(false) - }, + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(0))), ty: self.tcx.types.bool }) } @@ -175,13 +163,6 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { ) -> Literal<'tcx> { let tcx = self.tcx.global_tcx(); - let mut repr_ty = ty; - if let ty::TyAdt(adt, _) = ty.sty { - if adt.is_enum() { - repr_ty = adt.repr.discr_type().to_ty(tcx) - } - } - let parse_float = |num: &str, fty| -> ConstFloat { ConstFloat::from_str(num, fty).unwrap_or_else(|_| { // FIXME(#31407) this is only necessary because float parsing is buggy @@ -189,128 +170,59 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { }) }; - if tcx.sess.opts.debugging_opts.miri { - use rustc::mir::interpret::*; - let lit = match *lit { - LitKind::Str(ref s, _) => { - let s = s.as_str(); - let id = self.tcx.allocate_cached(s.as_bytes()); - let ptr = MemoryPointer::new(AllocId(id), 0); - Value::ByValPair( - PrimVal::Ptr(ptr), - PrimVal::from_u128(s.len() as u128), - ) - }, - LitKind::ByteStr(ref data) => { - let id = self.tcx.allocate_cached(data); - let ptr = MemoryPointer::new(AllocId(id), 0); - Value::ByVal(PrimVal::Ptr(ptr)) - }, - LitKind::Byte(n) => Value::ByVal(PrimVal::Bytes(n as u128)), - LitKind::Int(n, _) if neg => { - let n = n as i128; - let n = n.overflowing_neg().0; - Value::ByVal(PrimVal::Bytes(n as u128)) - }, - LitKind::Int(n, _) => Value::ByVal(PrimVal::Bytes(n)), - LitKind::Float(n, fty) => { - let n = n.as_str(); - let mut f = parse_float(&n, fty); - if neg { - f = -f; - } - let bits = f.bits; - Value::ByVal(PrimVal::Bytes(bits)) - } - LitKind::FloatUnsuffixed(n) => { - let fty = match ty.sty { - ty::TyFloat(fty) => fty, - _ => bug!() - }; - let n = n.as_str(); - let mut f = parse_float(&n, fty); - if neg { - f = -f; - } - let bits = f.bits; - Value::ByVal(PrimVal::Bytes(bits)) - } - LitKind::Bool(b) => Value::ByVal(PrimVal::Bytes(b as u128)), - LitKind::Char(c) => Value::ByVal(PrimVal::Bytes(c as u128)), - }; - return Literal::Value { - value: self.tcx.mk_const(ty::Const { - val: Value(lit), - ty, - }), - }; - } - - use syntax::ast::*; - use syntax::ast::LitIntType::*; - use rustc::middle::const_val::ConstVal::*; - use rustc_const_math::ConstInt::*; - use rustc::ty::util::IntTypeExt; - use rustc::middle::const_val::ByteArray; - use rustc_const_math::ConstFloat; - + use rustc::mir::interpret::*; let lit = match *lit { - LitKind::Str(ref s, _) => Ok(Str(s.as_str())), + LitKind::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") + }), } } @@ -352,12 +264,8 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { return (method_ty, Literal::Value { value: self.tcx.mk_const(ty::Const { - val: if self.tcx.sess.opts.debugging_opts.miri { - // ZST function type - ConstVal::Value(Value::ByVal(PrimVal::Undef)) - } else { - ConstVal::Function(item.def_id, substs) - }, + // ZST function type + val: ConstVal::Value(Value::ByVal(PrimVal::Undef)), ty: method_ty }), }); diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index 3a9149b44bba7..6929bccaa9500 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -5,17 +5,13 @@ use rustc::hir::def_id::DefId; use rustc::mir; use rustc::middle::const_val::ErrKind::{CheckMatchError, TypeckError}; use rustc::middle::const_val::{ConstEvalErr, ConstVal}; -use const_eval::{lookup_const_by_id, ConstContext}; -use rustc::mir::Field; -use rustc_data_structures::indexed_vec::Idx; +use const_eval::lookup_const_by_id; use syntax::ast::Mutability; use syntax::codemap::Span; use rustc::mir::interpret::{EvalResult, EvalError, EvalErrorKind, GlobalId, Value, MemoryPointer, Pointer, PrimVal}; -use super::{Place, EvalContext, StackPopCleanup, ValTy, HasMemory, PlaceExtra}; - -use rustc_const_math::ConstInt; +use super::{Place, EvalContext, StackPopCleanup, ValTy, PlaceExtra}; use std::fmt; use std::error::Error; @@ -43,93 +39,89 @@ pub fn mk_eval_cx<'a, 'tcx>( pub fn eval_body<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>, + promoted: Option, 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, + 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, + 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, +) -> EvalResult<'tcx, u128> { + let (value, _, ty) = eval_body(tcx, instance, promoted, param_env)?; + match value { + Value::ByVal(prim) => prim.to_bytes(), + _ => err!(TypeNotPrimitive(ty)), + } } pub struct CompileTimeEvaluator; @@ -337,7 +329,7 @@ fn const_val_field_inner<'a, 'tcx>( trace!("const_val_field: {:?}, {:?}, {:?}, {:?}", instance, field, value, ty); let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap(); let (mut field, ty) = match value { - Value::ByValPair(..) | Value::ByVal(_) => ecx.read_field(value, field, ty)?.expect("const_val_field on non-field"), + Value::ByValPair(..) | Value::ByVal(_) => ecx.read_field(value, variant, field, ty)?.expect("const_val_field on non-field"), Value::ByRef(ptr, align) => { let place = Place::Ptr { ptr, @@ -422,248 +414,13 @@ pub fn const_eval_provider<'a, 'tcx>( let instance = ty::Instance::new(def_id, substs); - if tcx.sess.opts.debugging_opts.miri { - return match ::interpret::eval_body(tcx, instance, key.param_env) { - Ok((miri_value, _, miri_ty)) => Ok(tcx.mk_const(ty::Const { - val: ConstVal::Value(miri_value), - ty: miri_ty, - })), - Err(err) => { - Err(ConstEvalErr { span: body.value.span, kind: err.into() }) - } - }; - } - - trace!("running old const eval"); - let old_result = ConstContext::new(tcx, key.param_env.and(substs), tables).eval(&body.value); - trace!("old const eval produced {:?}", old_result); - trace!("const eval instance: {:?}, {:?}", instance, key.param_env); - let miri_result = ::interpret::eval_body(tcx, instance, key.param_env); - match (miri_result, old_result) { - (Err(err), Ok(ok)) => { - trace!("miri failed, ctfe returned {:?}", ok); - tcx.sess.span_warn( - tcx.def_span(key.value.0), - "miri failed to eval, while ctfe succeeded", - ); - let ecx = mk_eval_cx(tcx, instance, key.param_env).unwrap(); - let () = unwrap_miri(&ecx, Err(err)); - Ok(ok) - }, - (Ok((value, _, ty)), Err(_)) => Ok(tcx.mk_const(ty::Const { - val: ConstVal::Value(value), - ty, + match ::interpret::eval_body(tcx, instance, None, key.param_env) { + Ok((miri_value, _, miri_ty)) => Ok(tcx.mk_const(ty::Const { + val: ConstVal::Value(miri_value), + ty: miri_ty, })), - (Err(_), Err(err)) => Err(err), - (Ok((_, miri_ptr, miri_ty)), Ok(ctfe)) => { - let mut ecx = mk_eval_cx(tcx, instance, key.param_env).unwrap(); - let layout = ecx.layout_of(miri_ty).unwrap(); - let miri_place = Place::from_primval_ptr(miri_ptr, layout.align); - check_ctfe_against_miri(&mut ecx, miri_place, miri_ty, ctfe.val); - Ok(ctfe) - } - } -} - -fn check_ctfe_against_miri<'a, 'tcx>( - ecx: &mut EvalContext<'a, 'tcx, CompileTimeEvaluator>, - miri_place: Place, - miri_ty: Ty<'tcx>, - ctfe: ConstVal<'tcx>, -) { - use rustc::middle::const_val::ConstAggregate::*; - use rustc_const_math::ConstFloat; - use rustc::ty::TypeVariants::*; - let miri_val = ValTy { - value: ecx.read_place(miri_place).unwrap(), - ty: miri_ty - }; - match miri_ty.sty { - TyInt(int_ty) => { - let prim = get_prim(ecx, miri_val); - let c = ConstInt::new_signed_truncating(prim as i128, - int_ty, - ecx.tcx.sess.target.isize_ty); - let c = ConstVal::Integral(c); - assert_eq!(c, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", c, ctfe); - }, - TyUint(uint_ty) => { - let prim = get_prim(ecx, miri_val); - let c = ConstInt::new_unsigned_truncating(prim, - uint_ty, - ecx.tcx.sess.target.usize_ty); - let c = ConstVal::Integral(c); - assert_eq!(c, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", c, ctfe); - }, - TyFloat(ty) => { - let prim = get_prim(ecx, miri_val); - let f = ConstVal::Float(ConstFloat { bits: prim, ty }); - assert_eq!(f, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", f, ctfe); - }, - TyBool => { - let bits = get_prim(ecx, miri_val); - if bits > 1 { - bug!("miri evaluated to {}, but expected a bool {:?}", bits, ctfe); - } - let b = ConstVal::Bool(bits == 1); - assert_eq!(b, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", b, ctfe); - }, - TyChar => { - let bits = get_prim(ecx, miri_val); - if let Some(cm) = ::std::char::from_u32(bits as u32) { - assert_eq!( - ConstVal::Char(cm), ctfe, - "miri evaluated to {:?}, but expected {:?}", cm, ctfe, - ); - } else { - bug!("miri evaluated to {}, but expected a char {:?}", bits, ctfe); - } - }, - TyStr => { - let value = ecx.follow_by_ref_value(miri_val.value, miri_val.ty); - if let Ok(Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(len))) = value { - let bytes = ecx - .memory - .read_bytes(ptr.into(), len as u64) - .expect("bad miri memory for str"); - if let Ok(s) = ::std::str::from_utf8(bytes) { - if let ConstVal::Str(s2) = ctfe { - assert_eq!(s, s2, "miri produced {:?}, but expected {:?}", s, s2); - } else { - bug!("miri produced {:?}, but expected {:?}", s, ctfe); - } - } else { - bug!( - "miri failed to produce valid utf8 {:?}, while ctfe produced {:?}", - bytes, - ctfe, - ); - } - } else { - bug!("miri evaluated to {:?}, but expected a str {:?}", value, ctfe); - } - }, - TyArray(elem_ty, n) => { - let n = n.val.unwrap_u64(); - let vec: Vec<(ConstVal, Ty<'tcx>)> = match ctfe { - ConstVal::ByteStr(arr) => arr.data.iter().map(|&b| { - (ConstVal::Integral(ConstInt::U8(b)), ecx.tcx.types.u8) - }).collect(), - ConstVal::Aggregate(Array(v)) => { - v.iter().map(|c| (c.val, c.ty)).collect() - }, - ConstVal::Aggregate(Repeat(v, n)) => { - vec![(v.val, v.ty); n as usize] - }, - _ => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe), - }; - let layout = ecx.layout_of(miri_ty).unwrap(); - for (i, elem) in vec.into_iter().enumerate() { - assert!((i as u64) < n); - let (field_place, _) = - ecx.place_field(miri_place, Field::new(i), layout).unwrap(); - check_ctfe_against_miri(ecx, field_place, elem_ty, elem.0); - } - }, - TyTuple(..) => { - let vec = match ctfe { - ConstVal::Aggregate(Tuple(v)) => v, - _ => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe), - }; - let layout = ecx.layout_of(miri_ty).unwrap(); - for (i, elem) in vec.into_iter().enumerate() { - let (field_place, _) = - ecx.place_field(miri_place, Field::new(i), layout).unwrap(); - check_ctfe_against_miri(ecx, field_place, elem.ty, elem.val); - } - }, - TyAdt(def, _) => { - let mut miri_place = miri_place; - let struct_variant = if def.is_enum() { - let discr = ecx.read_discriminant_value(miri_place, miri_ty).unwrap(); - let variant = def.discriminants(ecx.tcx).position(|variant_discr| { - variant_discr.to_u128_unchecked() == discr - }).expect("miri produced invalid enum discriminant"); - miri_place = ecx.place_downcast(miri_place, variant).unwrap(); - &def.variants[variant] - } else { - def.non_enum_variant() - }; - let vec = match ctfe { - ConstVal::Aggregate(Struct(v)) => v, - ConstVal::Variant(did) => { - assert_eq!(struct_variant.fields.len(), 0); - assert_eq!(did, struct_variant.did); - return; - }, - ctfe => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe), - }; - let layout = ecx.layout_of(miri_ty).unwrap(); - for &(name, elem) in vec.into_iter() { - let field = struct_variant.fields.iter().position(|f| f.name == name).unwrap(); - let (field_place, _) = - ecx.place_field(miri_place, Field::new(field), layout).unwrap(); - check_ctfe_against_miri(ecx, field_place, elem.ty, elem.val); - } - }, - TySlice(_) => bug!("miri produced a slice?"), - // not supported by ctfe - TyRawPtr(_) | - TyRef(..) => {} - TyDynamic(..) => bug!("miri produced a trait object"), - TyClosure(..) => bug!("miri produced a closure"), - TyGenerator(..) => bug!("miri produced a generator"), - TyGeneratorWitness(..) => bug!("miri produced a generator witness"), - TyNever => bug!("miri produced a value of the never type"), - TyProjection(_) => bug!("miri produced a projection"), - TyAnon(..) => bug!("miri produced an impl Trait type"), - TyParam(_) => bug!("miri produced an unmonomorphized type"), - TyInfer(_) => bug!("miri produced an uninferred type"), - TyError => bug!("miri produced a type error"), - TyForeign(_) => bug!("miri produced an extern type"), - // should be fine - TyFnDef(..) => {} - TyFnPtr(_) => { - let value = ecx.value_to_primval(miri_val); - let ptr = match value { - Ok(PrimVal::Ptr(ptr)) => ptr, - value => bug!("expected fn ptr, got {:?}", value), - }; - let inst = ecx.memory.get_fn(ptr).unwrap(); - match ctfe { - ConstVal::Function(did, substs) => { - let ctfe = ty::Instance::resolve( - ecx.tcx, - ecx.param_env, - did, - substs, - ).unwrap(); - assert_eq!(inst, ctfe, "expected fn ptr {:?}, but got {:?}", ctfe, inst); - }, - _ => bug!("ctfe produced {:?}, but miri produced function {:?}", ctfe, inst), - } - }, - } -} - -fn get_prim<'a, 'tcx>( - ecx: &mut EvalContext<'a, 'tcx, CompileTimeEvaluator>, - val: ValTy<'tcx>, -) -> u128 { - let res = ecx.value_to_primval(val).and_then(|prim| prim.to_bytes()); - unwrap_miri(ecx, res) -} - -fn unwrap_miri<'a, 'tcx, T>( - ecx: &EvalContext<'a, 'tcx, CompileTimeEvaluator>, - res: Result>, -) -> T { - 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() }) } } } diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index f37fb3072b584..63939f7e03817 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -241,39 +241,16 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } pub(super) fn const_to_value(&mut self, const_val: &ConstVal<'tcx>, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { - use rustc::middle::const_val::ConstVal; - - let primval = match *const_val { - ConstVal::Integral(const_int) => PrimVal::Bytes(const_int.to_u128_unchecked()), - - ConstVal::Float(val) => PrimVal::Bytes(val.bits), - - ConstVal::Bool(b) => PrimVal::from_bool(b), - ConstVal::Char(c) => PrimVal::from_char(c), - - ConstVal::Str(ref s) => return self.str_to_value(s), - - ConstVal::ByteStr(ref bs) => { - let ptr = self.memory.allocate_cached(bs.data); - PrimVal::Ptr(ptr) - } - + match *const_val { ConstVal::Unevaluated(def_id, substs) => { let instance = self.resolve(def_id, substs)?; - return Ok(self.read_global_as_value(GlobalId { + Ok(self.read_global_as_value(GlobalId { instance, promoted: None, - }, self.layout_of(ty)?)); + }, self.layout_of(ty)?)) } - - ConstVal::Aggregate(..) | - ConstVal::Variant(_) => bug!("should not have aggregate or variant constants in MIR"), - // function items are zero sized and thus have no readable value - ConstVal::Function(..) => PrimVal::Undef, - ConstVal::Value(val) => return Ok(val), - }; - - Ok(Value::ByVal(primval)) + ConstVal::Value(val) => Ok(val), + } } pub(super) fn resolve(&self, def_id: DefId, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, ty::Instance<'tcx>> { diff --git a/src/librustc_mir/interpret/mod.rs b/src/librustc_mir/interpret/mod.rs index a6ebdd45968fc..ba894a1728a9b 100644 --- a/src/librustc_mir/interpret/mod.rs +++ b/src/librustc_mir/interpret/mod.rs @@ -18,6 +18,6 @@ pub use self::place::{Place, PlaceExtra}; pub use self::memory::{Memory, MemoryKind, HasMemory}; -pub use self::const_eval::{eval_body_as_integer, eval_body, CompileTimeEvaluator, const_eval_provider, const_val_field, const_discr}; +pub use self::const_eval::{eval_body_as_integer, eval_body, CompileTimeEvaluator, const_eval_provider, const_val_field, const_discr, check_body}; pub use self::machine::Machine; diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index c5e4eeab86709..4a2b4547cb051 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -118,10 +118,14 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { pub fn read_field( &self, base: Value, + variant: Option, field: mir::Field, base_ty: Ty<'tcx>, ) -> EvalResult<'tcx, Option<(Value, Ty<'tcx>)>> { - let base_layout = self.layout_of(base_ty)?; + let mut base_layout = self.layout_of(base_ty)?; + if let Some(variant_index) = variant { + base_layout = base_layout.for_variant(self, variant_index); + } let field_index = field.index(); let field = base_layout.field(self, field_index)?; let offset = base_layout.fields.offset(field_index); @@ -149,7 +153,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { }; let base_ty = self.place_ty(&proj.base); match proj.elem { - Field(field, _) => Ok(self.read_field(base, field, base_ty)?.map(|(f, _)| f)), + Field(field, _) => Ok(self.read_field(base, None, field, base_ty)?.map(|(f, _)| f)), // The NullablePointer cases should work fine, need to take care for normal enums Downcast(..) | Subslice { .. } | diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 11dded1d3f075..ae790971ec863 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -16,7 +16,7 @@ use rustc::mir::*; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::subst::{Kind, Subst, Substs}; use rustc::ty::maps::Providers; -use rustc_const_math::{ConstInt, ConstUsize}; +use rustc_const_math::ConstUsize; use rustc::mir::interpret::{Value, PrimVal}; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; @@ -444,12 +444,8 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { ty: func_ty, literal: Literal::Value { value: tcx.mk_const(ty::Const { - val: if tcx.sess.opts.debugging_opts.miri { - // ZST function type - ConstVal::Value(Value::ByVal(PrimVal::Undef)) - } else { - ConstVal::Function(self.def_id, substs) - }, + // ZST function type + val: ConstVal::Value(Value::ByVal(PrimVal::Undef)), ty: func_ty }), }, @@ -512,14 +508,12 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { ty: self.tcx.types.usize, literal: Literal::Value { value: self.tcx.mk_const(ty::Const { - val: if self.tcx.sess.opts.debugging_opts.miri { - ConstVal::Value(Value::ByVal(PrimVal::Bytes(value.into()))) - } else { + val: { let value = ConstUsize::new( value, self.tcx.sess.target.usize_ty, - ).unwrap(); - ConstVal::Integral(ConstInt::Usize(value)) + ).unwrap().as_u64(); + ConstVal::Value(Value::ByVal(PrimVal::Bytes(value.into()))) }, ty: self.tcx.types.usize, }) @@ -752,12 +746,8 @@ fn build_call_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty, literal: Literal::Value { value: tcx.mk_const(ty::Const { - val: if tcx.sess.opts.debugging_opts.miri { - // ZST function type - ConstVal::Value(Value::ByVal(PrimVal::Undef)) - } else { - ConstVal::Function(def_id, Substs::identity_for_item(tcx, def_id)) - }, + // ZST function type + val: ConstVal::Value(Value::ByVal(PrimVal::Undef)), ty }), }, diff --git a/src/librustc_mir/transform/elaborate_drops.rs b/src/librustc_mir/transform/elaborate_drops.rs index 9eca343cb5edc..2e8dd623d744d 100644 --- a/src/librustc_mir/transform/elaborate_drops.rs +++ b/src/librustc_mir/transform/elaborate_drops.rs @@ -19,6 +19,7 @@ use rustc::hir; use rustc::ty::{self, TyCtxt}; use rustc::mir::*; use rustc::middle::const_val::ConstVal; +use rustc::mir::interpret::{Value, PrimVal}; use rustc::util::nodemap::FxHashMap; use rustc_data_structures::indexed_set::IdxSetBuf; use rustc_data_structures::indexed_vec::Idx; @@ -541,7 +542,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { ty: self.tcx.types.bool, literal: Literal::Value { value: self.tcx.mk_const(ty::Const { - val: ConstVal::Bool(val), + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(val as u128))), ty: self.tcx.types.bool }) } diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index ec3edb1e0680e..0ff7356943388 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -68,7 +68,6 @@ use rustc::ty::{self, TyCtxt, AdtDef, Ty, GeneratorInterior}; use rustc::ty::subst::Substs; use util::dump_mir; use util::liveness::{self, LivenessMode}; -use rustc_const_math::ConstInt; use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::indexed_set::IdxSetBuf; use std::collections::HashMap; @@ -182,11 +181,7 @@ impl<'a, 'tcx> TransformVisitor<'a, 'tcx> { ty: self.tcx.types.u32, literal: Literal::Value { value: self.tcx.mk_const(ty::Const { - val: if self.tcx.sess.opts.debugging_opts.miri { - ConstVal::Value(Value::ByVal(PrimVal::Bytes(state_disc.into()))) - } else { - ConstVal::Integral(ConstInt::U32(state_disc)) - }, + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(state_disc.into()))), ty: self.tcx.types.u32 }), }, @@ -703,7 +698,7 @@ fn insert_panic_block<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: tcx.types.bool, literal: Literal::Value { value: tcx.mk_const(ty::Const { - val: ConstVal::Bool(false), + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(0))), ty: tcx.types.bool }), }, diff --git a/src/librustc_mir/transform/simplify_branches.rs b/src/librustc_mir/transform/simplify_branches.rs index ca7f573b58a47..e39f5412355bf 100644 --- a/src/librustc_mir/transform/simplify_branches.rs +++ b/src/librustc_mir/transform/simplify_branches.rs @@ -13,6 +13,7 @@ use rustc::ty::{self, TyCtxt}; use rustc::middle::const_val::ConstVal; use rustc::mir::*; +use rustc::mir::interpret::{Value, PrimVal}; use transform::{MirPass, MirSource}; use std::borrow::Cow; @@ -56,9 +57,12 @@ impl MirPass for SimplifyBranches { }, TerminatorKind::Assert { target, cond: Operand::Constant(box Constant { literal: Literal::Value { - value: &ty::Const { val: ConstVal::Bool(cond), .. } + value: &ty::Const { + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(cond))), + .. } }, .. - }), expected, .. } if cond == expected => { + }), expected, .. } if (cond == 1) == expected => { + assert!(cond <= 1); TerminatorKind::Goto { target: target } }, TerminatorKind::FalseEdges { real_target, .. } => { diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs index 77ef2c20117f1..2ad1580f75dcd 100644 --- a/src/librustc_mir/util/elaborate_drops.rs +++ b/src/librustc_mir/util/elaborate_drops.rs @@ -950,11 +950,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> ty: self.tcx().types.usize, literal: Literal::Value { value: self.tcx().mk_const(ty::Const { - val: if self.tcx().sess.opts.debugging_opts.miri { - ConstVal::Value(Value::ByVal(PrimVal::Bytes(val.into()))) - } else { - ConstVal::Integral(self.tcx().const_usize(val)) - }, + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(val.into()))), ty: self.tcx().types.usize }) } diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs index 420e8c4aad2c2..013bc07d8e9a7 100644 --- a/src/librustc_passes/consts.rs +++ b/src/librustc_passes/consts.rs @@ -25,12 +25,6 @@ // by borrowck::gather_loans use rustc::ty::cast::CastKind; -use rustc_mir::const_eval::ConstContext; -use rustc::middle::const_val::ConstEvalErr; -use rustc::middle::const_val::ErrKind::{IndexOpFeatureGated, UnimplementedConstVal, MiscCatchAll}; -use rustc::middle::const_val::ErrKind::{ErroneousReferencedConstant, MiscBinaryOp, NonConstPath}; -use rustc::middle::const_val::ErrKind::{TypeckError, Math, LayoutError}; -use rustc_const_math::{ConstMathErr, Op}; use rustc::hir::def::{Def, CtorKind}; use rustc::hir::def_id::DefId; use rustc::hir::map::blocks::FnLikeNode; @@ -41,17 +35,13 @@ use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::maps::{queries, Providers}; use rustc::ty::subst::Substs; use rustc::traits::Reveal; -use rustc::util::common::ErrorReported; use rustc::util::nodemap::{ItemLocalSet, NodeSet}; -use rustc::lint::builtin::CONST_ERR; -use rustc::hir::{self, PatKind, RangeEnd}; +use rustc::hir; use rustc_data_structures::sync::Lrc; use syntax::ast; use syntax_pos::{Span, DUMMY_SP}; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; -use std::cmp::Ordering; - pub fn provide(providers: &mut Providers) { *providers = Providers { rvalue_promotable_map, @@ -124,32 +114,6 @@ struct CheckCrateVisitor<'a, 'tcx: 'a> { } impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> { - fn const_cx(&self) -> ConstContext<'a, 'gcx> { - ConstContext::new(self.tcx, self.param_env.and(self.identity_substs), self.tables) - } - - fn check_const_eval(&self, expr: &'gcx hir::Expr) { - if self.tcx.sess.opts.debugging_opts.miri { - return; - } - if let Err(err) = self.const_cx().eval(expr) { - match err.kind { - UnimplementedConstVal(_) => {} - IndexOpFeatureGated => {} - ErroneousReferencedConstant(_) => {} - TypeckError => {} - MiscCatchAll => {} - _ => { - self.tcx.lint_node(CONST_ERR, - expr.id, - expr.span, - &format!("constant evaluation error: {}", - err.description().into_oneline())); - } - } - } - } - // Returns true iff all the values of the type are promotable. fn type_has_only_promotable_values(&mut self, ty: Ty<'gcx>) -> bool { ty.is_freeze(self.tcx, self.param_env, DUMMY_SP) && @@ -199,9 +163,6 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> { self.identity_substs = Substs::identity_for_item(self.tcx, item_def_id); let body = self.tcx.hir.body(body_id); - if !self.in_fn { - self.check_const_eval(&body.value); - } let tcx = self.tcx; let param_env = self.param_env; @@ -217,54 +178,6 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> { self.identity_substs = outer_identity_substs; } - fn visit_pat(&mut self, p: &'tcx hir::Pat) { - match p.node { - PatKind::Lit(ref lit) => { - self.check_const_eval(lit); - } - PatKind::Range(ref start, ref end, RangeEnd::Excluded) => { - match self.const_cx().compare_lit_exprs(start, end) { - Ok(Some(Ordering::Less)) => {} - Ok(Some(Ordering::Equal)) | - Ok(Some(Ordering::Greater)) => { - span_err!(self.tcx.sess, - start.span, - E0579, - "lower range bound must be less than upper"); - } - Ok(None) => bug!("ranges must be char or int"), - Err(ErrorReported) => {} - } - } - PatKind::Range(ref start, ref end, RangeEnd::Included) => { - match self.const_cx().compare_lit_exprs(start, end) { - Ok(Some(Ordering::Less)) | - Ok(Some(Ordering::Equal)) => {} - Ok(Some(Ordering::Greater)) => { - let mut err = struct_span_err!( - self.tcx.sess, - start.span, - E0030, - "lower range bound must be less than or equal to upper" - ); - err.span_label(start.span, "lower bound larger than upper bound"); - if self.tcx.sess.teach(&err.get_code().unwrap()) { - err.note("When matching against a range, the compiler verifies that \ - the range is non-empty. Range patterns include both \ - end-points, so this is equivalent to requiring the start of \ - the range to be less than or equal to the end of the range."); - } - err.emit(); - } - Ok(None) => bug!("ranges must be char or int"), - Err(ErrorReported) => {} - } - } - _ => {} - } - intravisit::walk_pat(self, p); - } - fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt) { match stmt.node { hir::StmtDecl(ref decl, _) => { @@ -313,30 +226,6 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> { self.promotable = false; } - if self.in_fn && self.promotable && !self.tcx.sess.opts.debugging_opts.miri { - match self.const_cx().eval(ex) { - Ok(_) => {} - Err(ConstEvalErr { kind: UnimplementedConstVal(_), .. }) | - Err(ConstEvalErr { kind: MiscCatchAll, .. }) | - Err(ConstEvalErr { kind: MiscBinaryOp, .. }) | - Err(ConstEvalErr { kind: NonConstPath, .. }) | - Err(ConstEvalErr { kind: ErroneousReferencedConstant(_), .. }) | - Err(ConstEvalErr { kind: Math(ConstMathErr::Overflow(Op::Shr)), .. }) | - Err(ConstEvalErr { kind: Math(ConstMathErr::Overflow(Op::Shl)), .. }) | - Err(ConstEvalErr { kind: IndexOpFeatureGated, .. }) => {} - Err(ConstEvalErr { kind: TypeckError, .. }) => {} - Err(ConstEvalErr { - kind: LayoutError(ty::layout::LayoutError::Unknown(_)), .. - }) => {} - Err(msg) => { - self.tcx.lint_node(CONST_ERR, - ex.id, - msg.span, - &msg.description().into_oneline().into_owned()); - } - } - } - if self.promotable { self.result.insert(ex.hir_id.local_id); } diff --git a/src/librustc_passes/diagnostics.rs b/src/librustc_passes/diagnostics.rs index 184fab778c601..7a54fc72d53cc 100644 --- a/src/librustc_passes/diagnostics.rs +++ b/src/librustc_passes/diagnostics.rs @@ -31,23 +31,6 @@ const FOO2: i32 = { 0 }; // but brackets are useless here ``` "##, */ -E0030: r##" -When matching against a range, the compiler verifies that the range is -non-empty. Range patterns include both end-points, so this is equivalent to -requiring the start of the range to be less than or equal to the end of the -range. - -For example: - -```compile_fail -match 5u32 { - // This range is ok, albeit pointless. - 1 ... 1 => {} - // This range is empty, and the compiler can tell. - 1000 ... 5 => {} -} -``` -"##, E0130: r##" You declared a pattern as an argument in a foreign function declaration. @@ -228,24 +211,6 @@ impl Foo for Bar { "##, -E0579: r##" -When matching against an exclusive range, the compiler verifies that the range -is non-empty. Exclusive range patterns include the start point but not the end -point, so this is equivalent to requiring the start of the range to be less -than the end of the range. - -For example: - -```compile_fail -match 5u32 { - // This range is ok, albeit pointless. - 1 .. 2 => {} - // This range is empty, and the compiler can tell. - 5 .. 5 => {} -} -``` -"##, - E0590: r##" `break` or `continue` must include a label when used in the condition of a `while` loop. diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs index c88e39d7824c7..da10fcffb4c64 100644 --- a/src/librustc_trans/mir/analyze.rs +++ b/src/librustc_trans/mir/analyze.rs @@ -119,7 +119,6 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> { }), ref args, .. } => match val { - ConstVal::Function(def_id, _) => Some((def_id, args)), ConstVal::Value(Value::ByVal(PrimVal::Undef)) => match ty.sty { ty::TyFnDef(did, _) => Some((did, args)), _ => None, diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 5bd5f19a57c63..66173cfef9203 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -10,7 +10,6 @@ use llvm::{self, ValueRef}; use rustc::middle::const_val::{ConstEvalErr, ConstVal, ErrKind}; -use rustc_const_math::ConstInt::*; use rustc_const_math::{ConstInt, ConstMathErr, MAX_F32_PLUS_HALF_ULP}; use rustc::hir::def_id::DefId; use rustc::infer::TransNormalize; @@ -64,25 +63,6 @@ impl<'a, 'tcx> Const<'tcx> { } } - pub fn from_constint(cx: &CodegenCx<'a, 'tcx>, ci: &ConstInt) -> Const<'tcx> { - let tcx = cx.tcx; - let (llval, ty) = match *ci { - I8(v) => (C_int(Type::i8(cx), v as i64), tcx.types.i8), - I16(v) => (C_int(Type::i16(cx), v as i64), tcx.types.i16), - I32(v) => (C_int(Type::i32(cx), v as i64), tcx.types.i32), - I64(v) => (C_int(Type::i64(cx), v as i64), tcx.types.i64), - I128(v) => (C_uint_big(Type::i128(cx), v as u128), tcx.types.i128), - Isize(v) => (C_int(Type::isize(cx), v.as_i64()), tcx.types.isize), - U8(v) => (C_uint(Type::i8(cx), v as u64), tcx.types.u8), - U16(v) => (C_uint(Type::i16(cx), v as u64), tcx.types.u16), - U32(v) => (C_uint(Type::i32(cx), v as u64), tcx.types.u32), - U64(v) => (C_uint(Type::i64(cx), v), tcx.types.u64), - U128(v) => (C_uint_big(Type::i128(cx), v), tcx.types.u128), - Usize(v) => (C_uint(Type::isize(cx), v.as_u64()), tcx.types.usize), - }; - Const { llval: llval, ty: ty } - } - pub fn from_bytes(ccx: &CrateContext<'a, 'tcx>, b: u128, ty: Ty<'tcx>) -> Const<'tcx> { let llval = match ty.sty { ty::TyInt(ast::IntTy::I128) | @@ -124,26 +104,7 @@ impl<'a, 'tcx> Const<'tcx> { let llty = cx.layout_of(ty).llvm_type(cx); trace!("from_constval: {:#?}: {}", cv, ty); let val = match *cv { - ConstVal::Float(v) => { - let bits = match v.ty { - ast::FloatTy::F32 => C_u32(cx, v.bits as u32), - ast::FloatTy::F64 => C_u64(cx, v.bits as u64) - }; - consts::bitcast(bits, llty) - } - ConstVal::Bool(v) => C_bool(cx, v), - ConstVal::Integral(ref i) => return Const::from_constint(cx, i), - ConstVal::Str(ref v) => C_str_slice(cx, v.clone()), - ConstVal::ByteStr(v) => { - consts::addr_of(cx, C_bytes(cx, v.data), cx.align_of(ty), "byte_str") - } - ConstVal::Char(c) => C_uint(Type::char(cx), c as u64), - ConstVal::Function(..) => C_undef(llty), - ConstVal::Variant(_) | - ConstVal::Aggregate(..) | - ConstVal::Unevaluated(..) => { - bug!("MIR must not use `{:?}` (aggregates are expanded to MIR rvalues)", cv) - } + ConstVal::Unevaluated(..) => unimplemented!("const val `{:?}`", cv), ConstVal::Value(MiriValue::ByRef(..)) => unimplemented!("{:#?}:{}", cv, ty), ConstVal::Value(MiriValue::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(len))) => { match ty.sty { @@ -157,7 +118,7 @@ impl<'a, 'tcx> Const<'tcx> { .tcx() .interpret_interner .borrow() - .get_alloc(ptr.alloc_id.0) + .get_alloc(ptr.alloc_id) .expect("miri alloc not found"); assert_eq!(len as usize as u128, len); let slice = &alloc.bytes[(ptr.offset as usize)..][..(len as usize)]; @@ -174,7 +135,7 @@ impl<'a, 'tcx> Const<'tcx> { .tcx() .interpret_interner .borrow() - .get_alloc(ptr.alloc_id.0) + .get_alloc(ptr.alloc_id) .expect("miri alloc not found"); let data = &alloc.bytes[(ptr.offset as usize)..]; consts::addr_of(ccx, C_bytes(ccx, data), ccx.align_of(ty), "byte_str") diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 5ed35e8203cec..595ae463ab0f1 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -533,7 +533,6 @@ fn convert_enum_variant_types<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } match result { - Ok(&ty::Const { val: ConstVal::Integral(x), .. }) => Some(x), Ok(&ty::Const { val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))), .. diff --git a/src/test/compile-fail/issue-31109.rs b/src/test/compile-fail/issue-31109.rs index e3548d740717f..74121e3a420f9 100644 --- a/src/test/compile-fail/issue-31109.rs +++ b/src/test/compile-fail/issue-31109.rs @@ -12,6 +12,5 @@ fn main() { // FIXME(#31407) this error should go away, but in the meantime we test that it // is accompanied by a somewhat useful error message. let _: f64 = 1234567890123456789012345678901234567890e-340; - //~^ ERROR constant evaluation error - //~| unimplemented constant expression: could not evaluate float literal + //~^ ERROR could not evaluate float literal (see issue #31407) } diff --git a/src/test/compile-fail/issue-39559-2.rs b/src/test/compile-fail/issue-39559-2.rs index aa0750230649d..f01fd1fd8f144 100644 --- a/src/test/compile-fail/issue-39559-2.rs +++ b/src/test/compile-fail/issue-39559-2.rs @@ -22,7 +22,9 @@ impl Dim for Dim3 { fn main() { let array: [usize; Dim3::dim()] - //~^ ERROR calls in constants are limited to constant functions + //~^ ERROR E0015 + //~| ERROR E0080 = [0; Dim3::dim()]; - //~^ ERROR calls in constants are limited to constant functions + //~^ ERROR E0015 + //~| ERROR E0080 } diff --git a/src/test/compile-fail/issue-41255.rs b/src/test/compile-fail/issue-41255.rs index 191b867e7a8b5..823ece2bdca01 100644 --- a/src/test/compile-fail/issue-41255.rs +++ b/src/test/compile-fail/issue-41255.rs @@ -18,33 +18,33 @@ fn main() { let x = 42.0; match x { - 5.0 => {}, //~ ERROR floating-point literals cannot be used + 5.0 => {}, //~ ERROR floating-point cannot be used //~| WARNING hard error - 5.0f32 => {}, //~ ERROR floating-point literals cannot be used + 5.0f32 => {}, //~ ERROR floating-point cannot be used //~| WARNING hard error - -5.0 => {}, //~ ERROR floating-point literals cannot be used + -5.0 => {}, //~ ERROR floating-point cannot be used //~| WARNING hard error - 1.0 .. 33.0 => {}, //~ ERROR floating-point literals cannot be used + 1.0 .. 33.0 => {}, //~ ERROR floating-point cannot be used //~| WARNING hard error - //~| ERROR floating-point literals cannot be used + //~| ERROR floating-point cannot be used //~| WARNING hard error - 39.0 ... 70.0 => {}, //~ ERROR floating-point literals cannot be used + 39.0 ... 70.0 => {}, //~ ERROR floating-point cannot be used //~| WARNING hard error - //~| ERROR floating-point literals cannot be used + //~| ERROR floating-point cannot be used //~| WARNING hard error _ => {}, }; let y = 5.0; // Same for tuples match (x, 5) { - (3.14, 1) => {}, //~ ERROR floating-point literals cannot be used + (3.14, 1) => {}, //~ ERROR floating-point cannot be used //~| WARNING hard error _ => {}, } // Or structs struct Foo { x: f32 }; match (Foo { x }) { - Foo { x: 2.0 } => {}, //~ ERROR floating-point literals cannot be used + Foo { x: 2.0 } => {}, //~ ERROR floating-point cannot be used //~| WARNING hard error _ => {}, } diff --git a/src/test/compile-fail/lint-exceeding-bitshifts.rs b/src/test/compile-fail/lint-exceeding-bitshifts.rs index 3e51550d1fa07..e94d1ff779367 100644 --- a/src/test/compile-fail/lint-exceeding-bitshifts.rs +++ b/src/test/compile-fail/lint-exceeding-bitshifts.rs @@ -53,7 +53,7 @@ fn main() { let n = n << 8; //~ ERROR: bitshift exceeds the type's number of bits let n = 1u8 << -8; //~ ERROR: bitshift exceeds the type's number of bits - //~^ WARN: attempt to shift by a negative amount + let n = 1u8 << (4+3); let n = 1u8 << (4+4); //~ ERROR: bitshift exceeds the type's number of bits diff --git a/src/test/compile-fail/lint-type-overflow2.rs b/src/test/compile-fail/lint-type-overflow2.rs index 5593499758346..f7cf8a68d5684 100644 --- a/src/test/compile-fail/lint-type-overflow2.rs +++ b/src/test/compile-fail/lint-type-overflow2.rs @@ -17,7 +17,6 @@ #[rustc_error] fn main() { //~ ERROR: compilation successful let x2: i8 = --128; //~ warn: literal out of range for i8 - //~^ warn: attempt to negate with overflow let x = -3.40282357e+38_f32; //~ warn: literal out of range for f32 let x = 3.40282357e+38_f32; //~ warn: literal out of range for f32 diff --git a/src/test/compile-fail/rfc1445/match-forbidden-without-eq.rs b/src/test/compile-fail/rfc1445/match-forbidden-without-eq.rs index 679be9ce219fe..fd888b659f142 100644 --- a/src/test/compile-fail/rfc1445/match-forbidden-without-eq.rs +++ b/src/test/compile-fail/rfc1445/match-forbidden-without-eq.rs @@ -28,7 +28,8 @@ fn main() { let x = 0.0; match x { f32::INFINITY => { } - //~^ ERROR floating point constants cannot be used in patterns + //~^ WARNING floating-point cannot be used in patterns + //~| WARNING will become a hard error in a future release _ => { } } } diff --git a/src/test/compile-fail/thread-local-in-ctfe.rs b/src/test/compile-fail/thread-local-in-ctfe.rs index 720e15991c059..dc220bd1cc94f 100644 --- a/src/test/compile-fail/thread-local-in-ctfe.rs +++ b/src/test/compile-fail/thread-local-in-ctfe.rs @@ -16,7 +16,6 @@ static A: u32 = 1; static B: u32 = A; //~^ ERROR thread-local statics cannot be accessed at compile-time //~| ERROR cannot refer to other statics by value -//~| WARN non-constant path in constant expression static C: &u32 = &A; //~^ ERROR thread-local statics cannot be accessed at compile-time @@ -24,7 +23,6 @@ static C: &u32 = &A; const D: u32 = A; //~^ ERROR thread-local statics cannot be accessed at compile-time //~| ERROR cannot refer to statics by value -//~| WARN non-constant path in constant expression const E: &u32 = &A; //~^ ERROR thread-local statics cannot be accessed at compile-time From 4c9b1b13ddc3f3120bb7aee106a261613dfcb000 Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Sat, 6 Jan 2018 03:36:39 +0000 Subject: [PATCH 007/110] Initial changes to librustc to support const trait fns. --- src/librustc/hir/def_id.rs | 1 - src/librustc_metadata/decoder.rs | 9 ++++++++- src/librustc_metadata/encoder.rs | 9 ++++++++- src/librustdoc/clean/mod.rs | 9 ++------- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/librustc/hir/def_id.rs b/src/librustc/hir/def_id.rs index 637b156ceef5d..34b3aa53d6bcf 100644 --- a/src/librustc/hir/def_id.rs +++ b/src/librustc/hir/def_id.rs @@ -220,7 +220,6 @@ impl serialize::UseSpecializedDecodable for DefId {} pub struct LocalDefId(DefIndex); impl LocalDefId { - #[inline] pub fn from_def_id(def_id: DefId) -> LocalDefId { assert!(def_id.is_local()); diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 77c3eed9e44d2..d991fb0f67b11 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -21,7 +21,7 @@ use rustc::middle::cstore::{LinkagePreference, ExternConstBody, use rustc::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel}; use rustc::hir::def::{self, Def, CtorKind}; use rustc::hir::def_id::{CrateNum, DefId, DefIndex, - CRATE_DEF_INDEX, LOCAL_CRATE}; + CRATE_DEF_INDEX, LOCAL_CRATE, LocalDefId}; use rustc::ich::Fingerprint; use rustc::middle::lang_items; use rustc::mir::{self, interpret}; @@ -273,6 +273,13 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { } } +impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { + #[inline] + fn specialized_decode(&mut self) -> Result { + self.specialized_decode().map(|i| LocalDefId::from_def_id(i)) + } +} + impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { fn specialized_decode(&mut self) -> Result { const MAX1: usize = usize::max_value() - 1; diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index f5631f5fab91f..04ac32af7ccb8 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -16,7 +16,7 @@ use schema::*; use rustc::middle::cstore::{LinkMeta, LinkagePreference, NativeLibrary, EncodedMetadata}; use rustc::hir::def::CtorKind; -use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefIndex, DefId, LOCAL_CRATE}; +use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefIndex, DefId, LocalDefId, LOCAL_CRATE}; use rustc::hir::map::definitions::DefPathTable; use rustc::ich::Fingerprint; use rustc::middle::dependency_format::Linkage; @@ -181,6 +181,13 @@ impl<'a, 'tcx> SpecializedEncoder for EncodeContext<'a, 'tcx> { } } +impl<'a, 'tcx> SpecializedEncoder for EncodeContext<'a, 'tcx> { + #[inline] + fn specialized_encode(&mut self, def_id: &LocalDefId) -> Result<(), Self::Error> { + self.specialized_encode(&def_id.to_def_id()) + } +} + impl<'a, 'tcx> SpecializedEncoder> for EncodeContext<'a, 'tcx> { fn specialized_encode(&mut self, ty: &Ty<'tcx>) -> Result<(), Self::Error> { ty_codec::encode_with_shorthand(self, ty, |ecx| &mut ecx.type_shorthands) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index d0230a69374d4..d1e6f27069c0d 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -51,7 +51,6 @@ use std::collections::hash_map::Entry; use std::collections::VecDeque; use std::fmt; -use rustc_const_math::ConstInt; use std::default::Default; use std::{mem, slice, vec}; use std::iter::{FromIterator, once}; @@ -2507,9 +2506,7 @@ impl Clean for hir::Ty { ty: cx.tcx.types.usize }) }); - let n = if let ConstVal::Integral(ConstInt::Usize(n)) = n.val { - n.to_string() - } else if let ConstVal::Unevaluated(def_id, _) = n.val { + let n = if let ConstVal::Unevaluated(def_id, _) = n.val { if let Some(node_id) = cx.tcx.hir.as_local_node_id(def_id) { print_const_expr(cx, cx.tcx.hir.body_owned_by(node_id)) } else { @@ -2640,9 +2637,7 @@ impl<'tcx> Clean for Ty<'tcx> { n = new_n; } }; - let n = if let ConstVal::Integral(ConstInt::Usize(n)) = n.val { - n.to_string() - } else if let ConstVal::Unevaluated(def_id, _) = n.val { + let n = if let ConstVal::Unevaluated(def_id, _) = n.val { if let Some(node_id) = cx.tcx.hir.as_local_node_id(def_id) { print_const_expr(cx, cx.tcx.hir.body_owned_by(node_id)) } else { From b2b101befcfe1ca4ea57862675df0de5ca7b15bd Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Tue, 2 Jan 2018 23:22:09 +0000 Subject: [PATCH 008/110] Prepare for using miri in trans --- src/librustc/dep_graph/dep_node.rs | 8 +- src/librustc/ich/impls_mir.rs | 2 + src/librustc/ich/impls_ty.rs | 8 +- src/librustc/traits/fulfill.rs | 34 +++++-- src/librustc/traits/project.rs | 40 +++++---- src/librustc/traits/select.rs | 24 ++++- src/librustc/ty/maps/config.rs | 7 +- src/librustc/ty/maps/keys.rs | 10 +++ src/librustc/ty/maps/mod.rs | 5 +- src/librustc/ty/mod.rs | 16 +++- src/librustc/ty/relate.rs | 32 ++++--- src/librustc/ty/structural_impls.rs | 52 ++++++++++- src/librustc_mir/const_eval/check.rs | 14 ++- src/librustc_mir/const_eval/pattern.rs | 20 +++-- src/librustc_mir/hair/cx/expr.rs | 13 ++- src/librustc_mir/interpret/const_eval.rs | 109 ++++++++++------------- src/librustc_typeck/check/mod.rs | 13 ++- src/librustc_typeck/collect.rs | 9 +- 18 files changed, 286 insertions(+), 130 deletions(-) diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index ed46296389da4..6824c015eabcb 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -60,15 +60,15 @@ //! user of the `DepNode` API of having to know how to compute the expected //! fingerprint for a given set of node parameters. +use mir::interpret::{GlobalId}; use hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX}; use hir::map::DefPathHash; use hir::{HirId, ItemLocalId}; -use ich::Fingerprint; +use ich::{Fingerprint, StableHashingContext}; +use rustc_data_structures::stable_hasher::{StableHasher, HashStable}; use ty::{TyCtxt, Instance, InstanceDef, ParamEnv, ParamEnvAnd, PolyTraitRef, Ty}; use ty::subst::Substs; -use rustc_data_structures::stable_hasher::{StableHasher, HashStable}; -use ich::StableHashingContext; use std::fmt; use std::hash::Hash; use syntax_pos::symbol::InternedString; @@ -518,7 +518,7 @@ define_dep_nodes!( <'tcx> [] TypeckTables(DefId), [] UsedTraitImports(DefId), [] HasTypeckTables(DefId), - [] ConstEval { param_env: ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)> }, + [] ConstEval { param_env: ParamEnvAnd<'tcx, GlobalId<'tcx>> }, [] CheckMatch(DefId), [] SymbolName(DefId), [] InstanceSymbolName { instance: Instance<'tcx> }, diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs index 67b4cfb6fa7e4..40204943bb599 100644 --- a/src/librustc/ich/impls_mir.rs +++ b/src/librustc/ich/impls_mir.rs @@ -585,3 +585,5 @@ impl<'gcx> HashStable> for mir::ClosureOutlivesSubjec } } } + +impl_stable_hash_for!(struct mir::interpret::GlobalId<'tcx> { instance, promoted }); diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 08a42e61ea000..a25741c95aaca 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -926,13 +926,13 @@ impl<'gcx> HashStable> for ty::InstanceDef<'gcx> { ty::InstanceDef::ClosureOnceShim { call_once } => { call_once.hash_stable(hcx, hasher); } - ty::InstanceDef::DropGlue(def_id, t) => { + ty::InstanceDef::DropGlue(def_id, ty) => { def_id.hash_stable(hcx, hasher); - t.hash_stable(hcx, hasher); + ty.hash_stable(hcx, hasher); } - ty::InstanceDef::CloneShim(def_id, t) => { + ty::InstanceDef::CloneShim(def_id, ty) => { def_id.hash_stable(hcx, hasher); - t.hash_stable(hcx, hasher); + ty.hash_stable(hcx, hasher); } } } diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index 2f3e19d92bcda..2eeef70a14e0b 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -9,6 +9,9 @@ // except according to those terms. use infer::{RegionObligation, InferCtxt}; +use middle::const_val::ConstEvalErr; +use middle::const_val::ErrKind::TypeckError; +use mir::interpret::GlobalId; use ty::{self, Ty, TypeFoldable, ToPolyTraitRef, ToPredicate}; use ty::error::ExpectedFound; use rustc_data_structures::obligation_forest::{ObligationForest, Error}; @@ -514,17 +517,34 @@ fn process_predicate<'a, 'gcx, 'tcx>( } Some(param_env) => { match selcx.tcx().lift_to_global(&substs) { + Some(substs) => { + let instance = ty::Instance::resolve( + selcx.tcx().global_tcx(), + param_env, + def_id, + substs, + ); + if let Some(instance) = instance { + let cid = GlobalId { + instance, + promoted: None, + }; + match selcx.tcx().at(obligation.cause.span) + .const_eval(param_env.and(cid)) { + Ok(_) => Ok(Some(vec![])), + Err(e) => Err(CodeSelectionError(ConstEvalFailure(e))) + } + } else { + Err(CodeSelectionError(ConstEvalFailure(ConstEvalErr { + span: selcx.tcx().def_span(def_id), + kind: TypeckError, + }))) + } + }, None => { pending_obligation.stalled_on = substs.types().collect(); Ok(None) } - Some(substs) => { - match selcx.tcx().at(obligation.cause.span) - .const_eval(param_env.and((def_id, substs))) { - Ok(_) => Ok(Some(vec![])), - Err(e) => Err(CodeSelectionError(ConstEvalFailure(e))) - } - } } } } diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index 3fe72344e8fea..a9dc491574379 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -29,6 +29,7 @@ use hir::def_id::DefId; use infer::{InferCtxt, InferOk}; use infer::type_variable::TypeVariableOrigin; use middle::const_val::ConstVal; +use mir::interpret::{GlobalId}; use rustc_data_structures::snapshot_map::{Snapshot, SnapshotMap}; use syntax::symbol::Symbol; use ty::subst::{Subst, Substs}; @@ -400,12 +401,17 @@ impl<'a, 'b, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for AssociatedTypeNormalizer<'a, fn fold_const(&mut self, constant: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { if let ConstVal::Unevaluated(def_id, substs) = constant.val { - if substs.needs_infer() { - let identity_substs = Substs::identity_for_item(self.tcx(), def_id); - let data = self.param_env.and((def_id, identity_substs)); - match self.tcx().lift_to_global(&data) { - Some(data) => { - match self.tcx().const_eval(data) { + let tcx = self.selcx.tcx().global_tcx(); + if let Some(param_env) = self.tcx().lift_to_global(&self.param_env) { + if substs.needs_infer() { + let identity_substs = Substs::identity_for_item(tcx, def_id); + let instance = ty::Instance::resolve(tcx, param_env, def_id, identity_substs); + if let Some(instance) = instance { + let cid = GlobalId { + instance, + promoted: None + }; + match tcx.const_eval(param_env.and(cid)) { Ok(evaluated) => { let evaluated = evaluated.subst(self.tcx(), substs); return self.fold_const(evaluated); @@ -413,18 +419,20 @@ impl<'a, 'b, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for AssociatedTypeNormalizer<'a, Err(_) => {} } } - None => {} - } - } else { - let data = self.param_env.and((def_id, substs)); - match self.tcx().lift_to_global(&data) { - Some(data) => { - match self.tcx().const_eval(data) { - Ok(evaluated) => return self.fold_const(evaluated), - Err(_) => {} + } else { + if let Some(substs) = self.tcx().lift_to_global(&substs) { + let instance = ty::Instance::resolve(tcx, param_env, def_id, substs); + if let Some(instance) = instance { + let cid = GlobalId { + instance, + promoted: None + }; + match tcx.const_eval(param_env.and(cid)) { + Ok(evaluated) => return self.fold_const(evaluated), + Err(_) => {} + } } } - None => {} } } } diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 600b4a515f0bf..91d86394b0192 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -42,6 +42,7 @@ use ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable}; use ty::fast_reject; use ty::relate::TypeRelation; use middle::lang_items; +use mir::interpret::{GlobalId}; use rustc_data_structures::bitvec::BitVector; use std::iter; @@ -732,11 +733,26 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { } ty::Predicate::ConstEvaluatable(def_id, substs) => { - match self.tcx().lift_to_global(&(obligation.param_env, substs)) { + let tcx = self.tcx(); + match tcx.lift_to_global(&(obligation.param_env, substs)) { Some((param_env, substs)) => { - match self.tcx().const_eval(param_env.and((def_id, substs))) { - Ok(_) => EvaluatedToOk, - Err(_) => EvaluatedToErr + let instance = ty::Instance::resolve( + tcx.global_tcx(), + param_env, + def_id, + substs, + ); + if let Some(instance) = instance { + let cid = GlobalId { + instance, + promoted: None + }; + match self.tcx().const_eval(param_env.and(cid)) { + Ok(_) => EvaluatedToOk, + Err(_) => EvaluatedToErr + } + } else { + EvaluatedToErr } } None => { diff --git a/src/librustc/ty/maps/config.rs b/src/librustc/ty/maps/config.rs index d880b022e2f18..21affcbc9ede7 100644 --- a/src/librustc/ty/maps/config.rs +++ b/src/librustc/ty/maps/config.rs @@ -10,9 +10,10 @@ use dep_graph::SerializedDepNodeIndex; use hir::def_id::{CrateNum, DefId, DefIndex}; +use mir::interpret::{GlobalId}; use ty::{self, Ty, TyCtxt}; -use ty::maps::queries; use ty::subst::Substs; +use ty::maps::queries; use std::hash::Hash; use syntax_pos::symbol::InternedString; @@ -152,8 +153,8 @@ impl<'tcx> QueryDescription<'tcx> for queries::reachable_set<'tcx> { } impl<'tcx> QueryDescription<'tcx> for queries::const_eval<'tcx> { - fn describe(tcx: TyCtxt, key: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>) -> String { - format!("const-evaluating `{}`", tcx.item_path_str(key.value.0)) + fn describe(tcx: TyCtxt, key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) -> String { + format!("const-evaluating `{}`", tcx.item_path_str(key.value.instance.def.def_id())) } } diff --git a/src/librustc/ty/maps/keys.rs b/src/librustc/ty/maps/keys.rs index 3dd482ad16401..8fb1ad0da823b 100644 --- a/src/librustc/ty/maps/keys.rs +++ b/src/librustc/ty/maps/keys.rs @@ -53,6 +53,16 @@ impl<'tcx> Key for ty::Instance<'tcx> { } } +impl<'tcx> Key for mir::interpret::GlobalId<'tcx> { + fn map_crate(&self) -> CrateNum { + self.instance.map_crate() + } + + fn default_span(&self, tcx: TyCtxt) -> Span { + self.instance.default_span(tcx) + } +} + impl Key for CrateNum { fn map_crate(&self) -> CrateNum { *self diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs index 43a71f1c0d367..f41bdf61d9bf1 100644 --- a/src/librustc/ty/maps/mod.rs +++ b/src/librustc/ty/maps/mod.rs @@ -29,6 +29,7 @@ use middle::lang_items::{LanguageItems, LangItem}; use middle::exported_symbols::{SymbolExportLevel, ExportedSymbol}; use mir::mono::{CodegenUnit, Stats}; use mir; +use mir::interpret::{GlobalId}; use session::{CompileResult, CrateDisambiguator}; use session::config::OutputFilenames; use traits::Vtable; @@ -210,7 +211,7 @@ define_maps! { <'tcx> /// Results of evaluating const items or constants embedded in /// other items (such as enum variant explicit discriminants). - [] fn const_eval: const_eval_dep_node(ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>) + [] fn const_eval: const_eval_dep_node(ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) -> const_val::EvalResult<'tcx>, [] fn check_match: CheckMatch(DefId) @@ -450,7 +451,7 @@ fn typeck_item_bodies_dep_node<'tcx>(_: CrateNum) -> DepConstructor<'tcx> { DepConstructor::TypeckBodiesKrate } -fn const_eval_dep_node<'tcx>(param_env: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>) +fn const_eval_dep_node<'tcx>(param_env: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) -> DepConstructor<'tcx> { DepConstructor::ConstEval { param_env } } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 8c955bf340e8d..02bf409f33f4e 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -26,7 +26,7 @@ use middle::lang_items::{FnTraitLangItem, FnMutTraitLangItem, FnOnceTraitLangIte use middle::privacy::AccessLevels; use middle::resolve_lifetime::ObjectLifetimeDefault; use mir::Mir; -use mir::interpret::{Value, PrimVal}; +use mir::interpret::{GlobalId, Value, PrimVal}; use mir::GeneratorLayout; use session::CrateDisambiguator; use traits; @@ -1835,7 +1835,12 @@ impl<'a, 'gcx, 'tcx> AdtDef { let mut discr = prev_discr.map_or(initial, |d| d.wrap_incr()); 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))) { + let instance = ty::Instance::new(expr_did, substs); + let cid = GlobalId { + instance, + promoted: None + }; + match tcx.const_eval(param_env.and(cid)) { Ok(&ty::Const { val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))), .. @@ -1885,7 +1890,12 @@ impl<'a, 'gcx, 'tcx> AdtDef { } ty::VariantDiscr::Explicit(expr_did) => { let substs = Substs::identity_for_item(tcx.global_tcx(), expr_did); - match tcx.const_eval(param_env.and((expr_did, substs))) { + let instance = ty::Instance::new(expr_did, substs); + let cid = GlobalId { + instance, + promoted: None + }; + match tcx.const_eval(param_env.and(cid)) { Ok(&ty::Const { val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))), .. diff --git a/src/librustc/ty/relate.rs b/src/librustc/ty/relate.rs index 2cbe9da88b51b..bae1ce31a5e77 100644 --- a/src/librustc/ty/relate.rs +++ b/src/librustc/ty/relate.rs @@ -20,7 +20,7 @@ use ty::subst::{UnpackedKind, Substs}; use ty::{self, Ty, TyCtxt, TypeFoldable}; use ty::fold::{TypeVisitor, TypeFolder}; use ty::error::{ExpectedFound, TypeError}; -use mir::interpret::{Value, PrimVal}; +use mir::interpret::{GlobalId, Value, PrimVal}; use util::common::ErrorReported; use std::rc::Rc; use std::iter; @@ -489,17 +489,29 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R, let param_env = ty::ParamEnv::empty(Reveal::UserFacing); match tcx.lift_to_global(&substs) { Some(substs) => { - match tcx.const_eval(param_env.and((def_id, substs))) { - Ok(&ty::Const { - val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))), - .. - }) => { - assert_eq!(b as u64 as u128, b); - return Ok(b as u64); + let instance = ty::Instance::resolve( + tcx.global_tcx(), + param_env, + def_id, + substs, + ); + if let Some(instance) = instance { + let cid = GlobalId { + instance, + promoted: None + }; + match tcx.const_eval(param_env.and(cid)) { + Ok(&ty::Const { + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))), + .. + }) => { + assert_eq!(b as u64 as u128, b); + return Ok(b as u64); + } + _ => {} } - _ => {} } - } + }, None => {} } tcx.sess.delay_span_bug(tcx.def_span(def_id), diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index ad66cbd9eab3b..7cc509f691412 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -57,6 +57,7 @@ CopyImpls! { ::syntax::abi::Abi, ::hir::def_id::DefId, ::mir::Local, + ::mir::Promoted, ::traits::Reveal, ::syntax_pos::Span, } @@ -589,7 +590,7 @@ impl<'a, 'tcx> Lift<'tcx> for ConstEvalErr<'a> { impl<'a, 'tcx> Lift<'tcx> for interpret::EvalError<'a> { type Lifted = interpret::EvalError<'tcx>; fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option { - use mir::interpret::EvalErrorKind::*; + use ::mir::interpret::EvalErrorKind::*; let kind = match self.kind { MachineError(ref err) => MachineError(err.clone()), FunctionPointerTyMismatch(a, b) => FunctionPointerTyMismatch( @@ -744,6 +745,42 @@ impl<'a, 'tcx> Lift<'tcx> for ty::layout::LayoutError<'a> { } } +impl<'a, 'tcx> Lift<'tcx> for ty::InstanceDef<'a> { + type Lifted = ty::InstanceDef<'tcx>; + fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option { + match *self { + ty::InstanceDef::Item(def_id) => + Some(ty::InstanceDef::Item(def_id)), + ty::InstanceDef::Intrinsic(def_id) => + Some(ty::InstanceDef::Intrinsic(def_id)), + ty::InstanceDef::FnPtrShim(def_id, ref ty) => + Some(ty::InstanceDef::FnPtrShim(def_id, tcx.lift(ty)?)), + ty::InstanceDef::Virtual(def_id, n) => + Some(ty::InstanceDef::Virtual(def_id, n)), + ty::InstanceDef::ClosureOnceShim { call_once } => + Some(ty::InstanceDef::ClosureOnceShim { call_once }), + ty::InstanceDef::DropGlue(def_id, ref ty) => + Some(ty::InstanceDef::DropGlue(def_id, tcx.lift(ty)?)), + ty::InstanceDef::CloneShim(def_id, ref ty) => + Some(ty::InstanceDef::CloneShim(def_id, tcx.lift(ty)?)), + } + } +} + +BraceStructLiftImpl! { + impl<'a, 'tcx> Lift<'tcx> for ty::Instance<'a> { + type Lifted = ty::Instance<'tcx>; + def, substs + } +} + +BraceStructLiftImpl! { + impl<'a, 'tcx> Lift<'tcx> for interpret::GlobalId<'a> { + type Lifted = interpret::GlobalId<'tcx>; + instance, promoted + } +} + /////////////////////////////////////////////////////////////////////////// // TypeFoldable implementations. // @@ -945,6 +982,19 @@ impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> { } } +impl<'tcx> TypeFoldable<'tcx> for interpret::GlobalId<'tcx> { + fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { + Self { + instance: self.instance.fold_with(folder), + promoted: self.promoted + } + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.instance.visit_with(visitor) + } +} + impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { let sty = match self.sty { diff --git a/src/librustc_mir/const_eval/check.rs b/src/librustc_mir/const_eval/check.rs index 70435c16ff72e..a9508defdcd81 100644 --- a/src/librustc_mir/const_eval/check.rs +++ b/src/librustc_mir/const_eval/check.rs @@ -12,7 +12,7 @@ use rustc::mir::*; use rustc::mir::visit::Visitor; -use rustc::mir::interpret::{Value, PrimVal}; +use rustc::mir::interpret::{Value, PrimVal, GlobalId}; use rustc::middle::const_val::{ConstVal, ConstEvalErr, ErrKind}; use rustc::traits; use interpret::{eval_body_as_integer, check_body}; @@ -41,7 +41,11 @@ pub fn check<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) { 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); + let cid = GlobalId { + instance, + promoted: Some(Promoted::new(i)), + }; + check_body(tcx, cid, param_env); } } @@ -65,7 +69,11 @@ impl<'a, 'tcx> ConstErrVisitor<'a, 'tcx> { }, Literal::Promoted { index } => { let instance = Instance::mono(self.tcx, self.def_id); - eval_body_as_integer(self.tcx, param_env, instance, Some(index)).unwrap() + let cid = GlobalId { + instance, + promoted: Some(index), + }; + eval_body_as_integer(self.tcx, cid, param_env).unwrap() } }; Some(val) diff --git a/src/librustc_mir/const_eval/pattern.rs b/src/librustc_mir/const_eval/pattern.rs index 48438e7dbfd58..a70ee2d1d6442 100644 --- a/src/librustc_mir/const_eval/pattern.rs +++ b/src/librustc_mir/const_eval/pattern.rs @@ -12,7 +12,7 @@ use interpret::{const_val_field, const_discr}; use rustc::middle::const_val::{ConstEvalErr, ErrKind, ConstVal}; use rustc::mir::{Field, BorrowKind, Mutability}; -use rustc::mir::interpret::{Value, PrimVal}; +use rustc::mir::interpret::{GlobalId, Value, PrimVal}; use rustc::ty::{self, TyCtxt, AdtDef, Ty, Region}; use rustc::ty::subst::{Substs, Kind}; use rustc::hir::{self, PatKind, RangeEnd}; @@ -673,14 +673,18 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { let kind = match def { Def::Const(def_id) | Def::AssociatedConst(def_id) => { let substs = self.tables.node_substs(id); - match self.tcx.at(span).const_eval(self.param_env.and((def_id, substs))) { + let instance = ty::Instance::resolve( + self.tcx, + self.param_env, + def_id, + substs, + ).unwrap(); + let cid = GlobalId { + instance, + promoted: None, + }; + match self.tcx.at(span).const_eval(self.param_env.and(cid)) { Ok(value) => { - let instance = ty::Instance::resolve( - self.tcx, - self.param_env, - def_id, - substs, - ).unwrap(); return self.const_to_pat(instance, value, id, span) }, Err(e) => { diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index e65f8f3f683f1..dc8bbdc60e038 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -15,6 +15,7 @@ use hair::cx::block; use hair::cx::to_ref::ToRef; use rustc::hir::def::{Def, CtorKind}; use rustc::middle::const_val::ConstVal; +use rustc::mir::interpret::{GlobalId, Value, PrimVal}; use rustc::ty::{self, AdtKind, VariantDef, Ty}; use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow, AutoBorrowMutability}; use rustc::mir::interpret::{Value, PrimVal}; @@ -511,7 +512,17 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, let c = &cx.tcx.hir.body(count).value; let def_id = cx.tcx.hir.body_owner_def_id(count); let substs = Substs::identity_for_item(cx.tcx.global_tcx(), def_id); - let count = match cx.tcx.at(c.span).const_eval(cx.param_env.and((def_id, substs))) { + let instance = ty::Instance::resolve( + cx.tcx.global_tcx(), + cx.param_env, + def_id, + substs, + ).unwrap(); + let global_id = GlobalId { + instance, + promoted: None + }; + let count = match cx.tcx.at(c.span).const_eval(cx.param_env.and(global_id)) { Ok(cv) => cv.val.unwrap_usize(cx.tcx), Err(s) => cx.fatal_const_eval_err(&s, c.span, "expression") }; diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index 6929bccaa9500..48f123b1f57a7 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -1,11 +1,10 @@ -use rustc::ty::{self, TyCtxt, Ty, Instance}; -use rustc::ty::layout::{self, LayoutOf}; -use rustc::ty::subst::Substs; -use rustc::hir::def_id::DefId; -use rustc::mir; +use rustc::hir; use rustc::middle::const_val::ErrKind::{CheckMatchError, TypeckError}; use rustc::middle::const_val::{ConstEvalErr, ConstVal}; -use const_eval::lookup_const_by_id; +use rustc::mir; +use rustc::ty::{self, TyCtxt, Ty, Instance}; +use rustc::ty::layout::{self, LayoutOf}; +use rustc::ty::subst::Subst; use syntax::ast::Mutability; use syntax::codemap::Span; @@ -38,20 +37,18 @@ pub fn mk_eval_cx<'a, 'tcx>( pub fn eval_body<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, - instance: Instance<'tcx>, - promoted: Option, + cid: GlobalId<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> EvalResult<'tcx, (Value, Pointer, Ty<'tcx>)> { - eval_body_and_ecx(tcx, instance, promoted, param_env).0 + eval_body_and_ecx(tcx, cid, param_env).0 } pub fn check_body<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, - instance: Instance<'tcx>, - promoted: Option, + cid: GlobalId<'tcx>, param_env: ty::ParamEnv<'tcx>, ) { - let (res, ecx) = eval_body_and_ecx(tcx, instance, promoted, param_env); + let (res, ecx) = eval_body_and_ecx(tcx, cid, param_env); if let Err(mut err) = res { ecx.report(&mut err); } @@ -59,26 +56,22 @@ pub fn check_body<'a, 'tcx>( fn eval_body_and_ecx<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, - instance: Instance<'tcx>, - promoted: Option, + cid: GlobalId<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> (EvalResult<'tcx, (Value, Pointer, Ty<'tcx>)>, EvalContext<'a, 'tcx, CompileTimeEvaluator>) { - debug!("eval_body: {:?}, {:?}", instance, param_env); + debug!("eval_body: {:?}, {:?}", cid, param_env); let limits = super::ResourceLimits::default(); let mut ecx = EvalContext::new(tcx, param_env, limits, CompileTimeEvaluator, ()); - let cid = GlobalId { - instance, - promoted, - }; - let res = (|| { - if ecx.tcx.has_attr(instance.def_id(), "linkage") { + let mut mir = ecx.load_mir(cid.instance.def)?; + if let Some(index) = cid.promoted { + mir = &mir.promoted[index]; + } + let layout = ecx.layout_of(mir.return_ty().subst(tcx, cid.instance.substs))?; + if ecx.tcx.has_attr(cid.instance.def_id(), "linkage") { return Err(ConstEvalError::NotConst("extern global".to_string()).into()); } - 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(), @@ -87,10 +80,10 @@ fn eval_body_and_ecx<'a, 'tcx>( )?; 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())); + let name = ty::tls::with(|tcx| tcx.item_path_str(cid.instance.def_id())); trace!("const_eval: pushing stack frame for global: {}", name); ecx.push_stack_frame( - instance, + cid.instance, mir.span, mir, Place::from_ptr(ptr, layout.align), @@ -100,24 +93,22 @@ fn eval_body_and_ecx<'a, 'tcx>( 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)? { + let value = match ecx.try_read_value(ptr, layout.align, layout.ty)? { Some(val) => val, - _ => Value::ByRef(ptr, align), + _ => Value::ByRef(ptr, layout.align), }; - Ok((value, ptr, instance_ty)) + Ok((value, ptr, layout.ty)) })(); (res, ecx) } pub fn eval_body_as_integer<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, + cid: GlobalId<'tcx>, param_env: ty::ParamEnv<'tcx>, - instance: Instance<'tcx>, - promoted: Option, ) -> EvalResult<'tcx, u128> { - let (value, _, ty) = eval_body(tcx, instance, promoted, param_env)?; + let (value, _, ty) = eval_body(tcx, cid, param_env)?; match value { Value::ByVal(prim) => prim.to_bytes(), _ => err!(TypeNotPrimitive(ty)), @@ -325,7 +316,7 @@ fn const_val_field_inner<'a, 'tcx>( field: mir::Field, value: Value, ty: Ty<'tcx>, -) -> ::rustc::mir::interpret::EvalResult<'tcx, (Value, Ty<'tcx>)> { +) -> EvalResult<'tcx, (Value, Ty<'tcx>)> { trace!("const_val_field: {:?}, {:?}, {:?}, {:?}", instance, field, value, ty); let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap(); let (mut field, ty) = match value { @@ -376,51 +367,47 @@ pub fn const_discr<'a, 'tcx>( pub fn const_eval_provider<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, - key: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>, + key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, ) -> ::rustc::middle::const_val::EvalResult<'tcx> { trace!("const eval: {:?}", key); - let (def_id, substs) = if let Some(resolved) = lookup_const_by_id(tcx, key) { - resolved - } else { - return Err(ConstEvalErr { - span: tcx.def_span(key.value.0), - kind: TypeckError - }); - }; + let cid = key.value; + let def_id = cid.instance.def.def_id(); + let span = tcx.def_span(def_id); - let tables = tcx.typeck_tables_of(def_id); - let body = if let Some(id) = tcx.hir.as_local_node_id(def_id) { - let body_id = tcx.hir.body_owned_by(id); + if let Some(id) = tcx.hir.as_local_node_id(def_id) { + let tables = tcx.typeck_tables_of(def_id); // Do match-check before building MIR if tcx.check_match(def_id).is_err() { return Err(ConstEvalErr { - span: tcx.def_span(key.value.0), + span, kind: CheckMatchError, }); } - tcx.mir_const_qualif(def_id); - tcx.hir.body(body_id) - } else { - tcx.extern_const_body(def_id).body - }; - - // do not continue into miri if typeck errors occurred - // it will fail horribly - if tables.tainted_by_errors { - return Err(ConstEvalErr { span: body.value.span, kind: TypeckError }) - } + if let hir::BodyOwnerKind::Const = tcx.hir.body_owner_kind(id) { + tcx.mir_const_qualif(def_id); + } + // Do not continue into miri if typeck errors occurred; it will fail horribly + if tables.tainted_by_errors { + return Err(ConstEvalErr { + span, + kind: TypeckError + }); + } + }; - let instance = ty::Instance::new(def_id, substs); - match ::interpret::eval_body(tcx, instance, None, key.param_env) { + match ::interpret::eval_body(tcx, cid, 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() }) + Err(ConstEvalErr { + span, + kind: err.into() + }) } } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 26b9649331025..1ad26b4cda59d 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -93,6 +93,7 @@ use rustc::infer::{self, InferCtxt, InferOk, RegionVariableOrigin}; use rustc::infer::anon_types::AnonTypeDecl; use rustc::infer::type_variable::{TypeVariableOrigin}; use rustc::middle::region; +use rustc::mir::interpret::{GlobalId}; use rustc::ty::subst::{Kind, Subst, Substs}; use rustc::traits::{self, FulfillmentContext, ObligationCause, ObligationCauseCode}; use rustc::ty::{self, Ty, TyCtxt, Visibility, ToPredicate}; @@ -3999,7 +4000,17 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let count_def_id = tcx.hir.body_owner_def_id(count); let param_env = ty::ParamEnv::empty(traits::Reveal::UserFacing); let substs = Substs::identity_for_item(tcx.global_tcx(), count_def_id); - let count = tcx.const_eval(param_env.and((count_def_id, substs))); + let instance = ty::Instance::resolve( + tcx.global_tcx(), + param_env, + count_def_id, + substs, + ).unwrap(); + let global_id = GlobalId { + instance, + promoted: None + }; + let count = tcx.const_eval(param_env.and(global_id)); if let Err(ref err) = count { err.report(tcx, tcx.def_span(count_def_id), "constant expression"); diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 595ae463ab0f1..0271ad269e12f 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -38,7 +38,7 @@ use rustc::ty::{self, AdtKind, ToPolyTraitRef, Ty, TyCtxt}; use rustc::ty::maps::Providers; use rustc::ty::util::IntTypeExt; use rustc::util::nodemap::{FxHashSet, FxHashMap}; -use rustc::mir::interpret::{Value, PrimVal}; +use rustc::mir::interpret::{GlobalId, Value, PrimVal}; use rustc_const_math::ConstInt; @@ -524,7 +524,12 @@ fn convert_enum_variant_types<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, prev_discr = Some(if let Some(e) = variant.node.disr_expr { let expr_did = tcx.hir.local_def_id(e.node_id); let substs = Substs::identity_for_item(tcx, expr_did); - let result = tcx.at(variant.span).const_eval(param_env.and((expr_did, substs))); + let instance = ty::Instance::new(expr_did, substs); + let global_id = GlobalId { + instance, + promoted: None + }; + let result = tcx.at(variant.span).const_eval(param_env.and(global_id)); // enum variant evaluation happens before the global constant check // so we need to report the real error From b33e4e784e9906c732dcb3e0b6872f65dc8f5597 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 16 Jan 2018 09:31:48 +0100 Subject: [PATCH 009/110] Fully use miri in trans --- src/librustc/middle/const_val.rs | 13 +- src/librustc/mir/interpret/error.rs | 4 +- src/librustc/mir/interpret/mod.rs | 3 + src/librustc/mir/mod.rs | 17 +- src/librustc/traits/error_reporting.rs | 3 +- src/librustc/traits/fulfill.rs | 9 +- src/librustc/traits/mod.rs | 2 +- src/librustc/ty/context.rs | 32 +- src/librustc/ty/maps/mod.rs | 4 +- src/librustc/ty/mod.rs | 8 +- src/librustc/ty/util.rs | 28 +- src/librustc_llvm/ffi.rs | 10 + src/librustc_metadata/decoder.rs | 4 +- src/librustc_metadata/encoder.rs | 8 +- src/librustc_mir/borrow_check/mod.rs | 13 +- src/librustc_mir/const_eval/check.rs | 92 +- src/librustc_mir/const_eval/check_match.rs | 14 +- src/librustc_mir/const_eval/eval.rs | 12 +- src/librustc_mir/const_eval/pattern.rs | 75 +- src/librustc_mir/dataflow/impls/borrows.rs | 2 +- src/librustc_mir/hair/cx/expr.rs | 5 +- src/librustc_mir/hair/cx/mod.rs | 13 +- src/librustc_mir/interpret/cast.rs | 2 +- src/librustc_mir/interpret/const_eval.rs | 208 ++- src/librustc_mir/interpret/eval_context.rs | 125 +- src/librustc_mir/interpret/machine.rs | 28 +- src/librustc_mir/interpret/memory.rs | 144 +- src/librustc_mir/interpret/mod.rs | 13 +- src/librustc_mir/interpret/operator.rs | 22 +- src/librustc_mir/interpret/place.rs | 55 +- src/librustc_mir/interpret/step.rs | 241 +-- src/librustc_mir/interpret/terminator/drop.rs | 2 +- src/librustc_mir/interpret/terminator/mod.rs | 2 +- src/librustc_mir/interpret/traits.rs | 4 +- src/librustc_mir/monomorphize/collector.rs | 51 +- src/librustc_mir/transform/check_unsafety.rs | 2 +- src/librustc_mir/transform/instcombine.rs | 487 +++++- src/librustc_trans/consts.rs | 12 +- src/librustc_trans/mir/block.rs | 35 +- src/librustc_trans/mir/constant.rs | 1424 +++-------------- src/librustc_trans/mir/mod.rs | 3 + src/librustc_trans/mir/operand.rs | 92 +- src/librustc_trans/trans_item.rs | 7 +- src/librustc_typeck/check/mod.rs | 2 +- src/librustc_typeck/collect.rs | 2 +- src/librustdoc/clean/mod.rs | 12 +- src/test/compile-fail/const-call.rs | 1 + src/test/compile-fail/const-err-early.rs | 13 +- src/test/compile-fail/const-err-multi.rs | 5 + src/test/compile-fail/const-err.rs | 21 - src/test/compile-fail/const-err2.rs | 6 +- src/test/compile-fail/const-err3.rs | 28 + src/test/compile-fail/const-eval-overflow.rs | 154 -- src/test/compile-fail/const-eval-overflow2.rs | 91 ++ .../compile-fail/const-eval-overflow2b.rs | 91 ++ .../compile-fail/const-eval-overflow2c.rs | 91 ++ .../compile-fail/const-integer-bool-ops.rs | 10 + .../const-len-underflow-subspans.rs | 2 +- src/test/compile-fail/const-slice-oob.rs | 2 + src/test/compile-fail/const-tup-index-span.rs | 1 + src/test/compile-fail/eval-enum.rs | 15 +- .../float-int-invalid-const-cast.rs | 61 - src/test/compile-fail/issue-27895.rs | 3 +- src/test/compile-fail/issue-41255.rs | 18 +- src/test/compile-fail/issue-44578.rs | 4 +- src/test/compile-fail/issue-6804.rs | 7 +- src/test/compile-fail/issue-8460-const.rs | 20 + .../compile-fail/lint-exceeding-bitshifts.rs | 18 +- .../compile-fail/lint-exceeding-bitshifts2.rs | 27 + src/test/compile-fail/lint-type-overflow2.rs | 1 + .../non-constant-in-const-path.rs | 3 +- .../rfc1445/match-forbidden-without-eq.rs | 2 +- .../const-index-feature-gate.rs | 3 +- .../run-pass/float-int-invalid-const-cast.rs | 61 + src/test/ui/const-eval/issue-43197.rs | 2 + .../ui/const-len-underflow-separate-spans.rs | 1 + src/test/ui/error-codes/E0030.rs | 2 +- src/test/ui/error-codes/E0080.rs | 5 +- 78 files changed, 1932 insertions(+), 2183 deletions(-) create mode 100644 src/test/compile-fail/const-err3.rs delete mode 100644 src/test/compile-fail/const-eval-overflow.rs create mode 100644 src/test/compile-fail/const-eval-overflow2.rs create mode 100644 src/test/compile-fail/const-eval-overflow2b.rs create mode 100644 src/test/compile-fail/const-eval-overflow2c.rs delete mode 100644 src/test/compile-fail/float-int-invalid-const-cast.rs create mode 100644 src/test/compile-fail/lint-exceeding-bitshifts2.rs rename src/test/{compile-fail => run-pass}/const-index-feature-gate.rs (83%) create mode 100644 src/test/run-pass/float-int-invalid-const-cast.rs diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index dd99305809c28..35c51c0c206b0 100644 --- a/src/librustc/middle/const_val.rs +++ b/src/librustc/middle/const_val.rs @@ -15,9 +15,9 @@ use ty::{self, TyCtxt, layout}; use ty::subst::Substs; use rustc_const_math::*; use mir::interpret::{Value, PrimVal}; +use errors::DiagnosticBuilder; use graphviz::IntoCow; -use errors::DiagnosticBuilder; use serialize; use syntax_pos::Span; @@ -169,6 +169,7 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { TypeckError => simple!("type-checking failed"), CheckMatchError => simple!("match-checking failed"), + // FIXME: report a full backtrace Miri(ref err) => simple!("miri failed: {}", err), } } @@ -186,7 +187,7 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { err = i_err; } - let mut diag = struct_span_err!(tcx.sess, err.span, E0080, "constant evaluation error"); + let mut diag = struct_error(tcx, err.span, "constant evaluation error"); err.note(tcx, primary_span, primary_kind, &mut diag); diag } @@ -221,3 +222,11 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { self.struct_error(tcx, primary_span, primary_kind).emit(); } } + +pub fn struct_error<'a, 'gcx, 'tcx>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, + span: Span, + msg: &str, +) -> DiagnosticBuilder<'gcx> { + struct_span_err!(tcx.sess, span, E0080, "{}", msg) +} diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs index 6386de5952f99..977e617968aa4 100644 --- a/src/librustc/mir/interpret/error.rs +++ b/src/librustc/mir/interpret/error.rs @@ -286,8 +286,8 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { write!(f, "tried to reallocate memory from {} to {}", old, new), DeallocatedWrongMemoryKind(ref old, ref new) => write!(f, "tried to deallocate {} memory but gave {} as the kind", old, new), - Math(span, ref err) => - write!(f, "{:?} at {:?}", err, span), + Math(_, ref err) => + write!(f, "{}", err.description()), Intrinsic(ref err) => write!(f, "{}", err), InvalidChar(c) => diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs index 8b4f56e1dba5f..394b3d96a4fb6 100644 --- a/src/librustc/mir/interpret/mod.rs +++ b/src/librustc/mir/interpret/mod.rs @@ -169,6 +169,8 @@ pub struct Allocation { pub undef_mask: UndefMask, /// The alignment of the allocation to detect unaligned reads. pub align: Align, + /// Whether the allocation should be put into mutable memory when translating via llvm + pub mutable: bool, } impl Allocation { @@ -180,6 +182,7 @@ impl Allocation { relocations: BTreeMap::new(), undef_mask, align: Align::from_bytes(1, 1).unwrap(), + mutable: false, } } } diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index d899435141819..9e9ba51620338 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -1898,13 +1898,16 @@ fn print_miri_value(value: Value, ty: Ty, f: &mut W) -> fmt::Result { let alloc = tcx .interpret_interner .borrow() - .get_alloc(ptr.alloc_id) - .expect("miri alloc not found"); - assert_eq!(len as usize as u128, len); - let slice = &alloc.bytes[(ptr.offset as usize)..][..(len as usize)]; - let s = ::std::str::from_utf8(slice) - .expect("non utf8 str from miri"); - write!(f, "{:?}", s) + .get_alloc(ptr.alloc_id); + if let Some(alloc) = alloc { + assert_eq!(len as usize as u128, len); + let slice = &alloc.bytes[(ptr.offset as usize)..][..(len as usize)]; + let s = ::std::str::from_utf8(slice) + .expect("non utf8 str from miri"); + write!(f, "{:?}", s) + } else { + write!(f, "pointer to erroneous constant {:?}, {:?}", ptr, len) + } }) }, _ => write!(f, "{:?}:{}", value, ty), diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index ce23cb2349609..2e554aff13ee3 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -32,7 +32,6 @@ use hir; use hir::def_id::DefId; use infer::{self, InferCtxt}; use infer::type_variable::TypeVariableOrigin; -use middle::const_val; use std::fmt; use syntax::ast; use session::DiagnosticMessageId; @@ -776,7 +775,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } ConstEvalFailure(ref err) => { - if let const_val::ErrKind::TypeckError = err.kind { + if let ::middle::const_val::ErrKind::TypeckError = err.kind { return; } err.struct_error(self.tcx, span, "constant expression") diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index 2eeef70a14e0b..5c082b0610fe1 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -9,8 +9,6 @@ // except according to those terms. use infer::{RegionObligation, InferCtxt}; -use middle::const_val::ConstEvalErr; -use middle::const_val::ErrKind::TypeckError; use mir::interpret::GlobalId; use ty::{self, Ty, TypeFoldable, ToPolyTraitRef, ToPredicate}; use ty::error::ExpectedFound; @@ -18,6 +16,7 @@ use rustc_data_structures::obligation_forest::{ObligationForest, Error}; use rustc_data_structures::obligation_forest::{ForestObligation, ObligationProcessor}; use std::marker::PhantomData; use hir::def_id::DefId; +use middle::const_val::{ConstEvalErr, ErrKind}; use super::CodeAmbiguity; use super::CodeProjectionError; @@ -532,12 +531,12 @@ fn process_predicate<'a, 'gcx, 'tcx>( match selcx.tcx().at(obligation.cause.span) .const_eval(param_env.and(cid)) { Ok(_) => Ok(Some(vec![])), - Err(e) => Err(CodeSelectionError(ConstEvalFailure(e))) + Err(err) => Err(CodeSelectionError(ConstEvalFailure(err))) } } else { Err(CodeSelectionError(ConstEvalFailure(ConstEvalErr { - span: selcx.tcx().def_span(def_id), - kind: TypeckError, + span: obligation.cause.span, + kind: ErrKind::UnimplementedConstVal("could not resolve"), }))) } }, diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 8ac69c4b5287a..e0e85600b9036 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -20,8 +20,8 @@ pub use self::ObligationCauseCode::*; use hir; use hir::def_id::DefId; use infer::outlives::env::OutlivesEnvironment; -use middle::const_val::ConstEvalErr; use middle::region; +use middle::const_val::ConstEvalErr; use ty::subst::Substs; use ty::{self, AdtKind, Ty, TyCtxt, TypeFoldable, ToPredicate}; use ty::error::{ExpectedFound, TypeError}; diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index ebd78467c3b9e..bf75afcfa1a9f 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -907,16 +907,17 @@ pub struct InterpretInterner<'tcx> { alloc_by_id: FxHashMap, /// Reverse map of `alloc_cache` - /// - /// Multiple globals may share the same memory - global_cache: FxHashMap>>, + global_cache: FxHashMap, /// The AllocId to assign to the next new regular allocation. /// Always incremented, never gets smaller. next_id: interpret::AllocId, - /// Allows checking whether a constant already has an allocation - alloc_cache: FxHashMap, interpret::AllocId>, + /// Allows checking whether a static already has an allocation + /// + /// This is only important for detecting statics referring to themselves + // FIXME(oli-obk) move it to the EvalContext? + alloc_cache: FxHashMap, /// A cache for basic byte allocations keyed by their contents. This is used to deduplicate /// allocations for string and bytestring literals. @@ -951,30 +952,27 @@ impl<'tcx> InterpretInterner<'tcx> { pub fn get_cached( &self, - global_id: interpret::GlobalId<'tcx>, + static_id: DefId, ) -> Option { - self.alloc_cache.get(&global_id).cloned() + self.alloc_cache.get(&static_id).cloned() } pub fn cache( &mut self, - global_id: interpret::GlobalId<'tcx>, + static_id: DefId, alloc_id: interpret::AllocId, ) { - self.global_cache.entry(alloc_id).or_default().push(global_id); - if let Some(old) = self.alloc_cache.insert(global_id, alloc_id) { - bug!("tried to cache {:?}, but was already existing as {:#?}", global_id, old); + self.global_cache.insert(alloc_id, static_id); + if let Some(old) = self.alloc_cache.insert(static_id, alloc_id) { + bug!("tried to cache {:?}, but was already existing as {:#?}", static_id, old); } } - pub fn get_globals( + pub fn get_corresponding_static_def_id( &self, ptr: interpret::AllocId, - ) -> &[interpret::GlobalId<'tcx>] { - match self.global_cache.get(&ptr) { - Some(v) => v, - None => &[], - } + ) -> Option { + self.global_cache.get(&ptr).cloned() } pub fn intern_at_reserved( diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs index f41bdf61d9bf1..0ded759fec730 100644 --- a/src/librustc/ty/maps/mod.rs +++ b/src/librustc/ty/maps/mod.rs @@ -16,7 +16,6 @@ use hir::{self, TraitCandidate, ItemLocalId, TransFnAttrs}; use hir::svh::Svh; use lint; use middle::borrowck::BorrowCheckResult; -use middle::const_val; use middle::cstore::{ExternCrate, LinkagePreference, NativeLibrary, ExternBodyNestedBodies}; use middle::cstore::{NativeLibraryKind, DepKind, CrateSource, ExternConstBody}; @@ -27,6 +26,7 @@ use middle::resolve_lifetime::{ResolveLifetimes, Region, ObjectLifetimeDefault}; use middle::stability::{self, DeprecationEntry}; use middle::lang_items::{LanguageItems, LangItem}; use middle::exported_symbols::{SymbolExportLevel, ExportedSymbol}; +use middle::const_val::EvalResult; use mir::mono::{CodegenUnit, Stats}; use mir; use mir::interpret::{GlobalId}; @@ -212,7 +212,7 @@ define_maps! { <'tcx> /// Results of evaluating const items or constants embedded in /// other items (such as enum variant explicit discriminants). [] fn const_eval: const_eval_dep_node(ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) - -> const_val::EvalResult<'tcx>, + -> EvalResult<'tcx>, [] fn check_match: CheckMatch(DefId) -> Result<(), ErrorReported>, diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 02bf409f33f4e..f9071cff78f49 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1854,11 +1854,11 @@ impl<'a, 'gcx, 'tcx> AdtDef { b, uint_type, tcx.sess.target.usize_ty).unwrap(), }; } - err => { + _ => { if !expr_did.is_local() { span_bug!(tcx.def_span(expr_did), "variant discriminant evaluation succeeded \ - in its crate but failed locally: {:?}", err); + in its crate but failed locally"); } } } @@ -1910,11 +1910,11 @@ impl<'a, 'gcx, 'tcx> AdtDef { }; break; } - err => { + _ => { if !expr_did.is_local() { span_bug!(tcx.def_span(expr_did), "variant discriminant evaluation succeeded \ - in its crate but failed locally: {:?}", err); + in its crate but failed locally"); } if explicit_index == 0 { break; diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 6ae0d520e54d3..cdf619a1dfad0 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -691,22 +691,32 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } - /// Check if the node pointed to by def_id is a mutable static item - pub fn is_static_mut(&self, def_id: DefId) -> bool { + /// Return whether the node pointed to by def_id is a static item, and its mutability + pub fn is_static(&self, def_id: DefId) -> Option { if let Some(node) = self.hir.get_if_local(def_id) { match node { Node::NodeItem(&hir::Item { - node: hir::ItemStatic(_, hir::MutMutable, _), .. - }) => true, + node: hir::ItemStatic(_, mutbl, _), .. + }) => Some(mutbl), Node::NodeForeignItem(&hir::ForeignItem { - node: hir::ForeignItemStatic(_, mutbl), .. - }) => mutbl, - _ => false + node: hir::ForeignItemStatic(_, is_mutbl), .. + }) => + Some(if is_mutbl { + hir::Mutability::MutMutable + } else { + hir::Mutability::MutImmutable + }), + _ => None } } else { match self.describe_def(def_id) { - Some(Def::Static(_, mutbl)) => mutbl, - _ => false + Some(Def::Static(_, is_mutbl)) => + Some(if is_mutbl { + hir::Mutability::MutMutable + } else { + hir::Mutability::MutImmutable + }), + _ => None } } } diff --git a/src/librustc_llvm/ffi.rs b/src/librustc_llvm/ffi.rs index e71bef512cf06..0ec5700f5f32a 100644 --- a/src/librustc_llvm/ffi.rs +++ b/src/librustc_llvm/ffi.rs @@ -664,6 +664,16 @@ extern "C" { pub fn LLVMConstShl(LHSConstant: ValueRef, RHSConstant: ValueRef) -> ValueRef; pub fn LLVMConstLShr(LHSConstant: ValueRef, RHSConstant: ValueRef) -> ValueRef; pub fn LLVMConstAShr(LHSConstant: ValueRef, RHSConstant: ValueRef) -> ValueRef; + pub fn LLVMConstGEP( + ConstantVal: ValueRef, + ConstantIndices: *const ValueRef, + NumIndices: c_uint, + ) -> ValueRef; + pub fn LLVMConstInBoundsGEP( + ConstantVal: ValueRef, + ConstantIndices: *const ValueRef, + NumIndices: c_uint, + ) -> ValueRef; pub fn LLVMConstTrunc(ConstantVal: ValueRef, ToType: TypeRef) -> ValueRef; pub fn LLVMConstZExt(ConstantVal: ValueRef, ToType: TypeRef) -> ValueRef; pub fn LLVMConstUIToFP(ConstantVal: ValueRef, ToType: TypeRef) -> ValueRef; diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index d991fb0f67b11..14984e8a6d758 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -298,9 +298,7 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx let allocation = self.tcx.unwrap().intern_const_alloc(allocation); interpret_interner().intern_at_reserved(alloc_id, allocation); - let num = usize::decode(self)?; - for _ in 0..num { - let glob = interpret::GlobalId::decode(self)?; + if let Some(glob) = Option::::decode(self)? { interpret_interner().cache(glob, alloc_id); } diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 04ac32af7ccb8..71be3f2875928 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -210,11 +210,9 @@ impl<'a, 'tcx> SpecializedEncoder for EncodeContext<'a, 'tcx trace!("encoding {:?} with {:#?}", alloc_id, alloc); usize::max_value().encode(self)?; alloc.encode(self)?; - let globals = interpret_interner.get_globals(*alloc_id); - globals.len().encode(self)?; - for glob in globals { - glob.encode(self)?; - } + interpret_interner + .get_corresponding_static_def_id(*alloc_id) + .encode(self)?; } else if let Some(fn_instance) = interpret_interner.get_fn(*alloc_id) { trace!("encoding {:?} with {:#?}", alloc_id, fn_instance); (usize::max_value() - 1).encode(self)?; diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 1ff0ffaaa68b3..028fc337967fa 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -1635,11 +1635,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { Mutability::Mut => Ok(()), } } - Place::Static(ref static_) => if !self.tcx.is_static_mut(static_.def_id) { - Err(place) - } else { - Ok(()) - }, + Place::Static(ref static_) => + if self.tcx.is_static(static_.def_id) != Some(hir::Mutability::MutMutable) { + Err(place) + } else { + Ok(()) + }, Place::Projection(ref proj) => { match proj.elem { ProjectionElem::Deref => { @@ -1792,7 +1793,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { if static1.def_id != static2.def_id { debug!("place_element_conflict: DISJOINT-STATIC"); Overlap::Disjoint - } else if self.tcx.is_static_mut(static1.def_id) { + } else if self.tcx.is_static(static1.def_id) == Some(hir::Mutability::MutMutable) { // We ignore mutable statics - they can only be unsafe code. debug!("place_element_conflict: IGNORE-STATIC-MUT"); Overlap::Disjoint diff --git a/src/librustc_mir/const_eval/check.rs b/src/librustc_mir/const_eval/check.rs index a9508defdcd81..9827dd58cd640 100644 --- a/src/librustc_mir/const_eval/check.rs +++ b/src/librustc_mir/const_eval/check.rs @@ -11,47 +11,77 @@ //! Lints statically known runtime failures use rustc::mir::*; +use rustc::hir; +use rustc::hir::map::Node; use rustc::mir::visit::Visitor; use rustc::mir::interpret::{Value, PrimVal, GlobalId}; use rustc::middle::const_val::{ConstVal, ConstEvalErr, ErrKind}; +use rustc::hir::def::Def; use rustc::traits; -use interpret::{eval_body_as_integer, check_body}; -use rustc::ty::{TyCtxt, ParamEnv, self}; +use interpret::eval_body_with_mir; +use rustc::ty::{TyCtxt, ParamEnv}; use rustc::ty::Instance; use rustc::ty::layout::LayoutOf; use rustc::hir::def_id::DefId; +use rustc::ty::subst::Substs; + +fn is_const<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool { + if let Some(node) = tcx.hir.get_if_local(def_id) { + match node { + Node::NodeItem(&hir::Item { + node: hir::ItemConst(..), .. + }) => true, + _ => false + } + } else { + match tcx.describe_def(def_id) { + Some(Def::Const(_)) => true, + _ => false + } + } +} pub fn check<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) { - if tcx.is_closure(def_id) { - return; + let mir = &tcx.optimized_mir(def_id); + let substs = Substs::identity_for_item(tcx, def_id); + let instance = Instance::new(def_id, substs); + let param_env = tcx.param_env(def_id); + + if is_const(tcx, def_id) { + let cid = GlobalId { + instance, + promoted: None, + }; + eval_body_with_mir(tcx, cid, mir, param_env); } - let generics = tcx.generics_of(def_id); + + ConstErrVisitor { + tcx, + mir, + }.visit_mir(mir); + let outer_def_id = if tcx.is_closure(def_id) { + tcx.closure_base_def_id(def_id) + } else { + def_id + }; + let generics = tcx.generics_of(outer_def_id); // FIXME: miri should be able to eval stuff that doesn't need info // from the generics if generics.parent_types as usize + generics.types.len() > 0 { return; } - let mir = &tcx.optimized_mir(def_id); - ConstErrVisitor { - tcx, - def_id, - mir, - }.visit_mir(mir); - let param_env = ParamEnv::empty(traits::Reveal::All); - let instance = Instance::mono(tcx, def_id); for i in 0.. mir.promoted.len() { use rustc_data_structures::indexed_vec::Idx; let cid = GlobalId { instance, promoted: Some(Promoted::new(i)), }; - check_body(tcx, cid, param_env); + eval_body_with_mir(tcx, cid, mir, param_env); } } struct ConstErrVisitor<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId, mir: &'a Mir<'tcx>, } @@ -61,22 +91,13 @@ impl<'a, 'tcx> ConstErrVisitor<'a, 'tcx> { Operand::Constant(ref c) => c, _ => return None, }; - let param_env = ParamEnv::empty(traits::Reveal::All); - let val = match op.literal { + match op.literal { Literal::Value { value } => match value.val { - ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))) => b, + ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))) => Some(b), _ => return None, }, - Literal::Promoted { index } => { - let instance = Instance::mono(self.tcx, self.def_id); - let cid = GlobalId { - instance, - promoted: Some(index), - }; - eval_body_as_integer(self.tcx, cid, param_env).unwrap() - } - }; - Some(val) + _ => None, + } } } @@ -87,13 +108,14 @@ impl<'a, 'tcx> Visitor<'tcx> for ConstErrVisitor<'a, 'tcx> { location: Location) { self.super_terminator(block, terminator, location); match terminator.kind { - TerminatorKind::Assert { cond: Operand::Constant(box Constant { - literal: Literal::Value { - value: &ty::Const { - val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(cond))), - .. } - }, .. - }), expected, ref msg, .. } if (cond == 1) != expected => { + TerminatorKind::Assert { ref cond, expected, ref msg, .. } => { + let cond = match self.eval_op(cond) { + Some(val) => val, + None => return, + }; + if (cond == 1) == expected { + return; + } assert!(cond <= 1); // If we know we always panic, and the error message // is also constant, then we can produce a warning. diff --git a/src/librustc_mir/const_eval/check_match.rs b/src/librustc_mir/const_eval/check_match.rs index 3621d80df1de4..9fbf55396984a 100644 --- a/src/librustc_mir/const_eval/check_match.rs +++ b/src/librustc_mir/const_eval/check_match.rs @@ -138,8 +138,18 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { PatternError::AssociatedConstInPattern(span) => { self.span_e0158(span, "associated consts cannot be referenced in patterns") } - PatternError::ConstEval(ref err) => { - err.report(self.tcx, pat_span, "pattern"); + PatternError::FloatBug => { + // FIXME(#31407) this is only necessary because float parsing is buggy + ::rustc::middle::const_val::struct_error( + self.tcx, pat_span, + "could not evaluate float literal (see issue #31407)", + ).emit(); + } + PatternError::NonConstPath(span) => { + ::rustc::middle::const_val::struct_error( + self.tcx, span, + "runtime values cannot be referenced in patterns", + ).emit(); } } } diff --git a/src/librustc_mir/const_eval/eval.rs b/src/librustc_mir/const_eval/eval.rs index 370b8681ba648..25a9e52236773 100644 --- a/src/librustc_mir/const_eval/eval.rs +++ b/src/librustc_mir/const_eval/eval.rs @@ -9,8 +9,7 @@ // except according to those terms. use rustc::middle::const_val::ConstVal::*; -use rustc::middle::const_val::ErrKind::*; -use rustc::middle::const_val::{ConstVal, ErrKind}; +use rustc::middle::const_val::ConstVal; use rustc::hir::def_id::DefId; use rustc::ty::{self, Ty, TyCtxt}; @@ -39,7 +38,7 @@ pub fn lit_to_const<'a, 'tcx>(lit: &'tcx ast::LitKind, tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>, neg: bool) - -> Result, ErrKind<'tcx>> { + -> Result, ()> { use syntax::ast::*; use rustc::mir::interpret::*; @@ -126,11 +125,8 @@ pub fn lit_to_const<'a, 'tcx>(lit: &'tcx ast::LitKind, } fn parse_float<'tcx>(num: &str, fty: ast::FloatTy) - -> Result> { - ConstFloat::from_str(num, fty).map_err(|_| { - // FIXME(#31407) this is only necessary because float parsing is buggy - UnimplementedConstVal("could not evaluate float literal (see issue #31407)") - }) + -> Result { + ConstFloat::from_str(num, fty).map_err(|_| ()) } pub fn compare_const_vals(a: &ConstVal, b: &ConstVal, ty: Ty) -> Option { diff --git a/src/librustc_mir/const_eval/pattern.rs b/src/librustc_mir/const_eval/pattern.rs index a70ee2d1d6442..4ecf0d6e638e7 100644 --- a/src/librustc_mir/const_eval/pattern.rs +++ b/src/librustc_mir/const_eval/pattern.rs @@ -10,7 +10,7 @@ use interpret::{const_val_field, const_discr}; -use rustc::middle::const_val::{ConstEvalErr, ErrKind, ConstVal}; +use rustc::middle::const_val::ConstVal; use rustc::mir::{Field, BorrowKind, Mutability}; use rustc::mir::interpret::{GlobalId, Value, PrimVal}; use rustc::ty::{self, TyCtxt, AdtDef, Ty, Region}; @@ -28,10 +28,11 @@ use syntax::ptr::P; use syntax_pos::Span; #[derive(Clone, Debug)] -pub enum PatternError<'tcx> { +pub enum PatternError { AssociatedConstInPattern(Span), StaticInPattern(Span), - ConstEval(ConstEvalErr<'tcx>), + FloatBug, + NonConstPath(Span), } #[derive(Copy, Clone, Debug)] @@ -279,7 +280,7 @@ pub struct PatternContext<'a, 'tcx: 'a> { pub param_env: ty::ParamEnv<'tcx>, pub tables: &'a ty::TypeckTables<'tcx>, pub substs: &'tcx Substs<'tcx>, - pub errors: Vec>, + pub errors: Vec, } impl<'a, 'tcx> Pattern<'tcx> { @@ -650,10 +651,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } _ => { - self.errors.push(PatternError::ConstEval(ConstEvalErr { - span, - kind: ErrKind::NonConstPath, - })); + self.errors.push(PatternError::NonConstPath(span)); PatternKind::Wild } } @@ -673,24 +671,35 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { let kind = match def { Def::Const(def_id) | Def::AssociatedConst(def_id) => { let substs = self.tables.node_substs(id); - let instance = ty::Instance::resolve( + match ty::Instance::resolve( self.tcx, self.param_env, def_id, substs, - ).unwrap(); - let cid = GlobalId { - instance, - promoted: None, - }; - match self.tcx.at(span).const_eval(self.param_env.and(cid)) { - Ok(value) => { - return self.const_to_pat(instance, value, id, span) + ) { + Some(instance) => { + let cid = GlobalId { + instance, + promoted: None, + }; + match self.tcx.at(span).const_eval(self.param_env.and(cid)) { + Ok(value) => { + return self.const_to_pat(instance, value, id, span) + }, + Err(err) => { + err.report(self.tcx, span, "pattern"); + PatternKind::Wild + }, + } }, - Err(e) => { - self.errors.push(PatternError::ConstEval(e)); + None => { + self.errors.push(if is_associated_const { + PatternError::AssociatedConstInPattern(span) + } else { + PatternError::StaticInPattern(span) + }); PatternKind::Wild - } + }, } } _ => self.lower_variant_or_leaf(def, span, ty, vec![]), @@ -716,11 +725,8 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { let cv = self.tcx.mk_const(ty::Const { val, ty }); *self.const_to_pat(instance, cv, expr.hir_id, lit.span).kind }, - Err(e) => { - self.errors.push(PatternError::ConstEval(ConstEvalErr { - span: lit.span, - kind: e, - })); + Err(()) => { + self.errors.push(PatternError::FloatBug); PatternKind::Wild }, } @@ -733,17 +739,16 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { _ => span_bug!(expr.span, "not a literal: {:?}", expr), }; match super::eval::lit_to_const(&lit.node, self.tcx, ty, true) { - Ok(value) => PatternKind::Constant { - value: self.tcx.mk_const(ty::Const { - ty, - val: value, - }), + Ok(val) => { + let instance = ty::Instance::new( + self.tables.local_id_root.expect("literal outside any scope"), + self.substs, + ); + let cv = self.tcx.mk_const(ty::Const { val, ty }); + *self.const_to_pat(instance, cv, expr.hir_id, lit.span).kind }, - Err(e) => { - self.errors.push(PatternError::ConstEval(ConstEvalErr { - span: lit.span, - kind: e, - })); + Err(()) => { + self.errors.push(PatternError::FloatBug); PatternKind::Wild }, } diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index 533bad18c38a8..a150335a1ae4d 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -783,7 +783,7 @@ fn is_unsafe_place<'a, 'gcx: 'tcx, 'tcx: 'a>( match *place { Local(_) => false, - Static(ref static_) => tcx.is_static_mut(static_.def_id), + Static(ref static_) => tcx.is_static(static_.def_id) == Some(hir::Mutability::MutMutable), Projection(ref proj) => { match proj.elem { ProjectionElem::Field(..) | diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index dc8bbdc60e038..db44d870acb01 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -524,7 +524,10 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, }; let count = match cx.tcx.at(c.span).const_eval(cx.param_env.and(global_id)) { Ok(cv) => cv.val.unwrap_usize(cx.tcx), - Err(s) => cx.fatal_const_eval_err(&s, c.span, "expression") + Err(e) => { + e.report(cx.tcx, cx.tcx.def_span(def_id), "array length"); + ConstUsize::new(0, cx.tcx.sess.target.usize_ty).unwrap() + }, }; ExprKind::Repeat { diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index 6fda4703d1cbc..24bf9445d45c0 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -16,7 +16,7 @@ use hair::*; -use rustc::middle::const_val::{ConstEvalErr, ConstVal}; +use rustc::middle::const_val::ConstVal; use rustc_data_structures::indexed_vec::Idx; use rustc::hir::def_id::{DefId, LOCAL_CRATE}; use rustc::hir::map::blocks::FnLikeNode; @@ -238,17 +238,6 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { p) } - pub fn fatal_const_eval_err(&mut self, - err: &ConstEvalErr<'tcx>, - primary_span: Span, - primary_kind: &str) - -> ! - { - err.report(self.tcx, primary_span, primary_kind); - self.tcx.sess.abort_if_errors(); - unreachable!() - } - pub fn trait_method(&mut self, trait_def_id: DefId, method_name: &str, diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index b476ea5685229..7d7e6ec9451ed 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -7,7 +7,7 @@ use rustc::mir::interpret::{PrimVal, EvalResult, MemoryPointer, PointerArithmeti use rustc_apfloat::ieee::{Single, Double}; use rustc_apfloat::Float; -impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { pub(super) fn cast_primval( &self, val: PrimVal, diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index 48f123b1f57a7..2e2246f9ab927 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -1,6 +1,6 @@ use rustc::hir; -use rustc::middle::const_val::ErrKind::{CheckMatchError, TypeckError}; use rustc::middle::const_val::{ConstEvalErr, ConstVal}; +use rustc::middle::const_val::ErrKind::{TypeckError, CheckMatchError}; use rustc::mir; use rustc::ty::{self, TyCtxt, Ty, Instance}; use rustc::ty::layout::{self, LayoutOf}; @@ -9,17 +9,37 @@ use rustc::ty::subst::Subst; use syntax::ast::Mutability; use syntax::codemap::Span; -use rustc::mir::interpret::{EvalResult, EvalError, EvalErrorKind, GlobalId, Value, MemoryPointer, Pointer, PrimVal}; -use super::{Place, EvalContext, StackPopCleanup, ValTy, PlaceExtra}; +use rustc::mir::interpret::{EvalResult, EvalError, EvalErrorKind, GlobalId, Value, MemoryPointer, Pointer, PrimVal, AllocId}; +use super::{Place, EvalContext, StackPopCleanup, ValTy, PlaceExtra, Memory}; use std::fmt; use std::error::Error; +pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + instance: Instance<'tcx>, + mir: &'mir mir::Mir<'tcx>, +) -> EvalResult<'tcx, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>> { + debug!("mk_borrowck_eval_cx: {:?}", instance); + let param_env = tcx.param_env(instance.def_id()); + let limits = super::ResourceLimits::default(); + let mut ecx = EvalContext::new(tcx, param_env, limits, CompileTimeEvaluator, ()); + // insert a stack frame so any queries have the correct substs + ecx.push_stack_frame( + instance, + mir.span, + mir, + Place::undef(), + StackPopCleanup::None, + )?; + Ok(ecx) +} + pub fn mk_eval_cx<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>, param_env: ty::ParamEnv<'tcx>, -) -> EvalResult<'tcx, EvalContext<'a, 'tcx, CompileTimeEvaluator>> { +) -> EvalResult<'tcx, EvalContext<'a, 'tcx, 'tcx, CompileTimeEvaluator>> { debug!("mk_eval_cx: {:?}, {:?}", instance, param_env); let limits = super::ResourceLimits::default(); let mut ecx = EvalContext::new(tcx, param_env, limits, CompileTimeEvaluator, ()); @@ -35,64 +55,95 @@ pub fn mk_eval_cx<'a, 'tcx>( Ok(ecx) } -pub fn eval_body<'a, 'tcx>( +pub fn eval_body_with_mir<'a, 'mir, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, cid: GlobalId<'tcx>, + mir: &'mir mir::Mir<'tcx>, param_env: ty::ParamEnv<'tcx>, -) -> EvalResult<'tcx, (Value, Pointer, Ty<'tcx>)> { - eval_body_and_ecx(tcx, cid, param_env).0 +) -> Option<(Value, Pointer, Ty<'tcx>)> { + let (res, ecx) = eval_body_and_ecx(tcx, cid, Some(mir), param_env); + match res { + Ok(val) => Some(val), + Err(mut err) => { + ecx.report(&mut err, true); + None + } + } } -pub fn check_body<'a, 'tcx>( +pub fn eval_body<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, cid: GlobalId<'tcx>, param_env: ty::ParamEnv<'tcx>, -) { - let (res, ecx) = eval_body_and_ecx(tcx, cid, param_env); - if let Err(mut err) = res { - ecx.report(&mut err); +) -> Option<(Value, Pointer, Ty<'tcx>)> { + let (res, ecx) = eval_body_and_ecx(tcx, cid, None, param_env); + match res { + Ok(val) => Some(val), + Err(mut err) => { + ecx.report(&mut err, true); + None + } } } -fn eval_body_and_ecx<'a, 'tcx>( +fn eval_body_and_ecx<'a, 'mir, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, cid: GlobalId<'tcx>, + mir: Option<&'mir mir::Mir<'tcx>>, param_env: ty::ParamEnv<'tcx>, -) -> (EvalResult<'tcx, (Value, Pointer, Ty<'tcx>)>, EvalContext<'a, 'tcx, CompileTimeEvaluator>) { +) -> (EvalResult<'tcx, (Value, Pointer, Ty<'tcx>)>, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>) { debug!("eval_body: {:?}, {:?}", cid, param_env); let limits = super::ResourceLimits::default(); let mut ecx = EvalContext::new(tcx, param_env, limits, CompileTimeEvaluator, ()); let res = (|| { - let mut mir = ecx.load_mir(cid.instance.def)?; + let mut mir = match mir { + Some(mir) => mir, + None => ecx.load_mir(cid.instance.def)?, + }; if let Some(index) = cid.promoted { mir = &mir.promoted[index]; } let layout = ecx.layout_of(mir.return_ty().subst(tcx, cid.instance.substs))?; - if ecx.tcx.has_attr(cid.instance.def_id(), "linkage") { - return Err(ConstEvalError::NotConst("extern global".to_string()).into()); - } - if tcx.interpret_interner.borrow().get_cached(cid).is_none() { - assert!(!layout.is_unsized()); - let ptr = ecx.memory.allocate( - layout.size.bytes(), - layout.align, - None, - )?; - tcx.interpret_interner.borrow_mut().cache(cid, ptr.alloc_id); - let cleanup = StackPopCleanup::MarkStatic(Mutability::Immutable); - let name = ty::tls::with(|tcx| tcx.item_path_str(cid.instance.def_id())); - trace!("const_eval: pushing stack frame for global: {}", name); - ecx.push_stack_frame( - cid.instance, - mir.span, - mir, - Place::from_ptr(ptr, layout.align), - cleanup.clone(), - )?; - - while ecx.step()? {} - } - let alloc = tcx.interpret_interner.borrow().get_cached(cid).expect("global not cached"); + let alloc = tcx.interpret_interner.borrow().get_cached(cid.instance.def_id()); + let alloc = match alloc { + Some(alloc) => { + assert!(cid.promoted.is_none()); + assert!(param_env.caller_bounds.is_empty()); + alloc + }, + None => { + assert!(!layout.is_unsized()); + let ptr = ecx.memory.allocate( + layout.size.bytes(), + layout.align, + None, + )?; + if tcx.is_static(cid.instance.def_id()).is_some() { + tcx.interpret_interner.borrow_mut().cache(cid.instance.def_id(), ptr.alloc_id); + } + let span = tcx.def_span(cid.instance.def_id()); + let internally_mutable = !layout.ty.is_freeze(tcx, param_env, span); + let mutability = tcx.is_static(cid.instance.def_id()); + let mutability = if mutability == Some(hir::Mutability::MutMutable) || internally_mutable { + Mutability::Mutable + } else { + Mutability::Immutable + }; + let cleanup = StackPopCleanup::MarkStatic(mutability); + let name = ty::tls::with(|tcx| tcx.item_path_str(cid.instance.def_id())); + trace!("const_eval: pushing stack frame for global: {}", name); + ecx.push_stack_frame( + cid.instance, + mir.span, + mir, + Place::from_ptr(ptr, layout.align), + cleanup, + )?; + + while ecx.step()? {} + ptr.alloc_id + } + }; let ptr = MemoryPointer::new(alloc, 0).into(); let value = match ecx.try_read_value(ptr, layout.align, layout.ty)? { Some(val) => val, @@ -103,18 +154,6 @@ fn eval_body_and_ecx<'a, 'tcx>( (res, ecx) } -pub fn eval_body_as_integer<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - cid: GlobalId<'tcx>, - param_env: ty::ParamEnv<'tcx>, -) -> EvalResult<'tcx, u128> { - let (value, _, ty) = eval_body(tcx, cid, param_env)?; - match value { - Value::ByVal(prim) => prim.to_bytes(), - _ => err!(TypeNotPrimitive(ty)), - } -} - pub struct CompileTimeEvaluator; impl<'tcx> Into> for ConstEvalError { @@ -159,11 +198,11 @@ impl Error for ConstEvalError { } } -impl<'tcx> super::Machine<'tcx> for CompileTimeEvaluator { +impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator { type MemoryData = (); type MemoryKinds = !; fn eval_fn_call<'a>( - ecx: &mut EvalContext<'a, 'tcx, Self>, + ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, instance: ty::Instance<'tcx>, destination: Option<(Place, mir::BasicBlock)>, _args: &[ValTy<'tcx>], @@ -204,7 +243,7 @@ impl<'tcx> super::Machine<'tcx> for CompileTimeEvaluator { fn call_intrinsic<'a>( - ecx: &mut EvalContext<'a, 'tcx, Self>, + ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, instance: ty::Instance<'tcx>, _args: &[ValTy<'tcx>], dest: Place, @@ -246,7 +285,7 @@ impl<'tcx> super::Machine<'tcx> for CompileTimeEvaluator { } fn try_ptr_op<'a>( - _ecx: &EvalContext<'a, 'tcx, Self>, + _ecx: &EvalContext<'a, 'mir, 'tcx, Self>, _bin_op: mir::BinOp, left: PrimVal, _left_ty: Ty<'tcx>, @@ -262,12 +301,16 @@ impl<'tcx> super::Machine<'tcx> for CompileTimeEvaluator { } } - fn mark_static_initialized(m: !) -> EvalResult<'tcx> { - m + fn mark_static_initialized<'a>( + _mem: &mut Memory<'a, 'mir, 'tcx, Self>, + _id: AllocId, + _mutability: Mutability, + ) -> EvalResult<'tcx, bool> { + Ok(false) } fn box_alloc<'a>( - _ecx: &mut EvalContext<'a, 'tcx, Self>, + _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, _ty: Ty<'tcx>, _dest: Place, ) -> EvalResult<'tcx> { @@ -277,7 +320,7 @@ impl<'tcx> super::Machine<'tcx> for CompileTimeEvaluator { } fn global_item_with_linkage<'a>( - _ecx: &mut EvalContext<'a, 'tcx, Self>, + _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, _instance: ty::Instance<'tcx>, _mutability: Mutability, ) -> EvalResult<'tcx> { @@ -374,14 +417,34 @@ pub fn const_eval_provider<'a, 'tcx>( let def_id = cid.instance.def.def_id(); let span = tcx.def_span(def_id); + if tcx.is_foreign_item(def_id) { + let id = tcx.interpret_interner.borrow().get_cached(def_id); + let id = match id { + // FIXME: due to caches this shouldn't happen, add some assertions + Some(id) => id, + None => { + let id = tcx.interpret_interner.borrow_mut().reserve(); + tcx.interpret_interner.borrow_mut().cache(def_id, id); + id + }, + }; + let ty = tcx.type_of(def_id); + let layout = (tcx, key.param_env).layout_of(ty).unwrap(); + let ptr = MemoryPointer::new(id, 0); + return Ok(tcx.mk_const(ty::Const { + val: ConstVal::Value(Value::ByRef(ptr.into(), layout.align)), + ty, + })) + } + if let Some(id) = tcx.hir.as_local_node_id(def_id) { let tables = tcx.typeck_tables_of(def_id); // Do match-check before building MIR if tcx.check_match(def_id).is_err() { return Err(ConstEvalErr { - span, kind: CheckMatchError, + span, }); } @@ -392,22 +455,25 @@ pub fn const_eval_provider<'a, 'tcx>( // Do not continue into miri if typeck errors occurred; it will fail horribly if tables.tainted_by_errors { return Err(ConstEvalErr { + kind: TypeckError, span, - kind: TypeckError }); } }; - match ::interpret::eval_body(tcx, cid, key.param_env) { - Ok((miri_value, _, miri_ty)) => Ok(tcx.mk_const(ty::Const { + let (res, ecx) = eval_body_and_ecx(tcx, cid, None, key.param_env); + res.map(|(miri_value, _, miri_ty)| { + tcx.mk_const(ty::Const { val: ConstVal::Value(miri_value), ty: miri_ty, - })), - Err(err) => { - Err(ConstEvalErr { - span, - kind: err.into() - }) + }) + }).map_err(|mut err| { + if tcx.is_static(def_id).is_some() { + ecx.report(&mut err, true); } - } + ConstEvalErr { + kind: err.into(), + span, + } + }) } diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 63939f7e03817..7d2b83e921748 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -3,7 +3,7 @@ use std::fmt::Write; use rustc::hir::def_id::DefId; use rustc::hir::map::definitions::DefPathData; -use rustc::middle::const_val::ConstVal; +use rustc::middle::const_val::{ConstVal, ErrKind}; use rustc::mir; use rustc::traits::Reveal; use rustc::ty::layout::{self, Size, Align, HasDataLayout, LayoutOf, TyLayout}; @@ -21,7 +21,7 @@ use super::{Place, PlaceExtra, Memory, HasMemory, MemoryKind, operator, Machine}; -pub struct EvalContext<'a, 'tcx: 'a, M: Machine<'tcx>> { +pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { /// Stores the `Machine` instance. pub machine: M, @@ -32,10 +32,10 @@ pub struct EvalContext<'a, 'tcx: 'a, M: Machine<'tcx>> { pub param_env: ty::ParamEnv<'tcx>, /// The virtual memory system. - pub memory: Memory<'a, 'tcx, M>, + pub memory: Memory<'a, 'mir, 'tcx, M>, /// The virtual call stack. - pub(crate) stack: Vec>, + pub(crate) stack: Vec>, /// The maximum number of stack frames allowed pub(crate) stack_limit: usize, @@ -47,12 +47,12 @@ pub struct EvalContext<'a, 'tcx: 'a, M: Machine<'tcx>> { } /// A stack frame. -pub struct Frame<'tcx> { +pub struct Frame<'mir, 'tcx: 'mir> { //////////////////////////////////////////////////////////////////////////////// // Function and callsite information //////////////////////////////////////////////////////////////////////////////// /// The MIR for the function called on this frame. - pub mir: &'tcx mir::Mir<'tcx>, + pub mir: &'mir mir::Mir<'tcx>, /// The def_id and substs of the current function pub instance: ty::Instance<'tcx>, @@ -131,6 +131,15 @@ pub struct ValTy<'tcx> { pub ty: Ty<'tcx>, } +impl<'tcx> ValTy<'tcx> { + pub fn from(val: &ty::Const<'tcx>) -> Option { + match val.val { + ConstVal::Value(value) => Some(ValTy { value, ty: val.ty }), + ConstVal::Unevaluated { .. } => None, + } + } +} + impl<'tcx> ::std::ops::Deref for ValTy<'tcx> { type Target = Value; fn deref(&self) -> &Value { @@ -138,37 +147,37 @@ impl<'tcx> ::std::ops::Deref for ValTy<'tcx> { } } -impl<'a, 'tcx, M: Machine<'tcx>> HasDataLayout for &'a EvalContext<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for &'a EvalContext<'a, 'mir, 'tcx, M> { #[inline] fn data_layout(&self) -> &layout::TargetDataLayout { &self.tcx.data_layout } } -impl<'c, 'b, 'a, 'tcx, M: Machine<'tcx>> HasDataLayout - for &'c &'b mut EvalContext<'a, 'tcx, M> { +impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout + for &'c &'b mut EvalContext<'a, 'mir, 'tcx, M> { #[inline] fn data_layout(&self) -> &layout::TargetDataLayout { &self.tcx.data_layout } } -impl<'a, 'tcx, M: Machine<'tcx>> layout::HasTyCtxt<'tcx> for &'a EvalContext<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> layout::HasTyCtxt<'tcx> for &'a EvalContext<'a, 'mir, 'tcx, M> { #[inline] fn tcx<'b>(&'b self) -> TyCtxt<'b, 'tcx, 'tcx> { self.tcx } } -impl<'c, 'b, 'a, 'tcx, M: Machine<'tcx>> layout::HasTyCtxt<'tcx> - for &'c &'b mut EvalContext<'a, 'tcx, M> { +impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> layout::HasTyCtxt<'tcx> + for &'c &'b mut EvalContext<'a, 'mir, 'tcx, M> { #[inline] fn tcx<'d>(&'d self) -> TyCtxt<'d, 'tcx, 'tcx> { self.tcx } } -impl<'a, 'tcx, M: Machine<'tcx>> LayoutOf> for &'a EvalContext<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> LayoutOf> for &'a EvalContext<'a, 'mir, 'tcx, M> { type TyLayout = EvalResult<'tcx, TyLayout<'tcx>>; fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout { @@ -177,8 +186,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> LayoutOf> for &'a EvalContext<'a, 'tcx } } -impl<'c, 'b, 'a, 'tcx, M: Machine<'tcx>> LayoutOf> - for &'c &'b mut EvalContext<'a, 'tcx, M> { +impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> LayoutOf> + for &'c &'b mut EvalContext<'a, 'mir, 'tcx, M> { type TyLayout = EvalResult<'tcx, TyLayout<'tcx>>; #[inline] @@ -187,7 +196,7 @@ impl<'c, 'b, 'a, 'tcx, M: Machine<'tcx>> LayoutOf> } } -impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { pub fn new( tcx: TyCtxt<'a, 'tcx, 'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -214,15 +223,15 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { self.memory.allocate(size, layout.align, Some(MemoryKind::Stack)) } - pub fn memory(&self) -> &Memory<'a, 'tcx, M> { + pub fn memory(&self) -> &Memory<'a, 'mir, 'tcx, M> { &self.memory } - pub fn memory_mut(&mut self) -> &mut Memory<'a, 'tcx, M> { + pub fn memory_mut(&mut self) -> &mut Memory<'a, 'mir, 'tcx, M> { &mut self.memory } - pub fn stack(&self) -> &[Frame<'tcx>] { + pub fn stack(&self) -> &[Frame<'mir, 'tcx>] { &self.stack } @@ -240,14 +249,14 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { )) } - pub(super) fn const_to_value(&mut self, const_val: &ConstVal<'tcx>, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { + pub(super) fn const_to_value(&self, const_val: &ConstVal<'tcx>, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { match *const_val { ConstVal::Unevaluated(def_id, substs) => { let instance = self.resolve(def_id, substs)?; - Ok(self.read_global_as_value(GlobalId { + self.read_global_as_value(GlobalId { instance, promoted: None, - }, self.layout_of(ty)?)) + }, ty) } ConstVal::Value(val) => Ok(val), } @@ -380,14 +389,14 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { &mut self, instance: ty::Instance<'tcx>, span: codemap::Span, - mir: &'tcx mir::Mir<'tcx>, + mir: &'mir mir::Mir<'tcx>, return_place: Place, return_to_block: StackPopCleanup, ) -> EvalResult<'tcx> { ::log_settings::settings().indentation += 1; /// Return the set of locals that have a storage annotation anywhere - fn collect_storage_annotations<'tcx>(mir: &'tcx mir::Mir<'tcx>) -> HashSet { + fn collect_storage_annotations<'mir, 'tcx>(mir: &'mir mir::Mir<'tcx>) -> HashSet { use rustc::mir::StatementKind::*; let mut set = HashSet::new(); @@ -819,7 +828,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { self.read_global_as_value(GlobalId { instance: self.frame().instance, promoted: Some(index), - }, self.layout_of(ty)?) + }, ty)? } }; @@ -931,9 +940,28 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { Ok(()) } - pub fn read_global_as_value(&self, gid: GlobalId, layout: TyLayout) -> Value { - let alloc = self.tcx.interpret_interner.borrow().get_cached(gid).expect("global not cached"); - Value::ByRef(MemoryPointer::new(alloc, 0).into(), layout.align) + pub fn read_global_as_value(&self, gid: GlobalId<'tcx>, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { + if gid.promoted.is_none() { + let cached = self + .tcx + .interpret_interner + .borrow() + .get_cached(gid.instance.def_id()); + if let Some(alloc_id) = cached { + let layout = self.layout_of(ty)?; + let ptr = MemoryPointer::new(alloc_id, 0); + return Ok(Value::ByRef(ptr.into(), layout.align)) + } + } + let cv = match self.tcx.const_eval(self.param_env.and(gid)) { + Ok(val) => val, + Err(err) => match err.kind { + ErrKind::Miri(miri) => return Err(miri), + ErrKind::TypeckError => return err!(TypeckError), + other => bug!("const eval returned {:?}", other), + }, + }; + self.const_to_value(&cv.val, ty) } pub fn force_allocation(&mut self, place: Place) -> EvalResult<'tcx, Place> { @@ -1326,15 +1354,15 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { Ok(Some(Value::ByVal(val))) } - pub fn frame(&self) -> &Frame<'tcx> { + pub fn frame(&self) -> &Frame<'mir, 'tcx> { self.stack.last().expect("no call frames exist") } - pub fn frame_mut(&mut self) -> &mut Frame<'tcx> { + pub fn frame_mut(&mut self) -> &mut Frame<'mir, 'tcx> { self.stack.last_mut().expect("no call frames exist") } - pub(super) fn mir(&self) -> &'tcx mir::Mir<'tcx> { + pub(super) fn mir(&self) -> &'mir mir::Mir<'tcx> { self.frame().mir } @@ -1544,7 +1572,10 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { Ok(()) } - pub fn report(&self, e: &mut EvalError) { + pub fn report(&self, e: &mut EvalError, as_err: bool) { + if let EvalErrorKind::TypeckError = e.kind { + return; + } if let Some(ref mut backtrace) = e.backtrace { let mut trace_text = "\n\nAn error occurred in miri:\n".to_string(); backtrace.resolve(); @@ -1582,8 +1613,34 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } else { block.terminator().source_info.span }; - let mut err = self.tcx.sess.struct_span_err(span, &e.to_string()); + let node_id = self + .stack() + .iter() + .rev() + .filter_map(|frame| self.tcx.hir.as_local_node_id(frame.instance.def_id())) + .next() + .expect("some part of a failing const eval must be local"); + let mut err = if as_err { + ::rustc::middle::const_val::struct_error(self.tcx, span, "constant evaluation error") + } else { + self.tcx.struct_span_lint_node( + ::rustc::lint::builtin::CONST_ERR, + node_id, + span, + "constant evaluation error", + ) + }; + err.span_label(span, e.to_string()); + let mut last_span = None; for &Frame { instance, span, .. } in self.stack().iter().rev() { + // make sure we don't emit frames that are duplicates of the previous + if let Some(last) = last_span { + if last == span { + continue; + } + } else { + last_span = Some(span); + } if self.tcx.def_key(instance.def_id()).disambiguated_data.data == DefPathData::ClosureExpr { @@ -1599,7 +1656,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } } -impl<'tcx> Frame<'tcx> { +impl<'mir, 'tcx> Frame<'mir, 'tcx> { pub fn get_local(&self, local: mir::Local) -> EvalResult<'tcx, Value> { // Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0. self.locals[local.index() - 1].ok_or(EvalErrorKind::DeadLocal.into()) diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs index c2989dbaaf11f..d5e57d3317c55 100644 --- a/src/librustc_mir/interpret/machine.rs +++ b/src/librustc_mir/interpret/machine.rs @@ -12,7 +12,7 @@ use syntax::ast::Mutability; /// Methods of this trait signifies a point where CTFE evaluation would fail /// and some use case dependent behaviour can instead be applied -pub trait Machine<'tcx>: Sized { +pub trait Machine<'mir, 'tcx>: Sized { /// Additional data that can be accessed via the Memory type MemoryData; @@ -26,7 +26,7 @@ pub trait Machine<'tcx>: Sized { /// /// Returns Ok(false) if a new stack frame was pushed fn eval_fn_call<'a>( - ecx: &mut EvalContext<'a, 'tcx, Self>, + ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, instance: ty::Instance<'tcx>, destination: Option<(Place, mir::BasicBlock)>, args: &[ValTy<'tcx>], @@ -36,7 +36,7 @@ pub trait Machine<'tcx>: Sized { /// directly process an intrinsic without pushing a stack frame. fn call_intrinsic<'a>( - ecx: &mut EvalContext<'a, 'tcx, Self>, + ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, instance: ty::Instance<'tcx>, args: &[ValTy<'tcx>], dest: Place, @@ -51,7 +51,7 @@ pub trait Machine<'tcx>: Sized { /// /// Returns a (value, overflowed) pair if the operation succeeded fn try_ptr_op<'a>( - ecx: &EvalContext<'a, 'tcx, Self>, + ecx: &EvalContext<'a, 'mir, 'tcx, Self>, bin_op: mir::BinOp, left: PrimVal, left_ty: Ty<'tcx>, @@ -60,26 +60,30 @@ pub trait Machine<'tcx>: Sized { ) -> EvalResult<'tcx, Option<(PrimVal, bool)>>; /// Called when trying to mark machine defined `MemoryKinds` as static - fn mark_static_initialized(m: Self::MemoryKinds) -> EvalResult<'tcx>; + fn mark_static_initialized<'a>( + _mem: &mut Memory<'a, 'mir, 'tcx, Self>, + _id: AllocId, + _mutability: Mutability, + ) -> EvalResult<'tcx, bool>; /// Heap allocations via the `box` keyword /// /// Returns a pointer to the allocated memory fn box_alloc<'a>( - ecx: &mut EvalContext<'a, 'tcx, Self>, + ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, ty: Ty<'tcx>, dest: Place, ) -> EvalResult<'tcx>; /// Called when trying to access a global declared with a `linkage` attribute fn global_item_with_linkage<'a>( - ecx: &mut EvalContext<'a, 'tcx, Self>, + ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, instance: ty::Instance<'tcx>, mutability: Mutability, ) -> EvalResult<'tcx>; fn check_locks<'a>( - _mem: &Memory<'a, 'tcx, Self>, + _mem: &Memory<'a, 'mir, 'tcx, Self>, _ptr: MemoryPointer, _size: u64, _access: AccessKind, @@ -88,12 +92,12 @@ pub trait Machine<'tcx>: Sized { } fn add_lock<'a>( - _mem: &mut Memory<'a, 'tcx, Self>, + _mem: &mut Memory<'a, 'mir, 'tcx, Self>, _id: AllocId, ) {} fn free_lock<'a>( - _mem: &mut Memory<'a, 'tcx, Self>, + _mem: &mut Memory<'a, 'mir, 'tcx, Self>, _id: AllocId, _len: u64, ) -> EvalResult<'tcx> { @@ -101,14 +105,14 @@ pub trait Machine<'tcx>: Sized { } fn end_region<'a>( - _ecx: &mut EvalContext<'a, 'tcx, Self>, + _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, _reg: Option<::rustc::middle::region::Scope>, ) -> EvalResult<'tcx> { Ok(()) } fn validation_op<'a>( - _ecx: &mut EvalContext<'a, 'tcx, Self>, + _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, _op: ::rustc::mir::ValidationOp, _operand: &::rustc::mir::ValidationOperand<'tcx, ::rustc::mir::Place<'tcx>>, ) -> EvalResult<'tcx> { diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 7cc4ba8489525..5ee84e0e02c41 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -1,6 +1,6 @@ use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian, BigEndian}; use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque}; -use std::{ptr, mem, io}; +use std::{ptr, io}; use rustc::ty::{Instance, TyCtxt}; use rustc::ty::layout::{self, Align, TargetDataLayout}; @@ -19,8 +19,6 @@ use super::{EvalContext, Machine}; pub enum MemoryKind { /// Error if deallocated except during a stack pop Stack, - /// A mutable Static. All the others are interned in the tcx - MutableStatic, // FIXME: move me into the machine, rustc const eval doesn't need them /// Additional memory kinds a machine wishes to distinguish from the builtin ones Machine(T), } @@ -29,7 +27,7 @@ pub enum MemoryKind { // Top-level interpreter memory //////////////////////////////////////////////////////////////////////////////// -pub struct Memory<'a, 'tcx: 'a, M: Machine<'tcx>> { +pub struct Memory<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { /// Additional data required by the Machine pub data: M::MemoryData, @@ -56,7 +54,7 @@ pub struct Memory<'a, 'tcx: 'a, M: Machine<'tcx>> { pub tcx: TyCtxt<'a, 'tcx, 'tcx>, } -impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, max_memory: u64, data: M::MemoryData) -> Self { Memory { data, @@ -107,6 +105,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { relocations: BTreeMap::new(), undef_mask: UndefMask::new(size), align, + mutable: false, }; let id = self.tcx.interpret_interner.borrow_mut().reserve(); M::add_lock(self, id); @@ -119,7 +118,6 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { None => { self.uninitialized_statics.insert(id, alloc); }, - Some(MemoryKind::MutableStatic) => bug!("don't allocate mutable statics directly") } Ok(MemoryPointer::new(id, 0)) } @@ -164,10 +162,6 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { pub fn deallocate_local(&mut self, ptr: MemoryPointer) -> EvalResult<'tcx> { match self.alloc_kind.get(&ptr.alloc_id).cloned() { - // for a constant like `const FOO: &i32 = &1;` the local containing - // the `1` is referred to by the global. We transitively marked everything - // the global refers to as static itself, so we don't free it here - Some(MemoryKind::MutableStatic) => Ok(()), Some(MemoryKind::Stack) => self.deallocate(ptr, None, MemoryKind::Stack), // Happens if the memory was interned into immutable memory None => Ok(()), @@ -292,7 +286,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { } /// Allocation accessors -impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { pub fn get(&self, id: AllocId) -> EvalResult<'tcx, &Allocation> { // normal alloc? match self.alloc_map.get(&id) { @@ -376,7 +370,6 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { Some(a) => (a, match self.alloc_kind[&id] { MemoryKind::Stack => " (stack)".to_owned(), MemoryKind::Machine(m) => format!(" ({:?})", m), - MemoryKind::MutableStatic => " (static mut)".to_owned(), }), // uninitialized static alloc? None => match self.uninitialized_statics.get(&id) { @@ -388,15 +381,15 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { Some(a) => (a, "(immutable)".to_owned()), None => if let Some(func) = int.get_fn(id) { trace!("{} {}", msg, func); - continue; + continue; } else { - trace!("{} (deallocated)", msg); - continue; + trace!("{} (deallocated)", msg); + continue; }, - } + } }, }, - }; + }; for i in 0..(alloc.bytes.len() as u64) { if let Some(&target_id) = alloc.relocations.get(&i) { @@ -441,14 +434,9 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { pub fn leak_report(&self) -> usize { trace!("### LEAK REPORT ###"); - let kinds = &self.alloc_kind; let leaks: Vec<_> = self.alloc_map .keys() - .filter_map(|key| if kinds[key] != MemoryKind::MutableStatic { - Some(*key) - } else { - None - }) + .cloned() .collect(); let n = leaks.len(); self.dump_allocs(leaks); @@ -457,7 +445,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { } /// Byte accessors -impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { fn get_bytes_unchecked( &self, ptr: MemoryPointer, @@ -521,7 +509,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { } /// Reading and writing -impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { /// mark an allocation pointed to by a static as static and initialized fn mark_inner_allocation_initialized( &mut self, @@ -529,10 +517,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { mutability: Mutability, ) -> EvalResult<'tcx> { match self.alloc_kind.get(&alloc) { - // do not go into immutable statics - None | - // or mutable statics - Some(&MemoryKind::MutableStatic) => Ok(()), + // do not go into statics + None => Ok(()), // just locals and machine allocs Some(_) => self.mark_static_initalized(alloc, mutability), } @@ -549,60 +535,27 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { alloc_id, mutability ); - if mutability == Mutability::Immutable { - let alloc = self.alloc_map.remove(&alloc_id); - let kind = self.alloc_kind.remove(&alloc_id); - assert_ne!(kind, Some(MemoryKind::MutableStatic)); - let uninit = self.uninitialized_statics.remove(&alloc_id); - if let Some(alloc) = alloc.or(uninit) { - let alloc = self.tcx.intern_const_alloc(alloc); - self.tcx.interpret_interner.borrow_mut().intern_at_reserved(alloc_id, alloc); - // recurse into inner allocations - for &alloc in alloc.relocations.values() { - self.mark_inner_allocation_initialized(alloc, mutability)?; - } - } - return Ok(()); + // The machine handled it + if M::mark_static_initialized(self, alloc_id, mutability)? { + return Ok(()) } - // We are marking the static as initialized, so move it out of the uninit map - if let Some(uninit) = self.uninitialized_statics.remove(&alloc_id) { - self.alloc_map.insert(alloc_id, uninit); + let alloc = self.alloc_map.remove(&alloc_id); + match self.alloc_kind.remove(&alloc_id) { + None => {}, + Some(MemoryKind::Machine(_)) => bug!("machine didn't handle machine alloc"), + Some(MemoryKind::Stack) => {}, } - // do not use `self.get_mut(alloc_id)` here, because we might have already marked a - // sub-element or have circular pointers (e.g. `Rc`-cycles) - let relocations = match self.alloc_map.get_mut(&alloc_id) { - Some(&mut Allocation { - ref mut relocations, - .. - }) => { - match self.alloc_kind.get(&alloc_id) { - // const eval results can refer to "locals". - // E.g. `const Foo: &u32 = &1;` refers to the temp local that stores the `1` - None | - Some(&MemoryKind::Stack) => {}, - Some(&MemoryKind::Machine(m)) => M::mark_static_initialized(m)?, - Some(&MemoryKind::MutableStatic) => { - trace!("mark_static_initalized: skipping already initialized static referred to by static currently being initialized"); - return Ok(()); - }, - } - // overwrite or insert - self.alloc_kind.insert(alloc_id, MemoryKind::MutableStatic); - // take out the relocations vector to free the borrow on self, so we can call - // mark recursively - mem::replace(relocations, Default::default()) + let uninit = self.uninitialized_statics.remove(&alloc_id); + if let Some(mut alloc) = alloc.or(uninit) { + // ensure llvm knows not to put this into immutable memroy + alloc.mutable = mutability == Mutability::Mutable; + let alloc = self.tcx.intern_const_alloc(alloc); + self.tcx.interpret_interner.borrow_mut().intern_at_reserved(alloc_id, alloc); + // recurse into inner allocations + for &alloc in alloc.relocations.values() { + self.mark_inner_allocation_initialized(alloc, mutability)?; } - None => return err!(DanglingPointerDeref), - }; - // recurse into inner allocations - for &alloc in relocations.values() { - self.mark_inner_allocation_initialized(alloc, mutability)?; } - // put back the relocations - self.alloc_map - .get_mut(&alloc_id) - .expect("checked above") - .relocations = relocations; Ok(()) } @@ -829,7 +782,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { } /// Relocations -impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { fn relocations( &self, ptr: MemoryPointer, @@ -883,7 +836,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { } /// Undefined bytes -impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { // FIXME(solson): This is a very naive, slow version. fn copy_undef_mask( &mut self, @@ -944,7 +897,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { // Methods to access integers in the target endianness //////////////////////////////////////////////////////////////////////////////// -fn write_target_uint( +pub fn write_target_uint( endianness: layout::Endian, mut target: &mut [u8], data: u128, @@ -955,7 +908,8 @@ fn write_target_uint( layout::Endian::Big => target.write_uint128::(data, len), } } -fn write_target_int( + +pub fn write_target_int( endianness: layout::Endian, mut target: &mut [u8], data: i128, @@ -967,14 +921,14 @@ fn write_target_int( } } -fn read_target_uint(endianness: layout::Endian, mut source: &[u8]) -> Result { +pub fn read_target_uint(endianness: layout::Endian, mut source: &[u8]) -> Result { match endianness { layout::Endian::Little => source.read_uint128::(source.len()), layout::Endian::Big => source.read_uint128::(source.len()), } } -fn read_target_int(endianness: layout::Endian, mut source: &[u8]) -> Result { +pub fn read_target_int(endianness: layout::Endian, mut source: &[u8]) -> Result { match endianness { layout::Endian::Little => source.read_int128::(source.len()), layout::Endian::Big => source.read_int128::(source.len()), @@ -985,9 +939,9 @@ fn read_target_int(endianness: layout::Endian, mut source: &[u8]) -> Result> { - fn memory_mut(&mut self) -> &mut Memory<'a, 'tcx, M>; - fn memory(&self) -> &Memory<'a, 'tcx, M>; +pub trait HasMemory<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { + fn memory_mut(&mut self) -> &mut Memory<'a, 'mir, 'tcx, M>; + fn memory(&self) -> &Memory<'a, 'mir, 'tcx, M>; /// Convert the value into a pointer (or a pointer-sized integer). If the value is a ByRef, /// this may have to perform a load. @@ -1051,31 +1005,31 @@ pub trait HasMemory<'a, 'tcx: 'a, M: Machine<'tcx>> { } } -impl<'a, 'tcx, M: Machine<'tcx>> HasMemory<'a, 'tcx, M> for Memory<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasMemory<'a, 'mir, 'tcx, M> for Memory<'a, 'mir, 'tcx, M> { #[inline] - fn memory_mut(&mut self) -> &mut Memory<'a, 'tcx, M> { + fn memory_mut(&mut self) -> &mut Memory<'a, 'mir, 'tcx, M> { self } #[inline] - fn memory(&self) -> &Memory<'a, 'tcx, M> { + fn memory(&self) -> &Memory<'a, 'mir, 'tcx, M> { self } } -impl<'a, 'tcx, M: Machine<'tcx>> HasMemory<'a, 'tcx, M> for EvalContext<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasMemory<'a, 'mir, 'tcx, M> for EvalContext<'a, 'mir, 'tcx, M> { #[inline] - fn memory_mut(&mut self) -> &mut Memory<'a, 'tcx, M> { + fn memory_mut(&mut self) -> &mut Memory<'a, 'mir, 'tcx, M> { &mut self.memory } #[inline] - fn memory(&self) -> &Memory<'a, 'tcx, M> { + fn memory(&self) -> &Memory<'a, 'mir, 'tcx, M> { &self.memory } } -impl<'a, 'tcx, M: Machine<'tcx>> layout::HasDataLayout for &'a Memory<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> layout::HasDataLayout for &'a Memory<'a, 'mir, 'tcx, M> { #[inline] fn data_layout(&self) -> &TargetDataLayout { &self.tcx.data_layout diff --git a/src/librustc_mir/interpret/mod.rs b/src/librustc_mir/interpret/mod.rs index ba894a1728a9b..f23ba90fd4c3f 100644 --- a/src/librustc_mir/interpret/mod.rs +++ b/src/librustc_mir/interpret/mod.rs @@ -18,6 +18,17 @@ pub use self::place::{Place, PlaceExtra}; pub use self::memory::{Memory, MemoryKind, HasMemory}; -pub use self::const_eval::{eval_body_as_integer, eval_body, CompileTimeEvaluator, const_eval_provider, const_val_field, const_discr, check_body}; +pub use self::const_eval::{ + eval_body_with_mir, + mk_borrowck_eval_cx, + eval_body, + CompileTimeEvaluator, + const_eval_provider, + const_val_field, + const_discr, +}; pub use self::machine::Machine; + +pub use self::operator::unary_op; +pub use self::memory::{write_target_uint, write_target_int, read_target_uint, read_target_int}; diff --git a/src/librustc_mir/interpret/operator.rs b/src/librustc_mir/interpret/operator.rs index b20540b00ceae..bad744194d5e3 100644 --- a/src/librustc_mir/interpret/operator.rs +++ b/src/librustc_mir/interpret/operator.rs @@ -8,7 +8,7 @@ use super::{EvalContext, Place, Machine, ValTy}; use rustc::mir::interpret::{EvalResult, PrimVal, PrimValKind, Value, bytes_to_f32, bytes_to_f64}; -impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { fn binop_with_overflow( &mut self, op: mir::BinOp, @@ -56,6 +56,24 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } macro_rules! overflow { + (overflowing_div, $l:expr, $r:expr) => ({ + let (val, overflowed) = if $r == 0 { + ($l, true) + } else { + $l.overflowing_div($r) + }; + let primval = PrimVal::Bytes(val as u128); + Ok((primval, overflowed)) + }); + (overflowing_rem, $l:expr, $r:expr) => ({ + let (val, overflowed) = if $r == 0 { + ($l, true) + } else { + $l.overflowing_rem($r) + }; + let primval = PrimVal::Bytes(val as u128); + Ok((primval, overflowed)) + }); ($op:ident, $l:expr, $r:expr) => ({ let (val, overflowed) = $l.$op($r); let primval = PrimVal::Bytes(val as u128); @@ -105,7 +123,7 @@ macro_rules! int_shift { }) } -impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { /// Returns the result of the specified operation and whether it overflowed. pub fn binary_op( &self, diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 4a2b4547cb051..349ac63055992 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -1,11 +1,13 @@ use rustc::mir; use rustc::ty::{self, Ty}; use rustc::ty::layout::{self, Align, LayoutOf, TyLayout}; +use rustc::traits; use rustc_data_structures::indexed_vec::Idx; use rustc::mir::interpret::{GlobalId, Value, PrimVal, EvalResult, Pointer, MemoryPointer}; use super::{EvalContext, Machine, ValTy}; use interpret::memory::HasMemory; +use rustc::middle::const_val::ErrKind; #[derive(Copy, Clone, Debug)] pub enum Place { @@ -90,7 +92,7 @@ impl<'tcx> Place { } } -impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { /// Reads a value from the place without going through the intermediate step of obtaining /// a `miri::Place` pub fn try_read_place( @@ -106,10 +108,10 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { // Directly reading a static will always succeed Static(ref static_) => { let instance = ty::Instance::mono(self.tcx, static_.def_id); - Ok(Some(self.read_global_as_value(GlobalId { + self.read_global_as_value(GlobalId { instance, promoted: None, - }, self.layout_of(self.place_ty(place))?))) + }, self.place_ty(place)).map(Some) } Projection(ref proj) => self.try_read_place_projection(proj), } @@ -199,17 +201,44 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { }, Static(ref static_) => { - let instance = ty::Instance::mono(self.tcx, static_.def_id); - let gid = GlobalId { - instance, - promoted: None, - }; + let alloc = self + .tcx + .interpret_interner + .borrow() + .get_cached(static_.def_id); let layout = self.layout_of(self.place_ty(mir_place))?; - let alloc = self.tcx.interpret_interner.borrow().get_cached(gid).expect("uncached global"); - Place::Ptr { - ptr: MemoryPointer::new(alloc, 0).into(), - align: layout.align, - extra: PlaceExtra::None, + if let Some(alloc) = alloc { + Place::Ptr { + ptr: MemoryPointer::new(alloc, 0).into(), + align: layout.align, + extra: PlaceExtra::None, + } + } else { + let instance = ty::Instance::mono(self.tcx, static_.def_id); + let cid = GlobalId { + instance, + promoted: None + }; + let param_env = ty::ParamEnv::empty(traits::Reveal::All); + // ensure the static is computed + if let Err(err) = self.tcx.const_eval(param_env.and(cid)) { + match err.kind { + ErrKind::Miri(miri) => return Err(miri), + ErrKind::TypeckError => return err!(TypeckError), + other => bug!("const eval returned {:?}", other), + } + }; + let alloc = self + .tcx + .interpret_interner + .borrow() + .get_cached(static_.def_id) + .expect("uncached static"); + Place::Ptr { + ptr: MemoryPointer::new(alloc, 0).into(), + align: layout.align, + extra: PlaceExtra::None, + } } } diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs index 2b0f9041d5115..21e81ff668ea5 100644 --- a/src/librustc_mir/interpret/step.rs +++ b/src/librustc_mir/interpret/step.rs @@ -2,21 +2,12 @@ //! //! The main entry point is the `step` method. -use rustc::hir; -use rustc::mir::visit::{Visitor, PlaceContext}; use rustc::mir; -use rustc::ty::{self, Instance}; -use rustc::ty::layout::LayoutOf; -use rustc::middle::const_val::ConstVal; -use rustc::mir::interpret::GlobalId; -use rustc::mir::interpret::{EvalResult, EvalErrorKind}; -use super::{EvalContext, StackPopCleanup, Place, Machine}; +use rustc::mir::interpret::EvalResult; +use super::{EvalContext, Machine}; -use syntax::codemap::Span; -use syntax::ast::Mutability; - -impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { pub fn inc_step_counter_and_check_limit(&mut self, n: u64) -> EvalResult<'tcx> { self.steps_remaining = self.steps_remaining.saturating_sub(n); if self.steps_remaining > 0 { @@ -41,52 +32,14 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { let old_frames = self.cur_frame(); if let Some(stmt) = basic_block.statements.get(stmt_id) { - let mut new = Ok(false); - ConstantExtractor { - span: stmt.source_info.span, - instance: self.frame().instance, - ecx: self, - mir, - new_constant: &mut new, - }.visit_statement( - block, - stmt, - mir::Location { - block, - statement_index: stmt_id, - }, - ); - // if ConstantExtractor added a new frame, we don't execute anything here - // but await the next call to step - if !new? { - assert_eq!(old_frames, self.cur_frame()); - self.statement(stmt)?; - } + assert_eq!(old_frames, self.cur_frame()); + self.statement(stmt)?; return Ok(true); } let terminator = basic_block.terminator(); - let mut new = Ok(false); - ConstantExtractor { - span: terminator.source_info.span, - instance: self.frame().instance, - ecx: self, - mir, - new_constant: &mut new, - }.visit_terminator( - block, - terminator, - mir::Location { - block, - statement_index: stmt_id, - }, - ); - // if ConstantExtractor added a new frame, we don't execute anything here - // but await the next call to step - if !new? { - assert_eq!(old_frames, self.cur_frame()); - self.terminator(terminator)?; - } + assert_eq!(old_frames, self.cur_frame()); + self.terminator(terminator)?; Ok(true) } @@ -152,184 +105,4 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } Ok(()) } - - /// returns `true` if a stackframe was pushed - fn global_item( - &mut self, - instance: Instance<'tcx>, - span: Span, - mutability: Mutability, - ) -> EvalResult<'tcx, bool> { - debug!("global_item: {:?}", instance); - let cid = GlobalId { - instance, - promoted: None, - }; - if self.tcx.interpret_interner.borrow().get_cached(cid).is_some() { - return Ok(false); - } - if self.tcx.has_attr(instance.def_id(), "linkage") { - M::global_item_with_linkage(self, cid.instance, mutability)?; - return Ok(false); - } - let instance_ty = instance.ty(self.tcx); - let layout = self.layout_of(instance_ty)?; - assert!(!layout.is_unsized()); - let ptr = self.memory.allocate( - layout.size.bytes(), - layout.align, - None, - )?; - self.tcx.interpret_interner.borrow_mut().cache(cid, ptr.alloc_id); - let internally_mutable = !layout.ty.is_freeze(self.tcx, self.param_env, span); - let mutability = if mutability == Mutability::Mutable || internally_mutable { - Mutability::Mutable - } else { - Mutability::Immutable - }; - let cleanup = StackPopCleanup::MarkStatic(mutability); - let name = ty::tls::with(|tcx| tcx.item_path_str(instance.def_id())); - trace!("pushing stack frame for global: {}", name); - let mir = self.load_mir(instance.def)?; - self.push_stack_frame( - instance, - span, - mir, - Place::from_ptr(ptr, layout.align), - cleanup, - )?; - Ok(true) - } -} - -struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b, M: Machine<'tcx> + 'a> { - span: Span, - ecx: &'a mut EvalContext<'b, 'tcx, M>, - mir: &'tcx mir::Mir<'tcx>, - instance: ty::Instance<'tcx>, - // Whether a stackframe for a new constant has been pushed - new_constant: &'a mut EvalResult<'tcx, bool>, -} - -impl<'a, 'b, 'tcx, M: Machine<'tcx>> ConstantExtractor<'a, 'b, 'tcx, M> { - fn try EvalResult<'tcx, bool>>(&mut self, f: F) { - match *self.new_constant { - // already computed a constant, don't do more than one per iteration - Ok(true) => {}, - // no constants computed yet - Ok(false) => *self.new_constant = f(self), - // error happened, abort the visitor traversing - Err(_) => {}, - } - } -} - -impl<'a, 'b, 'tcx, M: Machine<'tcx>> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx, M> { - fn visit_constant(&mut self, constant: &mir::Constant<'tcx>, location: mir::Location) { - self.super_constant(constant, location); - self.try(|this| { - match constant.literal { - // already computed by rustc - mir::Literal::Value { value: &ty::Const { val: ConstVal::Unevaluated(def_id, substs), .. } } => { - debug!("global_item: {:?}, {:#?}", def_id, substs); - let substs = this.ecx.tcx.trans_apply_param_substs(this.instance.substs, &substs); - debug!("global_item_new_substs: {:#?}", substs); - debug!("global_item_param_env: {:#?}", this.ecx.param_env); - let instance = Instance::resolve( - this.ecx.tcx, - this.ecx.param_env, - def_id, - substs, - ).ok_or(EvalErrorKind::TypeckError)?; // turn error prop into a panic to expose associated type in const issue - this.ecx.global_item( - instance, - constant.span, - Mutability::Immutable, - ) - } - mir::Literal::Value { .. } => Ok(false), - mir::Literal::Promoted { index } => { - let cid = GlobalId { - instance: this.instance, - promoted: Some(index), - }; - if this.ecx.tcx.interpret_interner.borrow().get_cached(cid).is_some() { - return Ok(false); - } - let mir = &this.mir.promoted[index]; - let ty = this.ecx.monomorphize(mir.return_ty(), this.instance.substs); - let layout = this.ecx.layout_of(ty)?; - assert!(!layout.is_unsized()); - let ptr = this.ecx.memory.allocate( - layout.size.bytes(), - layout.align, - None, - )?; - this.ecx.tcx.interpret_interner.borrow_mut().cache(cid, ptr.alloc_id); - trace!("pushing stack frame for {:?}", index); - this.ecx.push_stack_frame( - this.instance, - constant.span, - mir, - Place::from_ptr(ptr, layout.align), - StackPopCleanup::MarkStatic(Mutability::Immutable), - )?; - Ok(true) - } - } - }); - } - - fn visit_place( - &mut self, - place: &mir::Place<'tcx>, - context: PlaceContext<'tcx>, - location: mir::Location, - ) { - self.super_place(place, context, location); - self.try(|this| { - if let mir::Place::Static(ref static_) = *place { - let def_id = static_.def_id; - let span = this.span; - if let Some(node_item) = this.ecx.tcx.hir.get_if_local(def_id) { - if let hir::map::Node::NodeItem(&hir::Item { ref node, .. }) = node_item { - if let hir::ItemStatic(_, m, _) = *node { - let instance = Instance::mono(this.ecx.tcx, def_id); - this.ecx.global_item( - instance, - span, - if m == hir::MutMutable { - Mutability::Mutable - } else { - Mutability::Immutable - }, - ) - } else { - bug!("static def id doesn't point to static"); - } - } else { - bug!("static def id doesn't point to item"); - } - } else { - let def = this.ecx.tcx.describe_def(def_id).expect("static not found"); - if let hir::def::Def::Static(_, mutable) = def { - let instance = Instance::mono(this.ecx.tcx, def_id); - this.ecx.global_item( - instance, - span, - if mutable { - Mutability::Mutable - } else { - Mutability::Immutable - }, - ) - } else { - bug!("static found but isn't a static: {:?}", def); - } - } - } else { - Ok(false) - } - }); - } } diff --git a/src/librustc_mir/interpret/terminator/drop.rs b/src/librustc_mir/interpret/terminator/drop.rs index c5942712b87dd..fbc0c499e59de 100644 --- a/src/librustc_mir/interpret/terminator/drop.rs +++ b/src/librustc_mir/interpret/terminator/drop.rs @@ -5,7 +5,7 @@ use syntax::codemap::Span; use rustc::mir::interpret::{EvalResult, PrimVal, Value}; use interpret::{Machine, ValTy, EvalContext, Place, PlaceExtra}; -impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { pub(crate) fn drop_place( &mut self, place: Place, diff --git a/src/librustc_mir/interpret/terminator/mod.rs b/src/librustc_mir/interpret/terminator/mod.rs index c18cf6d9f96b9..b1b5f77a2e6ef 100644 --- a/src/librustc_mir/interpret/terminator/mod.rs +++ b/src/librustc_mir/interpret/terminator/mod.rs @@ -13,7 +13,7 @@ use interpret::memory::HasMemory; mod drop; -impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { pub fn goto_block(&mut self, target: mir::BasicBlock) { self.frame_mut().block = target; self.frame_mut().stmt = 0; diff --git a/src/librustc_mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs index 22417201f0dc5..4dc0879c85d52 100644 --- a/src/librustc_mir/interpret/traits.rs +++ b/src/librustc_mir/interpret/traits.rs @@ -6,7 +6,7 @@ use rustc::mir::interpret::{PrimVal, Value, MemoryPointer, EvalResult}; use super::{EvalContext, eval_context, Machine}; -impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { /// Creates a dynamic vtable for the given type and vtable origin. This is used only for /// objects. /// @@ -54,7 +54,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { self.memory.mark_static_initalized( vtable.alloc_id, - Mutability::Mutable, + Mutability::Immutable, )?; Ok(vtable) diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index 0f512569adf97..0a8fd022dd1ac 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -194,6 +194,7 @@ use rustc::hir::itemlikevisit::ItemLikeVisitor; use rustc::hir::map as hir_map; use rustc::hir::def_id::DefId; use rustc::middle::const_val::ConstVal; +use rustc::mir::interpret::{Value, PrimVal, AllocId, Pointer}; use rustc::middle::lang_items::{ExchangeMallocFnLangItem, StartFnLangItem}; use rustc::traits; use rustc::ty::subst::{Substs, Kind}; @@ -568,14 +569,26 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, location: Location) { debug!("visiting const {:?} @ {:?}", *constant, location); - if let ConstVal::Unevaluated(def_id, substs) = constant.val { - let substs = self.tcx.trans_apply_param_substs(self.param_substs, - &substs); - let instance = ty::Instance::resolve(self.tcx, - ty::ParamEnv::empty(traits::Reveal::All), - def_id, - substs).unwrap(); - collect_neighbours(self.tcx, instance, true, self.output); + match constant.val { + ConstVal::Unevaluated(def_id, substs) => { + let substs = self.tcx.trans_apply_param_substs(self.param_substs, + &substs); + let instance = ty::Instance::resolve(self.tcx, + ty::ParamEnv::empty(traits::Reveal::All), + def_id, + substs).unwrap(); + collect_neighbours(self.tcx, instance, true, self.output); + }, + ConstVal::Value(Value::ByValPair(PrimVal::Ptr(a), PrimVal::Ptr(b))) => { + collect_miri(self.tcx, a.alloc_id, self.output); + collect_miri(self.tcx, b.alloc_id, self.output); + } + ConstVal::Value(Value::ByValPair(_, PrimVal::Ptr(ptr))) | + ConstVal::Value(Value::ByValPair(PrimVal::Ptr(ptr), _)) | + ConstVal::Value(Value::ByVal(PrimVal::Ptr(ptr))) | + ConstVal::Value(Value::ByRef(Pointer { primval: PrimVal::Ptr(ptr) }, _)) => + collect_miri(self.tcx, ptr.alloc_id, self.output), + _ => {}, } self.super_const(constant); @@ -1098,6 +1111,28 @@ fn create_mono_items_for_default_impls<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } +/// Scan the miri alloc in order to find function calls, closures, and drop-glue +fn collect_miri<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + alloc_id: AllocId, + output: &mut Vec>, +) { + let interpret_interner = tcx.interpret_interner.borrow(); + if let Some(alloc) = interpret_interner.get_alloc(alloc_id) { + trace!("collecting {:?} with {:#?}", alloc_id, alloc); + for &inner in alloc.relocations.values() { + collect_miri(tcx, inner, output); + } + } else if let Some(fn_instance) = interpret_interner.get_fn(alloc_id) { + if should_monomorphize_locally(tcx, &fn_instance) { + trace!("collecting {:?} with {:#?}", alloc_id, fn_instance); + output.push(create_fn_mono_item(fn_instance)); + } + } else { + bug!("alloc id without corresponding allocation: {}", alloc_id); + } +} + /// Scan the MIR in order to find function calls, closures, and drop-glue fn collect_neighbours<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>, diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs index 0bef9f0602d86..86d08dec2b9c3 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -213,7 +213,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { // locals are safe } &Place::Static(box Static { def_id, ty: _ }) => { - if self.tcx.is_static_mut(def_id) { + if self.tcx.is_static(def_id) == Some(hir::Mutability::MutMutable) { self.require_unsafe("use of mutable static"); } else if self.tcx.is_foreign_item(def_id) { let source_info = self.source_info; diff --git a/src/librustc_mir/transform/instcombine.rs b/src/librustc_mir/transform/instcombine.rs index 8856d263864cd..e27e7c72473be 100644 --- a/src/librustc_mir/transform/instcombine.rs +++ b/src/librustc_mir/transform/instcombine.rs @@ -11,44 +11,52 @@ //! Performs various peephole optimizations. use rustc::mir::{Constant, Literal, Location, Place, Mir, Operand, ProjectionElem, Rvalue, Local}; -use rustc::mir::visit::{MutVisitor, Visitor}; -use rustc::ty::{TyCtxt, TypeVariants}; +use rustc::mir::{NullOp, StatementKind, Statement, BasicBlock}; +use rustc::mir::{SourceInfo, ARGUMENT_VISIBILITY_SCOPE, TerminatorKind}; +use rustc::mir::visit::{MutVisitor, Visitor, TyContext}; +use rustc::middle::const_val::ConstVal; +use rustc::ty::{TyCtxt, TypeVariants, self, Instance}; +use rustc::mir::interpret::{Value, PrimVal, GlobalId}; +use interpret::{eval_body_with_mir, eval_body, mk_borrowck_eval_cx, unary_op, ValTy}; use rustc::util::nodemap::{FxHashMap, FxHashSet}; use rustc_data_structures::indexed_vec::Idx; use std::mem; +use std::collections::VecDeque; use transform::{MirPass, MirSource}; +use syntax::codemap::{Span, DUMMY_SP}; +use rustc_data_structures::control_flow_graph::ControlFlowGraph; +use rustc::ty::subst::Substs; pub struct InstCombine; impl MirPass for InstCombine { fn run_pass<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, - _: MirSource, + source: MirSource, mir: &mut Mir<'tcx>) { - // We only run when optimizing MIR (at any level). - if tcx.sess.opts.debugging_opts.mir_opt_level == 0 { - return - } // First, find optimization opportunities. This is done in a pre-pass to keep the MIR // read-only so that we can do global analyses on the MIR in the process (e.g. // `Place::ty()`). let optimizations = { - let mut optimization_finder = OptimizationFinder::new(mir, tcx); + let mut optimization_finder = OptimizationFinder::new(mir, tcx, source); optimization_finder.visit_mir(mir); optimization_finder.optimizations }; // Then carry out those optimizations. - MutVisitor::visit_mir(&mut InstCombineVisitor { optimizations }, mir); + MutVisitor::visit_mir(&mut InstCombineVisitor { optimizations, tcx }, mir); } } -pub struct InstCombineVisitor<'tcx> { +type Const<'tcx> = (Value, ty::Ty<'tcx>, Span); + +pub struct InstCombineVisitor<'a, 'tcx: 'a> { optimizations: OptimizationList<'tcx>, + tcx: TyCtxt<'a, 'tcx, 'tcx>, } -impl<'tcx> MutVisitor<'tcx> for InstCombineVisitor<'tcx> { +impl<'a, 'tcx> MutVisitor<'tcx> for InstCombineVisitor<'a, 'tcx> { fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) { if self.optimizations.and_stars.remove(&location) { debug!("Replacing `&*`: {:?}", rvalue); @@ -67,28 +75,460 @@ impl<'tcx> MutVisitor<'tcx> for InstCombineVisitor<'tcx> { *rvalue = Rvalue::Use(Operand::Constant(box constant)); } + if let Some((value, ty, span)) = self.optimizations.const_prop.remove(&location) { + let value = self.tcx.mk_const(ty::Const { + val: ConstVal::Value(value), + ty, + }); + debug!("Replacing `{:?}` with {:?}", rvalue, value); + let constant = Constant { + ty, + literal: Literal::Value { value }, + span, + }; + *rvalue = Rvalue::Use(Operand::Constant(box constant)); + } + self.super_rvalue(rvalue, location) } + + fn visit_constant( + &mut self, + constant: &mut Constant<'tcx>, + location: Location, + ) { + self.super_constant(constant, location); + if let Some(&(val, ty, _)) = self.optimizations.constants.get(constant) { + constant.literal = Literal::Value { + value: self.tcx.mk_const(ty::Const { + val: ConstVal::Value(val), + ty, + }), + }; + } + } + + fn visit_operand( + &mut self, + operand: &mut Operand<'tcx>, + location: Location, + ) { + self.super_operand(operand, location); + let new = match operand { + Operand::Move(Place::Local(local)) | + Operand::Copy(Place::Local(local)) => { + trace!("trying to read {:?}", local); + self.optimizations.places.get(&local).cloned() + }, + _ => return, + }; + if let Some((value, ty, span)) = new { + let value = self.tcx.mk_const(ty::Const { + val: ConstVal::Value(value), + ty, + }); + debug!("Replacing `{:?}` with {:?}", operand, value); + let constant = Constant { + ty, + literal: Literal::Value { value }, + span, + }; + *operand = Operand::Constant(box constant); + } + } + + fn visit_terminator_kind( + &mut self, + block: BasicBlock, + kind: &mut TerminatorKind<'tcx>, + location: Location, + ) { + match kind { + TerminatorKind::SwitchInt { discr: value, .. } | + TerminatorKind::Yield { value, .. } | + TerminatorKind::Assert { cond: value, .. } => { + if let Some((new, ty, span)) = self.optimizations.const_prop.remove(&location) { + let new = self.tcx.mk_const(ty::Const { + val: ConstVal::Value(new), + ty, + }); + debug!("Replacing `{:?}` with {:?}", value, new); + let constant = Constant { + ty, + literal: Literal::Value { value: new }, + span, + }; + *value = Operand::Constant(box constant); + } + } + // FIXME: do this optimization for function calls + _ => {}, + } + self.super_terminator_kind(block, kind, location) + } } /// Finds optimization opportunities on the MIR. struct OptimizationFinder<'b, 'a, 'tcx:'a+'b> { mir: &'b Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>, + source: MirSource, optimizations: OptimizationList<'tcx>, } impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { - fn new(mir: &'b Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> OptimizationFinder<'b, 'a, 'tcx> { + fn new( + mir: &'b Mir<'tcx>, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + source: MirSource, + ) -> OptimizationFinder<'b, 'a, 'tcx> { OptimizationFinder { mir, tcx, + source, optimizations: OptimizationList::default(), } } + + fn eval_constant(&mut self, c: &Constant<'tcx>, span: Span) -> Option> { + if let Some(&val) = self.optimizations.constants.get(c) { + return Some(val); + } + match c.literal { + Literal::Value { value } => match value.val { + ConstVal::Value(v) => Some((v, value.ty, span)), + ConstVal::Unevaluated(did, substs) => { + let param_env = self.tcx.param_env(self.source.def_id); + let span = self.tcx.def_span(did); + let instance = Instance::resolve( + self.tcx, + param_env, + did, + substs, + )?; + let cid = GlobalId { + instance, + promoted: None, + }; + let (value, _, ty) = eval_body(self.tcx, cid, param_env)?; + let val = (value, ty, span); + trace!("evaluated {:?} to {:?}", c, val); + self.optimizations.constants.insert(c.clone(), val); + Some(val) + }, + }, + // evaluate the promoted and replace the constant with the evaluated result + Literal::Promoted { index } => { + let generics = self.tcx.generics_of(self.source.def_id); + if generics.parent_types as usize + generics.types.len() > 0 { + // FIXME: can't handle code with generics + return None; + } + let substs = Substs::identity_for_item(self.tcx, self.source.def_id); + let instance = Instance::new(self.source.def_id, substs); + let cid = GlobalId { + instance, + promoted: Some(index), + }; + let span = self.tcx.def_span(self.source.def_id); + let param_env = self.tcx.param_env(self.source.def_id); + let (value, _, ty) = eval_body_with_mir(self.tcx, cid, self.mir, param_env)?; + let val = (value, ty, span); + trace!("evaluated {:?} to {:?}", c, val); + self.optimizations.constants.insert(c.clone(), val); + Some(val) + } + } + } + + fn eval_operand(&mut self, op: &Operand<'tcx>, span: Span) -> Option> { + match *op { + Operand::Constant(ref c) => self.eval_constant(c, span), + Operand::Move(ref place) | Operand::Copy(ref place) => match *place { + Place::Local(loc) => self.optimizations.places.get(&loc).cloned(), + // FIXME(oli-obk): field and index projections + Place::Projection(_) => None, + _ => None, + }, + } + } + + fn simplify_operand(&mut self, op: &Operand<'tcx>, span: Span) -> Option> { + match *op { + Operand::Constant(ref c) => match c.literal { + Literal::Value { .. } => None, + _ => self.eval_operand(op, span), + }, + _ => self.eval_operand(op, span), + } + } + + fn const_prop( + &mut self, + rvalue: &Rvalue<'tcx>, + place_ty: ty::Ty<'tcx>, + span: Span, + ) -> Option> { + match *rvalue { + Rvalue::Use(ref op) => self.simplify_operand(op, span), + Rvalue::Repeat(..) | + Rvalue::Ref(..) | + Rvalue::Cast(..) | + Rvalue::Aggregate(..) | + Rvalue::NullaryOp(NullOp::Box, _) | + Rvalue::Discriminant(..) => None, + // FIXME(oli-obk): evaluate static/constant slice lengths + Rvalue::Len(_) => None, + Rvalue::NullaryOp(NullOp::SizeOf, ty) => { + let param_env = self.tcx.param_env(self.source.def_id); + type_size_of(self.tcx, param_env, ty).map(|n| ( + Value::ByVal(PrimVal::Bytes(n as u128)), + self.tcx.types.usize, + span, + )) + } + Rvalue::UnaryOp(op, ref arg) => { + let def_id = if self.tcx.is_closure(self.source.def_id) { + self.tcx.closure_base_def_id(self.source.def_id) + } else { + self.source.def_id + }; + let generics = self.tcx.generics_of(def_id); + if generics.parent_types as usize + generics.types.len() > 0 { + // FIXME: can't handle code with generics + return None; + } + let substs = Substs::identity_for_item(self.tcx, self.source.def_id); + let instance = Instance::new(self.source.def_id, substs); + let ecx = mk_borrowck_eval_cx(self.tcx, instance, self.mir).unwrap(); + + let val = self.eval_operand(arg, span)?; + let prim = ecx.value_to_primval(ValTy { value: val.0, ty: val.1 }).ok()?; + let kind = ecx.ty_to_primval_kind(val.1).ok()?; + match unary_op(op, prim, kind) { + Ok(val) => Some((Value::ByVal(val), place_ty, span)), + Err(mut err) => { + ecx.report(&mut err, false); + None + }, + } + } + Rvalue::CheckedBinaryOp(op, ref left, ref right) | + Rvalue::BinaryOp(op, ref left, ref right) => { + trace!("rvalue binop {:?} for {:?} and {:?}", op, left, right); + let left = self.eval_operand(left, span)?; + let right = self.eval_operand(right, span)?; + let def_id = if self.tcx.is_closure(self.source.def_id) { + self.tcx.closure_base_def_id(self.source.def_id) + } else { + self.source.def_id + }; + let generics = self.tcx.generics_of(def_id); + let has_generics = generics.parent_types as usize + generics.types.len() > 0; + if has_generics { + // FIXME: can't handle code with generics + return None; + } + let substs = Substs::identity_for_item(self.tcx, self.source.def_id); + let instance = Instance::new(self.source.def_id, substs); + let ecx = mk_borrowck_eval_cx(self.tcx, instance, self.mir).unwrap(); + + let l = ecx.value_to_primval(ValTy { value: left.0, ty: left.1 }).ok()?; + let r = ecx.value_to_primval(ValTy { value: right.0, ty: right.1 }).ok()?; + trace!("const evaluating {:?} for {:?} and {:?}", op, left, right); + match ecx.binary_op(op, l, left.1, r, right.1) { + Ok((val, overflow)) => { + let val = if let Rvalue::CheckedBinaryOp(..) = *rvalue { + Value::ByValPair( + val, + PrimVal::from_bool(overflow), + ) + } else { + if overflow { + use rustc::mir::interpret::EvalError; + use rustc::mir::interpret::EvalErrorKind; + let mut err = EvalError { + kind: EvalErrorKind::OverflowingMath, + backtrace: None, + }; + ecx.report(&mut err, false); + return None; + } + Value::ByVal(val) + }; + Some((val, place_ty, span)) + }, + Err(mut err) => { + ecx.report(&mut err, false); + None + }, + } + }, + } + } +} + +fn type_size_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty: ty::Ty<'tcx>) -> Option { + use rustc::ty::layout::LayoutOf; + (tcx, param_env).layout_of(ty).ok().map(|layout| layout.size.bytes()) +} + +struct ConstPropVisitor { + local: Local, + can_const_prop: bool, + // false at the beginning, once set, there are not allowed to be any more assignments + found_assignment: bool, +} + +impl ConstPropVisitor { + /// returns true if `local` can be propagated + fn check<'tcx>(local: Local, mir: &Mir<'tcx>) -> bool { + let mut cpv = ConstPropVisitor { + local, + can_const_prop: true, + found_assignment: false, + }; + cpv.visit_mir(mir); + cpv.can_const_prop + } +} + +impl<'tcx> Visitor<'tcx> for ConstPropVisitor { + fn visit_statement( + &mut self, + block: BasicBlock, + statement: &Statement<'tcx>, + location: Location, + ) { + self.super_statement(block, statement, location); + match statement.kind { + StatementKind::SetDiscriminant { place: Place::Local(local), .. } | + StatementKind::Assign(Place::Local(local), _) => { + if local == self.local { + if self.found_assignment { + self.can_const_prop = false; + } else { + self.found_assignment = true + } + } + }, + StatementKind::InlineAsm { ref outputs, .. } => { + for place in outputs { + if let Place::Local(local) = *place { + if local == self.local { + if self.found_assignment { + self.can_const_prop = false; + } else { + self.found_assignment = true + } + return; + } + } + } + } + _ => {} + } + } + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { + self.super_rvalue(rvalue, location); + if let Rvalue::Ref(_, _, Place::Local(local)) = *rvalue { + if local == self.local { + self.can_const_prop = false; + } + } + } } impl<'b, 'a, 'tcx> Visitor<'tcx> for OptimizationFinder<'b, 'a, 'tcx> { + // override to visit basic blocks in execution order + fn super_mir(&mut self, mir: &Mir<'tcx>) { + let mut seen = FxHashSet::default(); + seen.insert(mir.start_node()); + let mut sorted = Vec::new(); + let mut next = VecDeque::new(); + sorted.push(mir.start_node()); + next.push_back(mir.start_node()); + while let Some(current) = next.pop_front() { + for successor in mir.successors(current) { + trace!("checking successor of {:?}: {:?}", current, successor); + trace!("{:?}, {:?}", sorted, next); + if seen.contains(&successor) { + for &pending in &next { + // not a back-edge, just a branch merging back into a single execution + if pending == successor { + // move to the back of the queue + let i = sorted.iter().position(|&b| b == successor).unwrap(); + sorted.remove(i); + sorted.push(successor); + break; + } + } + } else { + seen.insert(successor); + sorted.push(successor); + next.push_back(successor); + } + } + } + trace!("checking basic blocks: {:?}", sorted); + for bb in sorted { + self.visit_basic_block_data(bb, &mir[bb]); + } + + for scope in &mir.visibility_scopes { + self.visit_visibility_scope_data(scope); + } + + self.visit_ty(&mir.return_ty(), TyContext::ReturnTy(SourceInfo { + span: mir.span, + scope: ARGUMENT_VISIBILITY_SCOPE, + })); + + for local in mir.local_decls.indices() { + self.visit_local_decl(local, &mir.local_decls[local]); + } + + self.visit_span(&mir.span); + } + + fn visit_constant( + &mut self, + constant: &Constant<'tcx>, + location: Location, + ) { + trace!("visit_constant: {:?}", constant); + self.super_constant(constant, location); + self.eval_constant(constant, DUMMY_SP); + } + + fn visit_statement( + &mut self, + block: BasicBlock, + statement: &Statement<'tcx>, + location: Location, + ) { + trace!("visit_statement: {:?}", statement); + if let StatementKind::Assign(ref place, ref rval) = statement.kind { + let place_ty = place + .ty(&self.mir.local_decls, self.tcx) + .to_ty(self.tcx); + let span = self.mir.source_info(location).span; + if let Some(value) = self.const_prop(rval, place_ty, span) { + self.optimizations.const_prop.insert(location, value); + if let Place::Local(local) = *place { + if !self.mir.local_decls[local].is_user_variable + && ConstPropVisitor::check(local, self.mir) { + trace!("storing {:?} to {:?}", value, local); + assert!(self.optimizations.places.insert(local, value).is_none()); + } + } + } + } + self.super_statement(block, statement, location); + } + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { if let Rvalue::Ref(_, _, Place::Projection(ref projection)) = *rvalue { if let ProjectionElem::Deref = projection.elem { @@ -111,10 +551,33 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for OptimizationFinder<'b, 'a, 'tcx> { self.super_rvalue(rvalue, location) } + + fn visit_terminator_kind( + &mut self, + _block: BasicBlock, + kind: &TerminatorKind<'tcx>, + location: Location, + ) { + let span = self.mir.source_info(location).span; + match kind { + TerminatorKind::SwitchInt { discr: value, .. } | + TerminatorKind::Yield { value, .. } | + TerminatorKind::Assert { cond: value, .. } => { + if let Some(value) = self.simplify_operand(value, span) { + self.optimizations.const_prop.insert(location, value); + } + } + // FIXME: do this optimization for function calls + _ => {}, + } + } } #[derive(Default)] struct OptimizationList<'tcx> { and_stars: FxHashSet, arrays_lengths: FxHashMap>, + const_prop: FxHashMap>, + places: FxHashMap>, + constants: FxHashMap, Const<'tcx>>, } diff --git a/src/librustc_trans/consts.rs b/src/librustc_trans/consts.rs index 0ce3f729305b4..122b51dbbb778 100644 --- a/src/librustc_trans/consts.rs +++ b/src/librustc_trans/consts.rs @@ -13,7 +13,6 @@ use llvm::{SetUnnamedAddr}; use llvm::{ValueRef, True}; use rustc::hir::def_id::DefId; use rustc::hir::map as hir_map; -use rustc::middle::const_val::ConstEvalErr; use debuginfo; use base; use monomorphize::MonoItem; @@ -247,12 +246,15 @@ pub fn get_static(cx: &CodegenCx, def_id: DefId) -> ValueRef { pub fn trans_static<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, def_id: DefId, is_mutable: bool, - attrs: &[ast::Attribute]) - -> Result> { + attrs: &[ast::Attribute]) { unsafe { let g = get_static(cx, def_id); - let v = ::mir::trans_static_initializer(cx, def_id)?; + let v = match ::mir::trans_static_initializer(cx, def_id) { + Ok(v) => v, + // Error has already been reported + Err(_) => return, + }; // boolean SSA values are i1, but they have to be stored in i8 slots, // otherwise some LLVM optimization passes don't work as expected @@ -316,7 +318,5 @@ pub fn trans_static<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, let cast = llvm::LLVMConstPointerCast(g, Type::i8p(cx).to_ref()); cx.used_statics.borrow_mut().push(cast); } - - Ok(g) } } diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index 239300c1ecf4e..6d2f9c6c97fb0 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -10,7 +10,6 @@ use llvm::{self, ValueRef, BasicBlockRef}; use rustc::middle::lang_items; -use rustc::middle::const_val::{ConstEvalErr, ErrKind}; use rustc::ty::{self, TypeFoldable}; use rustc::ty::layout::{self, LayoutOf}; use rustc::traits; @@ -19,7 +18,7 @@ use abi::{Abi, FnType, ArgType, PassMode}; use base; use callee; use builder::Builder; -use common::{self, C_bool, C_str_slice, C_struct, C_u32, C_undef}; +use common::{self, C_bool, C_str_slice, C_struct, C_u32, C_uint_big, C_undef}; use consts; use meth; use monomorphize; @@ -30,7 +29,6 @@ use syntax::symbol::Symbol; use syntax_pos::Pos; use super::{FunctionCx, LocalRef}; -use super::constant::Const; use super::place::PlaceRef; use super::operand::OperandRef; use super::operand::OperandValue::{Pair, Ref, Immediate}; @@ -206,10 +204,11 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { let (otherwise, targets) = targets.split_last().unwrap(); let switch = bx.switch(discr.immediate(), llblock(self, *otherwise), values.len()); + let switch_llty = bcx.ccx.layout_of(switch_ty).immediate_llvm_type(bcx.ccx); for (&value, target) in values.iter().zip(targets) { - let val = Const::from_bytes(bx.cx, value, switch_ty); + let llval = C_uint_big(switch_llty, value); let llbb = llblock(self, *target); - bx.add_case(switch, val.llval, llbb) + bx.add_case(switch, llval, llbb) } } } @@ -359,10 +358,10 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { let const_err = common::const_to_opt_u128(len, false) .and_then(|len| common::const_to_opt_u128(index, false) - .map(|index| ErrKind::IndexOutOfBounds { - len: len as u64, - index: index as u64 - })); + .map(|index| format!( + "index out of bounds: the len is {} but the index is {}", + len, index, + ))); let file_line_col = C_struct(bx.cx, &[filename, line, col], false); let file_line_col = consts::addr_of(bx.cx, @@ -385,7 +384,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { "panic_loc"); (lang_items::PanicFnLangItem, vec![msg_file_line_col], - Some(ErrKind::Math(err.clone()))) + Some(err.description().to_owned())) } mir::AssertMessage::GeneratorResumedAfterReturn | mir::AssertMessage::GeneratorResumedAfterPanic => { @@ -413,10 +412,11 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { // is also constant, then we can produce a warning. if const_cond == Some(!expected) { if let Some(err) = const_err { - let err = ConstEvalErr{ span: span, kind: err }; let mut diag = bx.tcx().sess.struct_span_warn( - span, "this expression will panic at run-time"); - err.note(bx.tcx(), span, "expression", &mut diag); + span, &format!( + "this expression will panic at run-time with {:?}", + err, + )); diag.emit(); } } @@ -530,10 +530,13 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { span_bug!(span, "shuffle indices must be constant"); } mir::Operand::Constant(ref constant) => { - let val = self.trans_constant(&bx, constant); + let (llval, ty) = self.remove_me_shuffle_indices( + &bx, + constant, + ); return OperandRef { - val: Immediate(val.llval), - layout: bx.cx.layout_of(val.ty) + val: Immediate(llval), + layout: bx.cx.layout_of(ty) }; } } diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 66173cfef9203..5050628b024db 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -9,966 +9,27 @@ // except according to those terms. use llvm::{self, ValueRef}; -use rustc::middle::const_val::{ConstEvalErr, ConstVal, ErrKind}; -use rustc_const_math::{ConstInt, ConstMathErr, MAX_F32_PLUS_HALF_ULP}; +use rustc_const_math::{ConstInt, ConstMathErr}; +use rustc::middle::const_val::{ConstVal, ConstEvalErr}; +use rustc_mir::interpret::{read_target_uint, const_val_field}; use rustc::hir::def_id::DefId; -use rustc::infer::TransNormalize; use rustc::traits; use rustc::mir; -use rustc::mir::interpret::{Value as MiriValue, PrimVal}; -use rustc::mir::tcx::PlaceTy; -use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; -use rustc::ty::layout::{self, LayoutOf, Size}; -use rustc::ty::cast::{CastTy, IntTy}; -use rustc::ty::subst::{Kind, Substs}; -use rustc_apfloat::{ieee, Float, Status}; -use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use rustc_data_structures::indexed_vec::Idx; +use rustc::mir::interpret::{Allocation, GlobalId, MemoryPointer, PrimVal, Value as MiriValue}; +use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::layout::{self, HasDataLayout, LayoutOf, Scalar}; use base; -use abi::{self, Abi}; -use callee; use builder::Builder; -use common::{self, CodegenCx, const_get_elt, val_ty}; -use common::{C_array, C_bool, C_bytes, C_int, C_uint, C_uint_big, C_u32, C_u64}; -use common::{C_null, C_struct, C_str_slice, C_undef, C_usize, C_vector, C_fat_ptr}; +use common::{CodegenCx}; +use common::{C_bytes, C_struct, C_uint_big, C_undef, C_usize}; use common::const_to_opt_u128; use consts; use type_of::LayoutLlvmExt; use type_::Type; -use value::Value; -use syntax_pos::Span; -use syntax::ast; -use syntax::symbol::Symbol; - -use std::fmt; -use std::ptr; - -use super::operand::{OperandRef, OperandValue}; -use super::FunctionCx; - -/// A sized constant rvalue. -/// The LLVM type might not be the same for a single Rust type, -/// e.g. each enum variant would have its own LLVM struct type. -#[derive(Copy, Clone)] -pub struct Const<'tcx> { - pub llval: ValueRef, - pub ty: Ty<'tcx> -} - -impl<'a, 'tcx> Const<'tcx> { - pub fn new(llval: ValueRef, ty: Ty<'tcx>) -> Const<'tcx> { - Const { - llval, - 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) | - ty::TyUint(ast::UintTy::U128) => C_uint_big(Type::i128(ccx), b), - ty::TyInt(i) => C_int(Type::int_from_ty(ccx, i), b as i128 as i64), - ty::TyUint(u) => C_uint(Type::uint_from_ty(ccx, u), b as u64), - ty::TyBool => { - assert!(b <= 1); - C_bool(ccx, b == 1) - }, - ty::TyChar => { - assert_eq!(b as u32 as u128, b); - let c = b as u32; - assert!(::std::char::from_u32(c).is_some()); - C_uint(Type::char(ccx), c as u64) - }, - ty::TyFloat(fty) => { - let llty = ccx.layout_of(ty).llvm_type(ccx); - let bits = match fty { - ast::FloatTy::F32 => C_u32(ccx, b as u32), - ast::FloatTy::F64 => C_u64(ccx, b as u64), - }; - consts::bitcast(bits, llty) - }, - ty::TyAdt(adt, _) if adt.is_enum() => { - use rustc::ty::util::IntTypeExt; - Const::from_bytes(ccx, b, adt.repr.discr_type().to_ty(ccx.tcx())).llval - }, - _ => bug!("from_bytes({}, {})", b, ty), - }; - Const { llval, ty } - } - - /// Translate ConstVal into a LLVM constant value. - pub fn from_constval(cx: &CodegenCx<'a, 'tcx>, - cv: &ConstVal, - ty: Ty<'tcx>) - -> Const<'tcx> { - let llty = cx.layout_of(ty).llvm_type(cx); - trace!("from_constval: {:#?}: {}", cv, ty); - let val = match *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 { - ty::TyRef(_, ref tam) => match tam.ty.sty { - ty::TyStr => {}, - _ => unimplemented!("non-str fat pointer: {:?}: {:?}", ptr, ty), - }, - _ => unimplemented!("non-str fat pointer: {:?}: {:?}", ptr, ty), - } - let alloc = ccx - .tcx() - .interpret_interner - .borrow() - .get_alloc(ptr.alloc_id) - .expect("miri alloc not found"); - assert_eq!(len as usize as u128, len); - let slice = &alloc.bytes[(ptr.offset as usize)..][..(len as usize)]; - let s = ::std::str::from_utf8(slice) - .expect("non utf8 str from miri"); - C_str_slice(ccx, Symbol::intern(s).as_str()) - }, - ConstVal::Value(MiriValue::ByValPair(..)) => unimplemented!(), - ConstVal::Value(MiriValue::ByVal(PrimVal::Bytes(b))) => - return Const::from_bytes(ccx, b, ty), - ConstVal::Value(MiriValue::ByVal(PrimVal::Undef)) => C_undef(llty), - ConstVal::Value(MiriValue::ByVal(PrimVal::Ptr(ptr))) => { - let alloc = ccx - .tcx() - .interpret_interner - .borrow() - .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") - } - }; - - assert!(!ty.has_erasable_regions()); - - Const::new(val, ty) - } - - fn get_field(&self, cx: &CodegenCx<'a, 'tcx>, i: usize) -> ValueRef { - let layout = cx.layout_of(self.ty); - let field = layout.field(cx, i); - if field.is_zst() { - return C_undef(field.immediate_llvm_type(cx)); - } - let offset = layout.fields.offset(i); - match layout.abi { - layout::Abi::Scalar(_) | - layout::Abi::ScalarPair(..) | - layout::Abi::Vector { .. } - if offset.bytes() == 0 && field.size == layout.size => self.llval, - - layout::Abi::ScalarPair(ref a, ref b) => { - if offset.bytes() == 0 { - assert_eq!(field.size, a.value.size(cx)); - const_get_elt(self.llval, 0) - } else { - assert_eq!(offset, a.value.size(cx) - .abi_align(b.value.align(cx))); - assert_eq!(field.size, b.value.size(cx)); - const_get_elt(self.llval, 1) - } - } - _ => { - match layout.fields { - layout::FieldPlacement::Union(_) => self.llval, - _ => const_get_elt(self.llval, layout.llvm_field_index(i)), - } - } - } - } - - fn get_pair(&self, cx: &CodegenCx<'a, 'tcx>) -> (ValueRef, ValueRef) { - (self.get_field(cx, 0), self.get_field(cx, 1)) - } - - fn get_fat_ptr(&self, cx: &CodegenCx<'a, 'tcx>) -> (ValueRef, ValueRef) { - assert_eq!(abi::FAT_PTR_ADDR, 0); - assert_eq!(abi::FAT_PTR_EXTRA, 1); - self.get_pair(cx) - } - - fn as_place(&self) -> ConstPlace<'tcx> { - ConstPlace { - base: Base::Value(self.llval), - llextra: ptr::null_mut(), - ty: self.ty - } - } - - pub fn to_operand(&self, cx: &CodegenCx<'a, 'tcx>) -> OperandRef<'tcx> { - let layout = cx.layout_of(self.ty); - let llty = layout.immediate_llvm_type(cx); - let llvalty = val_ty(self.llval); - - let val = if llty == llvalty && layout.is_llvm_scalar_pair() { - OperandValue::Pair( - const_get_elt(self.llval, 0), - const_get_elt(self.llval, 1)) - } else if llty == llvalty && layout.is_llvm_immediate() { - // If the types match, we can use the value directly. - OperandValue::Immediate(self.llval) - } else { - // Otherwise, or if the value is not immediate, we create - // a constant LLVM global and cast its address if necessary. - let align = cx.align_of(self.ty); - let ptr = consts::addr_of(cx, self.llval, align, "const"); - OperandValue::Ref(consts::ptrcast(ptr, layout.llvm_type(cx).ptr_to()), - layout.align) - }; - - OperandRef { - val, - layout - } - } -} - -impl<'tcx> fmt::Debug for Const<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Const({:?}: {:?})", Value(self.llval), self.ty) - } -} - -#[derive(Copy, Clone)] -enum Base { - /// A constant value without an unique address. - Value(ValueRef), - - /// String literal base pointer (cast from array). - Str(ValueRef), - - /// The address of a static. - Static(ValueRef) -} - -/// A place as seen from a constant. -#[derive(Copy, Clone)] -struct ConstPlace<'tcx> { - base: Base, - llextra: ValueRef, - ty: Ty<'tcx> -} - -impl<'tcx> ConstPlace<'tcx> { - fn to_const(&self, span: Span) -> Const<'tcx> { - match self.base { - Base::Value(val) => Const::new(val, self.ty), - Base::Str(ptr) => { - span_bug!(span, "loading from `str` ({:?}) in constant", - Value(ptr)) - } - Base::Static(val) => { - span_bug!(span, "loading from `static` ({:?}) in constant", - Value(val)) - } - } - } - - pub fn len<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> ValueRef { - match self.ty.sty { - ty::TyArray(_, n) => { - C_usize(cx, n.val.unwrap_u64()) - } - ty::TySlice(_) | ty::TyStr => { - assert!(self.llextra != ptr::null_mut()); - self.llextra - } - _ => bug!("unexpected type `{}` in ConstPlace::len", self.ty) - } - } -} - -/// Machinery for translating a constant's MIR to LLVM values. -/// FIXME(eddyb) use miri and lower its allocations to LLVM. -struct MirConstContext<'a, 'tcx: 'a> { - cx: &'a CodegenCx<'a, 'tcx>, - mir: &'a mir::Mir<'tcx>, - - /// Type parameters for const fn and associated constants. - substs: &'tcx Substs<'tcx>, - - /// Values of locals in a constant or const fn. - locals: IndexVec, ConstEvalErr<'tcx>>>> -} - -fn add_err<'tcx, U, V>(failure: &mut Result>, - value: &Result>) -{ - if let &Err(ref err) = value { - if failure.is_ok() { - *failure = Err(err.clone()); - } - } -} - -impl<'a, 'tcx> MirConstContext<'a, 'tcx> { - fn new(cx: &'a CodegenCx<'a, 'tcx>, - mir: &'a mir::Mir<'tcx>, - substs: &'tcx Substs<'tcx>, - args: IndexVec, ConstEvalErr<'tcx>>>) - -> MirConstContext<'a, 'tcx> { - let mut context = MirConstContext { - cx, - mir, - substs, - locals: (0..mir.local_decls.len()).map(|_| None).collect(), - }; - for (i, arg) in args.into_iter().enumerate() { - // Locals after local 0 are the function arguments - let index = mir::Local::new(i + 1); - context.locals[index] = Some(arg); - } - context - } - - fn trans_def(cx: &'a CodegenCx<'a, 'tcx>, - def_id: DefId, - substs: &'tcx Substs<'tcx>, - args: IndexVec, ConstEvalErr<'tcx>>>) - -> Result, ConstEvalErr<'tcx>> { - let instance = ty::Instance::resolve(cx.tcx, - ty::ParamEnv::empty(traits::Reveal::All), - def_id, - substs).unwrap(); - let mir = cx.tcx.instance_mir(instance.def); - MirConstContext::new(cx, &mir, instance.substs, args).trans() - } - - fn monomorphize(&self, value: &T) -> T - where T: TransNormalize<'tcx> - { - self.cx.tcx.trans_apply_param_substs(self.substs, value) - } - - fn trans(&mut self) -> Result, ConstEvalErr<'tcx>> { - let tcx = self.cx.tcx; - let mut bb = mir::START_BLOCK; - - // Make sure to evaluate all statements to - // report as many errors as we possibly can. - let mut failure = Ok(()); - - loop { - let data = &self.mir[bb]; - for statement in &data.statements { - let span = statement.source_info.span; - match statement.kind { - mir::StatementKind::Assign(ref dest, ref rvalue) => { - let ty = dest.ty(self.mir, tcx); - let ty = self.monomorphize(&ty).to_ty(tcx); - let value = self.const_rvalue(rvalue, ty, span); - add_err(&mut failure, &value); - self.store(dest, value, span); - } - mir::StatementKind::StorageLive(_) | - mir::StatementKind::StorageDead(_) | - mir::StatementKind::Validate(..) | - mir::StatementKind::EndRegion(_) | - mir::StatementKind::Nop => {} - mir::StatementKind::InlineAsm { .. } | - mir::StatementKind::SetDiscriminant{ .. } => { - span_bug!(span, "{:?} should not appear in constants?", statement.kind); - } - } - } - - let terminator = data.terminator(); - let span = terminator.source_info.span; - bb = match terminator.kind { - mir::TerminatorKind::Drop { target, .. } | // No dropping. - mir::TerminatorKind::Goto { target } => target, - mir::TerminatorKind::Return => { - failure?; - return self.locals[mir::RETURN_PLACE].clone().unwrap_or_else(|| { - span_bug!(span, "no returned value in constant"); - }); - } - - mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, .. } => { - let cond = self.const_operand(cond, span)?; - let cond_bool = common::const_to_uint(cond.llval) != 0; - if cond_bool != expected { - let err = match *msg { - mir::AssertMessage::BoundsCheck { ref len, ref index } => { - let len = self.const_operand(len, span)?; - let index = self.const_operand(index, span)?; - ErrKind::IndexOutOfBounds { - len: common::const_to_uint(len.llval), - index: common::const_to_uint(index.llval) - } - } - mir::AssertMessage::Math(ref err) => { - ErrKind::Math(err.clone()) - } - mir::AssertMessage::GeneratorResumedAfterReturn | - mir::AssertMessage::GeneratorResumedAfterPanic => - span_bug!(span, "{:?} should not appear in constants?", msg), - }; - - let err = ConstEvalErr { span: span, kind: err }; - err.report(tcx, span, "expression"); - failure = Err(err); - } - target - } - - mir::TerminatorKind::Call { ref func, ref args, ref destination, .. } => { - let fn_ty = func.ty(self.mir, tcx); - let fn_ty = self.monomorphize(&fn_ty); - let (def_id, substs) = match fn_ty.sty { - ty::TyFnDef(def_id, substs) => (def_id, substs), - _ => span_bug!(span, "calling {:?} (of type {}) in constant", - func, fn_ty) - }; - trace!("trans const fn call {:?}, {:?}, {:#?}", func, fn_ty, args); - - let mut arg_vals = IndexVec::with_capacity(args.len()); - for arg in args { - let arg_val = self.const_operand(arg, span); - add_err(&mut failure, &arg_val); - arg_vals.push(arg_val); - } - if let Some((ref dest, target)) = *destination { - let result = if fn_ty.fn_sig(tcx).abi() == Abi::RustIntrinsic { - match &tcx.item_name(def_id)[..] { - "size_of" => { - let llval = C_usize(self.cx, - self.cx.size_of(substs.type_at(0)).bytes()); - Ok(Const::new(llval, tcx.types.usize)) - } - "min_align_of" => { - let llval = C_usize(self.cx, - self.cx.align_of(substs.type_at(0)).abi()); - Ok(Const::new(llval, tcx.types.usize)) - } - "type_id" => { - let llval = C_u64(self.cx, - self.cx.tcx.type_id_hash(substs.type_at(0))); - Ok(Const::new(llval, tcx.types.u64)) - } - _ => span_bug!(span, "{:?} in constant", terminator.kind) - } - } else if let Some((op, is_checked)) = tcx.is_binop_lang_item(def_id) { - (||{ - assert_eq!(arg_vals.len(), 2); - let rhs = arg_vals.pop().unwrap()?; - let lhs = arg_vals.pop().unwrap()?; - if !is_checked { - let binop_ty = op.ty(tcx, lhs.ty, rhs.ty); - let (lhs, rhs) = (lhs.llval, rhs.llval); - Ok(Const::new(const_scalar_binop(op, lhs, rhs, binop_ty), - binop_ty)) - } else { - let ty = lhs.ty; - let val_ty = op.ty(tcx, lhs.ty, rhs.ty); - let binop_ty = tcx.intern_tup(&[val_ty, tcx.types.bool], false); - let (lhs, rhs) = (lhs.llval, rhs.llval); - assert!(!ty.is_fp()); - - match const_scalar_checked_binop(tcx, op, lhs, rhs, ty) { - Some((llval, of)) => { - Ok(trans_const_adt( - self.cx, - binop_ty, - &mir::AggregateKind::Tuple, - &[ - Const::new(llval, val_ty), - Const::new(C_bool(self.cx, of), tcx.types.bool) - ])) - } - None => { - span_bug!(span, - "{:?} got non-integer operands: {:?} and {:?}", - op, Value(lhs), Value(rhs)); - } - } - } - })() - } else { - MirConstContext::trans_def(self.cx, def_id, substs, arg_vals) - }; - add_err(&mut failure, &result); - self.store(dest, result, span); - target - } else { - span_bug!(span, "diverging {:?} in constant", terminator.kind); - } - } - _ => span_bug!(span, "{:?} in constant", terminator.kind) - }; - } - } - - fn store(&mut self, - dest: &mir::Place<'tcx>, - value: Result, ConstEvalErr<'tcx>>, - span: Span) { - if let mir::Place::Local(index) = *dest { - self.locals[index] = Some(value); - } else { - span_bug!(span, "assignment to {:?} in constant", dest); - } - } - - fn const_place(&self, place: &mir::Place<'tcx>, span: Span) - -> Result, ConstEvalErr<'tcx>> { - let tcx = self.cx.tcx; - - if let mir::Place::Local(index) = *place { - return self.locals[index].clone().unwrap_or_else(|| { - span_bug!(span, "{:?} not initialized", place) - }).map(|v| v.as_place()); - } - - let place = match *place { - mir::Place::Local(_) => bug!(), // handled above - mir::Place::Static(box mir::Static { def_id, ty }) => { - ConstPlace { - base: Base::Static(consts::get_static(self.cx, def_id)), - llextra: ptr::null_mut(), - ty: self.monomorphize(&ty), - } - } - mir::Place::Projection(ref projection) => { - let tr_base = self.const_place(&projection.base, span)?; - let projected_ty = PlaceTy::Ty { ty: tr_base.ty } - .projection_ty(tcx, &projection.elem); - let base = tr_base.to_const(span); - let projected_ty = self.monomorphize(&projected_ty).to_ty(tcx); - let has_metadata = self.cx.type_has_metadata(projected_ty); - - let (projected, llextra) = match projection.elem { - mir::ProjectionElem::Deref => { - let (base, extra) = if !has_metadata { - (base.llval, ptr::null_mut()) - } else { - base.get_fat_ptr(self.cx) - }; - if self.cx.statics.borrow().contains_key(&base) { - (Base::Static(base), extra) - } else if let ty::TyStr = projected_ty.sty { - (Base::Str(base), extra) - } else { - let v = base; - let v = self.cx.const_unsized.borrow().get(&v).map_or(v, |&v| v); - let mut val = unsafe { llvm::LLVMGetInitializer(v) }; - if val.is_null() { - span_bug!(span, "dereference of non-constant pointer `{:?}`", - Value(base)); - } - let layout = self.cx.layout_of(projected_ty); - if let layout::Abi::Scalar(ref scalar) = layout.abi { - let i1_type = Type::i1(self.cx); - if scalar.is_bool() && val_ty(val) != i1_type { - unsafe { - val = llvm::LLVMConstTrunc(val, i1_type.to_ref()); - } - } - } - (Base::Value(val), extra) - } - } - mir::ProjectionElem::Field(ref field, _) => { - let llprojected = base.get_field(self.cx, field.index()); - let llextra = if !has_metadata { - ptr::null_mut() - } else { - tr_base.llextra - }; - (Base::Value(llprojected), llextra) - } - mir::ProjectionElem::Index(index) => { - let index = &mir::Operand::Copy(mir::Place::Local(index)); - let llindex = self.const_operand(index, span)?.llval; - - let iv = if let Some(iv) = common::const_to_opt_u128(llindex, false) { - iv - } else { - span_bug!(span, "index is not an integer-constant expression") - }; - - // Produce an undef instead of a LLVM assertion on OOB. - let len = common::const_to_uint(tr_base.len(self.cx)); - let llelem = if iv < len as u128 { - const_get_elt(base.llval, iv as u64) - } else { - C_undef(self.cx.layout_of(projected_ty).llvm_type(self.cx)) - }; - - (Base::Value(llelem), ptr::null_mut()) - } - _ => span_bug!(span, "{:?} in constant", projection.elem) - }; - ConstPlace { - base: projected, - llextra, - ty: projected_ty - } - } - }; - Ok(place) - } - - fn const_operand(&self, operand: &mir::Operand<'tcx>, span: Span) - -> Result, ConstEvalErr<'tcx>> { - debug!("const_operand({:?} @ {:?})", operand, span); - let result = match *operand { - mir::Operand::Copy(ref place) | - mir::Operand::Move(ref place) => { - Ok(self.const_place(place, span)?.to_const(span)) - } - - mir::Operand::Constant(ref constant) => { - let ty = self.monomorphize(&constant.ty); - match constant.literal.clone() { - mir::Literal::Promoted { index } => { - let mir = &self.mir.promoted[index]; - MirConstContext::new(self.cx, mir, self.substs, IndexVec::new()).trans() - } - mir::Literal::Value { value } => { - if let ConstVal::Unevaluated(def_id, substs) = value.val { - let substs = self.monomorphize(&substs); - MirConstContext::trans_def(self.cx, def_id, substs, IndexVec::new()) - } else { - Ok(Const::from_constval(self.cx, &value.val, ty)) - } - } - } - } - }; - debug!("const_operand({:?} @ {:?}) = {:?}", operand, span, - result.as_ref().ok()); - result - } - - fn const_array(&self, array_ty: Ty<'tcx>, fields: &[ValueRef]) - -> Const<'tcx> - { - let elem_ty = array_ty.builtin_index().unwrap_or_else(|| { - bug!("bad array type {:?}", array_ty) - }); - let llunitty = self.cx.layout_of(elem_ty).llvm_type(self.cx); - // If the array contains enums, an LLVM array won't work. - let val = if fields.iter().all(|&f| val_ty(f) == llunitty) { - C_array(llunitty, fields) - } else { - C_struct(self.cx, fields, false) - }; - Const::new(val, array_ty) - } - - fn const_rvalue(&self, rvalue: &mir::Rvalue<'tcx>, - dest_ty: Ty<'tcx>, span: Span) - -> Result, ConstEvalErr<'tcx>> { - let tcx = self.cx.tcx; - debug!("const_rvalue({:?}: {:?} @ {:?})", rvalue, dest_ty, span); - let val = match *rvalue { - mir::Rvalue::Use(ref operand) => self.const_operand(operand, span)?, - - mir::Rvalue::Repeat(ref elem, count) => { - let elem = self.const_operand(elem, span)?; - let size = count.as_u64(); - assert_eq!(size as usize as u64, size); - let fields = vec![elem.llval; size as usize]; - self.const_array(dest_ty, &fields) - } - - mir::Rvalue::Aggregate(box mir::AggregateKind::Array(_), ref operands) => { - // Make sure to evaluate all operands to - // report as many errors as we possibly can. - let mut fields = Vec::with_capacity(operands.len()); - let mut failure = Ok(()); - for operand in operands { - match self.const_operand(operand, span) { - Ok(val) => fields.push(val.llval), - Err(err) => if failure.is_ok() { failure = Err(err); } - } - } - failure?; - - self.const_array(dest_ty, &fields) - } - - mir::Rvalue::Aggregate(ref kind, ref operands) => { - // Make sure to evaluate all operands to - // report as many errors as we possibly can. - let mut fields = Vec::with_capacity(operands.len()); - let mut failure = Ok(()); - for operand in operands { - match self.const_operand(operand, span) { - Ok(val) => fields.push(val), - Err(err) => if failure.is_ok() { failure = Err(err); } - } - } - failure?; - - trans_const_adt(self.cx, dest_ty, kind, &fields) - } - - mir::Rvalue::Cast(ref kind, ref source, cast_ty) => { - let operand = self.const_operand(source, span)?; - let cast_ty = self.monomorphize(&cast_ty); - - let val = match *kind { - mir::CastKind::ReifyFnPointer => { - match operand.ty.sty { - ty::TyFnDef(def_id, substs) => { - if tcx.has_attr(def_id, "rustc_args_required_const") { - bug!("reifying a fn ptr that requires \ - const arguments"); - } - callee::resolve_and_get_fn(self.cx, def_id, substs) - } - _ => { - span_bug!(span, "{} cannot be reified to a fn ptr", - operand.ty) - } - } - } - mir::CastKind::ClosureFnPointer => { - match operand.ty.sty { - ty::TyClosure(def_id, substs) => { - // Get the def_id for FnOnce::call_once - let fn_once = tcx.lang_items().fn_once_trait().unwrap(); - let call_once = tcx - .global_tcx().associated_items(fn_once) - .find(|it| it.kind == ty::AssociatedKind::Method) - .unwrap().def_id; - // Now create its substs [Closure, Tuple] - let input = substs.closure_sig(def_id, tcx).input(0); - let input = tcx.erase_late_bound_regions_and_normalize(&input); - let substs = tcx.mk_substs([operand.ty, input] - .iter().cloned().map(Kind::from)); - callee::resolve_and_get_fn(self.cx, call_once, substs) - } - _ => { - bug!("{} cannot be cast to a fn ptr", operand.ty) - } - } - } - mir::CastKind::UnsafeFnPointer => { - // this is a no-op at the LLVM level - operand.llval - } - mir::CastKind::Unsize => { - let pointee_ty = operand.ty.builtin_deref(true) - .expect("consts: unsizing got non-pointer type").ty; - let (base, old_info) = if !self.cx.type_is_sized(pointee_ty) { - // Normally, the source is a thin pointer and we are - // adding extra info to make a fat pointer. The exception - // is when we are upcasting an existing object fat pointer - // to use a different vtable. In that case, we want to - // load out the original data pointer so we can repackage - // it. - let (base, extra) = operand.get_fat_ptr(self.cx); - (base, Some(extra)) - } else { - (operand.llval, None) - }; - - let unsized_ty = cast_ty.builtin_deref(true) - .expect("consts: unsizing got non-pointer target type").ty; - let ptr_ty = self.cx.layout_of(unsized_ty).llvm_type(self.cx).ptr_to(); - let base = consts::ptrcast(base, ptr_ty); - let info = base::unsized_info(self.cx, pointee_ty, - unsized_ty, old_info); - - if old_info.is_none() { - let prev_const = self.cx.const_unsized.borrow_mut() - .insert(base, operand.llval); - assert!(prev_const.is_none() || prev_const == Some(operand.llval)); - } - C_fat_ptr(self.cx, base, info) - } - mir::CastKind::Misc if self.cx.layout_of(operand.ty).is_llvm_immediate() => { - let r_t_in = CastTy::from_ty(operand.ty).expect("bad input type for cast"); - let r_t_out = CastTy::from_ty(cast_ty).expect("bad output type for cast"); - let cast_layout = self.cx.layout_of(cast_ty); - assert!(cast_layout.is_llvm_immediate()); - let ll_t_out = cast_layout.immediate_llvm_type(self.cx); - let llval = operand.llval; - - let mut signed = false; - let l = self.cx.layout_of(operand.ty); - if let layout::Abi::Scalar(ref scalar) = l.abi { - if let layout::Int(_, true) = scalar.value { - signed = true; - } - } - - unsafe { - match (r_t_in, r_t_out) { - (CastTy::Int(_), CastTy::Int(_)) => { - let s = signed as llvm::Bool; - llvm::LLVMConstIntCast(llval, ll_t_out.to_ref(), s) - } - (CastTy::Int(_), CastTy::Float) => { - cast_const_int_to_float(self.cx, llval, signed, ll_t_out) - } - (CastTy::Float, CastTy::Float) => { - llvm::LLVMConstFPCast(llval, ll_t_out.to_ref()) - } - (CastTy::Float, CastTy::Int(IntTy::I)) => { - cast_const_float_to_int(self.cx, &operand, - true, ll_t_out, span) - } - (CastTy::Float, CastTy::Int(_)) => { - cast_const_float_to_int(self.cx, &operand, - false, ll_t_out, span) - } - (CastTy::Ptr(_), CastTy::Ptr(_)) | - (CastTy::FnPtr, CastTy::Ptr(_)) | - (CastTy::RPtr(_), CastTy::Ptr(_)) => { - consts::ptrcast(llval, ll_t_out) - } - (CastTy::Int(_), CastTy::Ptr(_)) => { - let s = signed as llvm::Bool; - let usize_llval = llvm::LLVMConstIntCast(llval, - self.cx.isize_ty.to_ref(), s); - llvm::LLVMConstIntToPtr(usize_llval, ll_t_out.to_ref()) - } - (CastTy::Ptr(_), CastTy::Int(_)) | - (CastTy::FnPtr, CastTy::Int(_)) => { - llvm::LLVMConstPtrToInt(llval, ll_t_out.to_ref()) - } - _ => bug!("unsupported cast: {:?} to {:?}", operand.ty, cast_ty) - } - } - } - mir::CastKind::Misc => { // Casts from a fat-ptr. - let l = self.cx.layout_of(operand.ty); - let cast = self.cx.layout_of(cast_ty); - if l.is_llvm_scalar_pair() { - let (data_ptr, meta) = operand.get_fat_ptr(self.cx); - if cast.is_llvm_scalar_pair() { - let data_cast = consts::ptrcast(data_ptr, - cast.scalar_pair_element_llvm_type(self.cx, 0)); - C_fat_ptr(self.cx, data_cast, meta) - } else { // cast to thin-ptr - // Cast of fat-ptr to thin-ptr is an extraction of data-ptr and - // pointer-cast of that pointer to desired pointer type. - let llcast_ty = cast.immediate_llvm_type(self.cx); - consts::ptrcast(data_ptr, llcast_ty) - } - } else { - bug!("Unexpected non-fat-pointer operand") - } - } - }; - Const::new(val, cast_ty) - } - - mir::Rvalue::Ref(_, bk, ref place) => { - let tr_place = self.const_place(place, span)?; - - let ty = tr_place.ty; - let ref_ty = tcx.mk_ref(tcx.types.re_erased, - ty::TypeAndMut { ty: ty, mutbl: bk.to_mutbl_lossy() }); - - let base = match tr_place.base { - Base::Value(llval) => { - // FIXME: may be wrong for &*(&simd_vec as &fmt::Debug) - let align = if self.cx.type_is_sized(ty) { - self.cx.align_of(ty) - } else { - self.cx.tcx.data_layout.pointer_align - }; - if let mir::BorrowKind::Mut { .. } = bk { - consts::addr_of_mut(self.cx, llval, align, "ref_mut") - } else { - consts::addr_of(self.cx, llval, align, "ref") - } - } - Base::Str(llval) | - Base::Static(llval) => llval - }; - - let ptr = if self.cx.type_is_sized(ty) { - base - } else { - C_fat_ptr(self.cx, base, tr_place.llextra) - }; - Const::new(ptr, ref_ty) - } - - mir::Rvalue::Len(ref place) => { - let tr_place = self.const_place(place, span)?; - Const::new(tr_place.len(self.cx), tcx.types.usize) - } - - mir::Rvalue::BinaryOp(op, ref lhs, ref rhs) => { - let lhs = self.const_operand(lhs, span)?; - let rhs = self.const_operand(rhs, span)?; - let ty = lhs.ty; - let binop_ty = op.ty(tcx, lhs.ty, rhs.ty); - let (lhs, rhs) = (lhs.llval, rhs.llval); - Const::new(const_scalar_binop(op, lhs, rhs, ty), binop_ty) - } - - mir::Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) => { - let lhs = self.const_operand(lhs, span)?; - let rhs = self.const_operand(rhs, span)?; - let ty = lhs.ty; - let val_ty = op.ty(tcx, lhs.ty, rhs.ty); - let binop_ty = tcx.intern_tup(&[val_ty, tcx.types.bool], false); - let (lhs, rhs) = (lhs.llval, rhs.llval); - assert!(!ty.is_fp()); - - match const_scalar_checked_binop(tcx, op, lhs, rhs, ty) { - Some((llval, of)) => { - trans_const_adt(self.cx, binop_ty, &mir::AggregateKind::Tuple, &[ - Const::new(llval, val_ty), - Const::new(C_bool(self.cx, of), tcx.types.bool) - ]) - } - None => { - span_bug!(span, "{:?} got non-integer operands: {:?} and {:?}", - rvalue, Value(lhs), Value(rhs)); - } - } - } - - mir::Rvalue::UnaryOp(op, ref operand) => { - let operand = self.const_operand(operand, span)?; - let lloperand = operand.llval; - let llval = match op { - mir::UnOp::Not => { - unsafe { - llvm::LLVMConstNot(lloperand) - } - } - mir::UnOp::Neg => { - let is_float = operand.ty.is_fp(); - unsafe { - if is_float { - llvm::LLVMConstFNeg(lloperand) - } else { - llvm::LLVMConstNeg(lloperand) - } - } - } - }; - Const::new(llval, operand.ty) - } - - mir::Rvalue::NullaryOp(mir::NullOp::SizeOf, ty) => { - assert!(self.cx.type_is_sized(ty)); - let llval = C_usize(self.cx, self.cx.size_of(ty).bytes()); - Const::new(llval, tcx.types.usize) - } - - _ => span_bug!(span, "{:?} in constant", rvalue) - }; - - debug!("const_rvalue({:?}: {:?} @ {:?}) = {:?}", rvalue, dest_ty, span, val); - - Ok(val) - } - -} +use super::super::callee; +use super::MirContext; fn to_const_int(value: ValueRef, t: Ty, tcx: TyCtxt) -> Option { match t.sty { @@ -1074,278 +135,239 @@ pub fn const_scalar_checked_binop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } -unsafe fn cast_const_float_to_int(cx: &CodegenCx, - operand: &Const, - signed: bool, - int_ty: Type, - span: Span) -> ValueRef { - let llval = operand.llval; - let float_bits = match operand.ty.sty { - ty::TyFloat(fty) => fty.bit_width(), - _ => bug!("cast_const_float_to_int: operand not a float"), - }; - // Note: this breaks if llval is a complex constant expression rather than a simple constant. - // One way that might happen would be if addresses could be turned into integers in constant - // expressions, but that doesn't appear to be possible? - // In any case, an ICE is better than producing undef. - let llval_bits = consts::bitcast(llval, Type::ix(cx, float_bits as u64)); - let bits = const_to_opt_u128(llval_bits, false).unwrap_or_else(|| { - panic!("could not get bits of constant float {:?}", - Value(llval)); - }); - let int_width = int_ty.int_width() as usize; - // Try to convert, but report an error for overflow and NaN. This matches HIR const eval. - let cast_result = match float_bits { - 32 if signed => ieee::Single::from_bits(bits).to_i128(int_width).map(|v| v as u128), - 64 if signed => ieee::Double::from_bits(bits).to_i128(int_width).map(|v| v as u128), - 32 => ieee::Single::from_bits(bits).to_u128(int_width), - 64 => ieee::Double::from_bits(bits).to_u128(int_width), - n => bug!("unsupported float width {}", n), - }; - if cast_result.status.contains(Status::INVALID_OP) { - let err = ConstEvalErr { span: span, kind: ErrKind::CannotCast }; - err.report(cx.tcx, span, "expression"); - } - C_uint_big(int_ty, cast_result.value) -} - -unsafe fn cast_const_int_to_float(cx: &CodegenCx, - llval: ValueRef, - signed: bool, - float_ty: Type) -> ValueRef { - // Note: this breaks if llval is a complex constant expression rather than a simple constant. - // One way that might happen would be if addresses could be turned into integers in constant - // expressions, but that doesn't appear to be possible? - // In any case, an ICE is better than producing undef. - let value = const_to_opt_u128(llval, signed).unwrap_or_else(|| { - panic!("could not get z128 value of constant integer {:?}", - Value(llval)); - }); - if signed { - llvm::LLVMConstSIToFP(llval, float_ty.to_ref()) - } else if float_ty.float_width() == 32 && value >= MAX_F32_PLUS_HALF_ULP { - // We're casting to f32 and the value is > f32::MAX + 0.5 ULP -> round up to infinity. - let infinity_bits = C_u32(cx, ieee::Single::INFINITY.to_bits() as u32); - consts::bitcast(infinity_bits, float_ty) - } else { - llvm::LLVMConstUIToFP(llval, float_ty.to_ref()) - } -} - -impl<'a, 'tcx> FunctionCx<'a, 'tcx> { - pub fn trans_constant(&mut self, - bx: &Builder<'a, 'tcx>, - constant: &mir::Constant<'tcx>) - -> Const<'tcx> - { - debug!("trans_constant({:?})", constant); - let ty = self.monomorphize(&constant.ty); - let result = match constant.literal.clone() { - mir::Literal::Promoted { index } => { - let mir = &self.mir.promoted[index]; - MirConstContext::new(bx.cx, mir, self.param_substs, IndexVec::new()).trans() +pub fn primval_to_llvm(ccx: &CrateContext, + cv: PrimVal, + scalar: &Scalar, + llty: Type) -> ValueRef { + let bits = if scalar.is_bool() { 1 } else { scalar.value.size(ccx).bits() }; + match cv { + PrimVal::Undef => C_undef(Type::ix(ccx, bits)), + PrimVal::Bytes(b) => { + let llval = C_uint_big(Type::ix(ccx, bits), b); + if scalar.value == layout::Pointer { + unsafe { llvm::LLVMConstIntToPtr(llval, llty.to_ref()) } + } else { + consts::bitcast(llval, llty) } - mir::Literal::Value { value } => { - if let ConstVal::Unevaluated(def_id, substs) = value.val { - let substs = self.monomorphize(&substs); - MirConstContext::trans_def(bx.cx, def_id, substs, IndexVec::new()) + }, + PrimVal::Ptr(ptr) => { + let interpret_interner = ccx.tcx().interpret_interner.borrow(); + if let Some(fn_instance) = interpret_interner.get_fn(ptr.alloc_id) { + callee::get_fn(ccx, fn_instance) + } else { + let static_ = interpret_interner.get_corresponding_static_def_id(ptr.alloc_id); + let base_addr = if let Some(def_id) = static_ { + assert!(ccx.tcx().is_static(def_id).is_some()); + consts::get_static(ccx, def_id) + } else if let Some(alloc) = interpret_interner.get_alloc(ptr.alloc_id) { + let init = global_initializer(ccx, alloc); + if alloc.mutable { + consts::addr_of_mut(ccx, init, alloc.align, "byte_str") + } else { + consts::addr_of(ccx, init, alloc.align, "byte_str") + } } else { - Ok(Const::from_constval(bx.cx, &value.val, ty)) + bug!("missing allocation {:?}", ptr.alloc_id); + }; + + let llval = unsafe { llvm::LLVMConstInBoundsGEP( + consts::bitcast(base_addr, Type::i8p(ccx)), + &C_usize(ccx, ptr.offset), + 1, + ) }; + if scalar.value != layout::Pointer { + unsafe { llvm::LLVMConstPtrToInt(llval, llty.to_ref()) } + } else { + consts::bitcast(llval, llty) } } - }; - - let result = result.unwrap_or_else(|_| { - // We've errored, so we don't have to produce working code. - let llty = bx.cx.layout_of(ty).llvm_type(bx.cx); - Const::new(C_undef(llty), ty) - }); - - debug!("trans_constant({:?}) = {:?}", constant, result); - result + } } } +pub fn global_initializer(ccx: &CrateContext, alloc: &Allocation) -> ValueRef { + let mut llvals = Vec::with_capacity(alloc.relocations.len() + 1); + let layout = ccx.data_layout(); + let pointer_size = layout.pointer_size.bytes() as usize; + + let mut next_offset = 0; + for (&offset, &alloc_id) in &alloc.relocations { + assert_eq!(offset as usize as u64, offset); + let offset = offset as usize; + if offset > next_offset { + llvals.push(C_bytes(ccx, &alloc.bytes[next_offset..offset])); + } + let ptr_offset = read_target_uint( + layout.endian, + &alloc.bytes[offset..(offset + pointer_size)], + ).expect("global_initializer: could not read relocation pointer") as u64; + llvals.push(primval_to_llvm( + ccx, + PrimVal::Ptr(MemoryPointer { alloc_id, offset: ptr_offset }), + &Scalar { + value: layout::Primitive::Pointer, + valid_range: 0..=!0 + }, + Type::i8p(ccx) + )); + next_offset = offset + pointer_size; + } + if alloc.bytes.len() >= next_offset { + llvals.push(C_bytes(ccx, &alloc.bytes[next_offset ..])); + } + + C_struct(ccx, &llvals, true) +} pub fn trans_static_initializer<'a, 'tcx>( cx: &CodegenCx<'a, 'tcx>, def_id: DefId) -> Result> { - MirConstContext::trans_def(cx, def_id, Substs::empty(), IndexVec::new()) - .map(|c| c.llval) -} - -/// Construct a constant value, suitable for initializing a -/// GlobalVariable, given a case and constant values for its fields. -/// Note that this may have a different LLVM type (and different -/// alignment!) from the representation's `type_of`, so it needs a -/// pointer cast before use. -/// -/// The LLVM type system does not directly support unions, and only -/// pointers can be bitcast, so a constant (and, by extension, the -/// GlobalVariable initialized by it) will have a type that can vary -/// depending on which case of an enum it is. -/// -/// To understand the alignment situation, consider `enum E { V64(u64), -/// V32(u32, u32) }` on Windows. The type has 8-byte alignment to -/// accommodate the u64, but `V32(x, y)` would have LLVM type `{i32, -/// i32, i32}`, which is 4-byte aligned. -/// -/// Currently the returned value has the same size as the type, but -/// this could be changed in the future to avoid allocating unnecessary -/// space after values of shorter-than-maximum cases. -fn trans_const_adt<'a, 'tcx>( - cx: &CodegenCx<'a, 'tcx>, - t: Ty<'tcx>, - kind: &mir::AggregateKind, - vals: &[Const<'tcx>] -) -> Const<'tcx> { - let l = cx.layout_of(t); - let variant_index = match *kind { - mir::AggregateKind::Adt(_, index, _, _) => index, - _ => 0, + let instance = ty::Instance::mono(ccx.tcx(), def_id); + let cid = GlobalId { + instance, + promoted: None }; + let param_env = ty::ParamEnv::empty(traits::Reveal::All); + ccx.tcx().const_eval(param_env.and(cid))?; + + let alloc_id = ccx + .tcx() + .interpret_interner + .borrow() + .get_cached(def_id) + .expect("global not cached"); + + let alloc = ccx + .tcx() + .interpret_interner + .borrow() + .get_alloc(alloc_id) + .expect("miri allocation never successfully created"); + Ok(global_initializer(ccx, alloc)) +} - if let layout::Abi::Uninhabited = l.abi { - return Const::new(C_undef(l.llvm_type(cx)), t); - } - - match l.variants { - layout::Variants::Single { index } => { - assert_eq!(variant_index, index); - if let layout::FieldPlacement::Union(_) = l.fields { - assert_eq!(variant_index, 0); - assert_eq!(vals.len(), 1); - let (field_size, field_align) = cx.size_and_align_of(vals[0].ty); - let contents = [ - vals[0].llval, - padding(cx, l.size - field_size) - ]; - - let packed = l.align.abi() < field_align.abi(); - Const::new(C_struct(cx, &contents, packed), t) - } else { - if let layout::Abi::Vector { .. } = l.abi { - if let layout::FieldPlacement::Array { .. } = l.fields { - return Const::new(C_vector(&vals.iter().map(|x| x.llval) - .collect::>()), t); - } - } - build_const_struct(cx, l, vals, None) - } - } - layout::Variants::Tagged { .. } => { - let discr = match *kind { - mir::AggregateKind::Adt(adt_def, _, _, _) => { - adt_def.discriminant_for_variant(cx.tcx, variant_index) - .to_u128_unchecked() as u64 - }, - _ => 0, - }; - let discr_field = l.field(cx, 0); - let discr = C_int(discr_field.llvm_type(cx), discr as i64); - if let layout::Abi::Scalar(_) = l.abi { - Const::new(discr, t) - } else { - let discr = Const::new(discr, discr_field.ty); - build_const_struct(cx, l.for_variant(cx, variant_index), vals, Some(discr)) - } - } - layout::Variants::NicheFilling { - dataful_variant, - ref niche_variants, - niche_start, - .. - } => { - if variant_index == dataful_variant { - build_const_struct(cx, l.for_variant(cx, dataful_variant), vals, None) - } else { - let niche = l.field(cx, 0); - let niche_llty = niche.llvm_type(cx); - let niche_value = ((variant_index - niche_variants.start) as u128) - .wrapping_add(niche_start); - // FIXME(eddyb) Check the actual primitive type here. - let niche_llval = if niche_value == 0 { - // HACK(eddyb) Using `C_null` as it works on all types. - C_null(niche_llty) - } else { - C_uint_big(niche_llty, niche_value) +impl<'a, 'tcx> FunctionCx<'a, 'tcx> { + fn const_to_miri_value( + &mut self, + bcx: &Builder<'a, 'tcx>, + constant: &'tcx ty::Const<'tcx>, + ) -> Result> { + match constant.val { + ConstVal::Unevaluated(def_id, ref substs) => { + let tcx = bcx.tcx(); + let param_env = ty::ParamEnv::empty(traits::Reveal::All); + let instance = ty::Instance::resolve(tcx, param_env, def_id, substs).unwrap(); + let cid = GlobalId { + instance, + promoted: None, }; - build_const_struct(cx, l, &[Const::new(niche_llval, niche.ty)], None) - } + let c = tcx.const_eval(param_env.and(cid))?; + self.const_to_miri_value(bcx, c) + }, + ConstVal::Value(miri_val) => Ok(miri_val), } } -} - -/// Building structs is a little complicated, because we might need to -/// insert padding if a field's value is less aligned than its type. -/// -/// Continuing the example from `trans_const_adt`, a value of type `(u32, -/// E)` should have the `E` at offset 8, but if that field's -/// initializer is 4-byte aligned then simply translating the tuple as -/// a two-element struct will locate it at offset 4, and accesses to it -/// will read the wrong memory. -fn build_const_struct<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - layout: layout::TyLayout<'tcx>, - vals: &[Const<'tcx>], - discr: Option>) - -> Const<'tcx> { - assert_eq!(vals.len(), layout.fields.count()); - match layout.abi { - layout::Abi::Scalar(_) | - layout::Abi::ScalarPair(..) | - layout::Abi::Vector { .. } if discr.is_none() => { - let mut non_zst_fields = vals.iter().enumerate().map(|(i, f)| { - (f, layout.fields.offset(i)) - }).filter(|&(f, _)| !cx.layout_of(f.ty).is_zst()); - match (non_zst_fields.next(), non_zst_fields.next()) { - (Some((x, offset)), None) if offset.bytes() == 0 => { - return Const::new(x.llval, layout.ty); - } - (Some((a, a_offset)), Some((b, _))) if a_offset.bytes() == 0 => { - return Const::new(C_struct(cx, &[a.llval, b.llval], false), layout.ty); - } - (Some((a, _)), Some((b, b_offset))) if b_offset.bytes() == 0 => { - return Const::new(C_struct(cx, &[b.llval, a.llval], false), layout.ty); - } - _ => {} + pub fn mir_constant_to_miri_value( + &mut self, + bcx: &Builder<'a, 'tcx>, + constant: &mir::Constant<'tcx>, + ) -> Result> { + match constant.literal { + mir::Literal::Promoted { index } => { + let param_env = ty::ParamEnv::empty(traits::Reveal::All); + let cid = mir::interpret::GlobalId { + instance: self.instance, + promoted: Some(index), + }; + bcx.tcx().const_eval(param_env.and(cid)) } - } - _ => {} - } - - // offset of current value - let mut packed = false; - let mut offset = Size::from_bytes(0); - let mut cfields = Vec::new(); - cfields.reserve(discr.is_some() as usize + 1 + layout.fields.count() * 2); - - if let Some(discr) = discr { - let (field_size, field_align) = cx.size_and_align_of(discr.ty); - packed |= layout.align.abi() < field_align.abi(); - cfields.push(discr.llval); - offset = field_size; + mir::Literal::Value { value } => { + Ok(self.monomorphize(&value)) + } + }.and_then(|c| self.const_to_miri_value(bcx, c)) } - let parts = layout.fields.index_by_increasing_offset().map(|i| { - (vals[i], layout.fields.offset(i)) - }); - for (val, target_offset) in parts { - let (field_size, field_align) = cx.size_and_align_of(val.ty); - packed |= layout.align.abi() < field_align.abi(); - cfields.push(padding(cx, target_offset - offset)); - cfields.push(val.llval); - offset = target_offset + field_size; + // Old version of trans_constant now used just for SIMD shuffle + pub fn remove_me_shuffle_indices(&mut self, + bcx: &Builder<'a, 'tcx>, + constant: &mir::Constant<'tcx>) + -> (ValueRef, Ty<'tcx>) + { + let layout = bcx.ccx.layout_of(constant.ty); + self.mir_constant_to_miri_value(bcx, constant) + .and_then(|c| { + let llval = match c { + MiriValue::ByVal(val) => { + let scalar = match layout.abi { + layout::Abi::Scalar(ref x) => x, + _ => bug!("from_const: invalid ByVal layout: {:#?}", layout) + }; + primval_to_llvm(bcx.ccx, val, scalar, layout.immediate_llvm_type(bcx.ccx)) + }, + MiriValue::ByValPair(a_val, b_val) => { + let (a_scalar, b_scalar) = match layout.abi { + layout::Abi::ScalarPair(ref a, ref b) => (a, b), + _ => bug!("from_const: invalid ByValPair layout: {:#?}", layout) + }; + let a_llval = primval_to_llvm( + bcx.ccx, + a_val, + a_scalar, + layout.scalar_pair_element_llvm_type(bcx.ccx, 0), + ); + let b_llval = primval_to_llvm( + bcx.ccx, + b_val, + b_scalar, + layout.scalar_pair_element_llvm_type(bcx.ccx, 1), + ); + C_struct(bcx.ccx, &[a_llval, b_llval], false) + }, + MiriValue::ByRef(..) => { + let field_ty = constant.ty.builtin_index().unwrap(); + let fields = match constant.ty.sty { + ty::TyArray(_, n) => n.val.unwrap_u64(), + ref other => bug!("invalid simd shuffle type: {}", other), + }; + let values: Result, _> = (0..fields).map(|field| { + let field = const_val_field( + bcx.tcx(), + ty::ParamEnv::empty(traits::Reveal::All), + self.instance, + None, + mir::Field::new(field as usize), + c, + constant.ty, + )?; + match field.val { + ConstVal::Value(MiriValue::ByVal(prim)) => { + let layout = bcx.ccx.layout_of(field_ty); + let scalar = match layout.abi { + layout::Abi::Scalar(ref x) => x, + _ => bug!("from_const: invalid ByVal layout: {:#?}", layout) + }; + Ok(primval_to_llvm( + bcx.ccx, prim, scalar, + layout.immediate_llvm_type(bcx.ccx), + )) + }, + other => bug!("simd shuffle field {:?}, {}", other, constant.ty), + } + }).collect(); + C_struct(bcx.ccx, &values?, false) + }, + }; + Ok((llval, constant.ty)) + }) + .unwrap_or_else(|e| { + e.report(bcx.tcx(), constant.span, "shuffle_indices"); + // We've errored, so we don't have to produce working code. + let ty = self.monomorphize(&constant.ty); + let llty = bcx.ccx.layout_of(ty).llvm_type(bcx.ccx); + (C_undef(llty), ty) + }) } - - // Pad to the size of the whole type, not e.g. the variant. - cfields.push(padding(cx, cx.size_of(layout.ty) - offset)); - - Const::new(C_struct(cx, &cfields, packed), layout.ty) -} - -fn padding(cx: &CodegenCx, size: Size) -> ValueRef { - C_undef(Type::array(&Type::i8(cx), size.bytes())) } diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs index 99e3a59e2c4f4..a1044ac87e4c5 100644 --- a/src/librustc_trans/mir/mod.rs +++ b/src/librustc_trans/mir/mod.rs @@ -44,6 +44,8 @@ use self::operand::{OperandRef, OperandValue}; /// Master context for translating MIR. pub struct FunctionCx<'a, 'tcx:'a> { + instance: Instance<'tcx>, + mir: &'a mir::Mir<'tcx>, debug_context: debuginfo::FunctionDebugContext, @@ -227,6 +229,7 @@ pub fn trans_mir<'a, 'tcx: 'a>( let (landing_pads, funclets) = create_funclets(mir, &bx, &cleanup_kinds, &block_bxs); let mut fx = FunctionCx { + instance, mir, llfn, fn_ty, diff --git a/src/librustc_trans/mir/operand.rs b/src/librustc_trans/mir/operand.rs index e1b906646aa43..01cf324124c4f 100644 --- a/src/librustc_trans/mir/operand.rs +++ b/src/librustc_trans/mir/operand.rs @@ -9,12 +9,14 @@ // except according to those terms. use llvm::ValueRef; -use rustc::ty::layout::{self, Align, LayoutOf, TyLayout}; +use rustc::middle::const_val::ConstEvalErr; use rustc::mir; +use rustc::mir::interpret::Value as MiriValue; +use rustc::ty::layout::{self, Align, LayoutOf, TyLayout}; use rustc_data_structures::indexed_vec::Idx; use base; -use common::{self, CodegenCx, C_undef, C_usize}; +use common::{self, CodegenCx, C_null, C_undef, C_usize}; use builder::Builder; use value::Value; use type_of::LayoutLlvmExt; @@ -24,6 +26,7 @@ use std::fmt; use std::ptr; use super::{FunctionCx, LocalRef}; +use super::constant::{primval_to_llvm}; use super::place::PlaceRef; /// The representation of a Rust value. The enum variant is in fact @@ -89,6 +92,70 @@ impl<'a, 'tcx> OperandRef<'tcx> { } } + pub fn from_const(bcx: &Builder<'a, 'tcx>, + miri_val: MiriValue, + ty: ty::Ty<'tcx>) + -> Result, ConstEvalErr<'tcx>> { + let layout = bcx.ccx.layout_of(ty); + + if layout.is_zst() { + return Ok(OperandRef::new_zst(bcx.ccx, layout)); + } + + let val = match miri_val { + MiriValue::ByVal(x) => { + let scalar = match layout.abi { + layout::Abi::Scalar(ref x) => x, + _ => bug!("from_const: invalid ByVal layout: {:#?}", layout) + }; + let llval = primval_to_llvm( + bcx.ccx, + x, + scalar, + layout.immediate_llvm_type(bcx.ccx), + ); + OperandValue::Immediate(llval) + }, + MiriValue::ByValPair(a, b) => { + let (a_scalar, b_scalar) = match layout.abi { + layout::Abi::ScalarPair(ref a, ref b) => (a, b), + _ => bug!("from_const: invalid ByValPair layout: {:#?}", layout) + }; + let a_llval = primval_to_llvm( + bcx.ccx, + a, + a_scalar, + layout.scalar_pair_element_llvm_type(bcx.ccx, 0), + ); + let b_llval = primval_to_llvm( + bcx.ccx, + b, + b_scalar, + layout.scalar_pair_element_llvm_type(bcx.ccx, 1), + ); + OperandValue::Pair(a_llval, b_llval) + }, + MiriValue::ByRef(ptr, align) => { + let scalar = layout::Scalar { + value: layout::Primitive::Pointer, + valid_range: 0..=!0 + }; + let ptr = primval_to_llvm( + bcx.ccx, + ptr.into_inner_primval(), + &scalar, + layout.llvm_type(bcx.ccx).ptr_to(), + ); + return Ok(PlaceRef::new_sized(ptr, layout, align).load(bcx)); + }, + }; + + Ok(OperandRef { + val, + layout + }) + } + /// Asserts that this operand refers to a scalar and returns /// a reference to its value. pub fn immediate(self) -> ValueRef { @@ -327,14 +394,19 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { } mir::Operand::Constant(ref constant) => { - let val = self.trans_constant(&bx, constant); - let operand = val.to_operand(bx.cx); - if let OperandValue::Ref(ptr, align) = operand.val { - // If this is a OperandValue::Ref to an immediate constant, load it. - PlaceRef::new_sized(ptr, operand.layout, align).load(bx) - } else { - operand - } + let ty = self.monomorphize(&constant.ty); + self.mir_constant_to_miri_value(bx, constant) + .and_then(|c| OperandRef::from_const(bx, c, ty)) + .unwrap_or_else(|err| { + err.report(bx.tcx(), constant.span, "const operand"); + // We've errored, so we don't have to produce working code. + let layout = bx.cx.layout_of(ty); + PlaceRef::new_sized( + C_null(layout.llvm_type(bx.cx).ptr_to()), + layout, + layout.align, + ).load(bx) + }) } } } diff --git a/src/librustc_trans/trans_item.rs b/src/librustc_trans/trans_item.rs index 91c1097fc7f8a..06d94e8d15569 100644 --- a/src/librustc_trans/trans_item.rs +++ b/src/librustc_trans/trans_item.rs @@ -58,12 +58,7 @@ pub trait MonoItemExt<'a, 'tcx>: fmt::Debug + BaseMonoItemExt<'a, 'tcx> { }; let attrs = tcx.get_attrs(def_id); - match consts::trans_static(&cx, def_id, is_mutable, &attrs) { - Ok(_) => { /* Cool, everything's alright. */ }, - Err(err) => { - err.report(tcx, tcx.def_span(def_id), "static"); - } - }; + consts::trans_static(&cx, def_id, is_mutable, &attrs); } MonoItem::GlobalAsm(node_id) => { let item = cx.tcx.hir.expect_item(node_id); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 1ad26b4cda59d..f5303979f9c32 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -4013,7 +4013,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let count = tcx.const_eval(param_env.and(global_id)); if let Err(ref err) = count { - err.report(tcx, tcx.def_span(count_def_id), "constant expression"); + err.report(tcx, tcx.def_span(count_def_id), "constant expression"); } let uty = match expected { diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 0271ad269e12f..bb41acc541001 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -535,7 +535,7 @@ fn convert_enum_variant_types<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // so we need to report the real error if let Err(ref err) = result { err.report(tcx, variant.span, "enum discriminant"); - } +} match result { Ok(&ty::Const { diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index d1e6f27069c0d..b9e4ce661d0c4 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2500,7 +2500,11 @@ impl Clean for hir::Ty { let def_id = cx.tcx.hir.body_owner_def_id(n); let param_env = cx.tcx.param_env(def_id); let substs = Substs::identity_for_item(cx.tcx, def_id); - let n = cx.tcx.const_eval(param_env.and((def_id, substs))).unwrap_or_else(|_| { + let cid = GlobalId { + instance: ty::Instance::new(def_id, substs), + promoted: None + }; + let n = cx.tcx.const_eval(param_env.and(cid)).unwrap_or_else(|_| { cx.tcx.mk_const(ty::Const { val: ConstVal::Unevaluated(def_id, substs), ty: cx.tcx.types.usize @@ -2633,7 +2637,11 @@ impl<'tcx> Clean for Ty<'tcx> { let mut n = cx.tcx.lift(&n).unwrap(); if let ConstVal::Unevaluated(def_id, substs) = n.val { let param_env = cx.tcx.param_env(def_id); - if let Ok(new_n) = cx.tcx.const_eval(param_env.and((def_id, substs))) { + let cid = GlobalId { + instance: ty::Instance::new(def_id, substs), + promoted: None + }; + if let Ok(new_n) = cx.tcx.const_eval(param_env.and(cid)) { n = new_n; } }; diff --git a/src/test/compile-fail/const-call.rs b/src/test/compile-fail/const-call.rs index 18476494300b2..02264228a6bf6 100644 --- a/src/test/compile-fail/const-call.rs +++ b/src/test/compile-fail/const-call.rs @@ -15,4 +15,5 @@ fn f(x: usize) -> usize { fn main() { let _ = [0; f(2)]; //~^ ERROR calls in constants are limited to constant functions + //~| E0080 } diff --git a/src/test/compile-fail/const-err-early.rs b/src/test/compile-fail/const-err-early.rs index 42fb40394fb29..8c2e6233abfe2 100644 --- a/src/test/compile-fail/const-err-early.rs +++ b/src/test/compile-fail/const-err-early.rs @@ -11,14 +11,15 @@ #![feature(const_indexing)] #![deny(const_err)] -pub const A: i8 = -std::i8::MIN; //~ ERROR attempt to negate with overflow -pub const B: u8 = 200u8 + 200u8; //~ ERROR attempt to add with overflow -pub const C: u8 = 200u8 * 4; //~ ERROR attempt to multiply with overflow -pub const D: u8 = 42u8 - (42u8 + 1); //~ ERROR attempt to subtract with overflow +pub const A: i8 = -std::i8::MIN; //~ ERROR E0080 +//~^ ERROR attempt to negate with overflow +//~| ERROR const_err +pub const B: u8 = 200u8 + 200u8; //~ ERROR E0080 +pub const C: u8 = 200u8 * 4; //~ ERROR E0080 +pub const D: u8 = 42u8 - (42u8 + 1); //~ ERROR E0080 pub const E: u8 = [5u8][1]; -//~^ ERROR index out of bounds: the len is 1 but the index is 1 +//~^ ERROR E0080 fn main() { let _e = [6u8][1]; - //~^ ERROR index out of bounds: the len is 1 but the index is 1 } diff --git a/src/test/compile-fail/const-err-multi.rs b/src/test/compile-fail/const-err-multi.rs index d4f9c0fe56dae..668f95f2c8d73 100644 --- a/src/test/compile-fail/const-err-multi.rs +++ b/src/test/compile-fail/const-err-multi.rs @@ -11,9 +11,14 @@ #![deny(const_err)] pub const A: i8 = -std::i8::MIN; //~ ERROR attempt to negate with overflow +//~^ ERROR E0080 +//~| ERROR const_err pub const B: i8 = A; +//~^ ERROR E0080 pub const C: u8 = A as u8; +//~^ ERROR E0080 pub const D: i8 = 50 - A; +//~^ ERROR E0080 fn main() { } diff --git a/src/test/compile-fail/const-err.rs b/src/test/compile-fail/const-err.rs index e65194ab56f98..8bd759b6d3735 100644 --- a/src/test/compile-fail/const-err.rs +++ b/src/test/compile-fail/const-err.rs @@ -26,26 +26,5 @@ const FOO: u8 = [5u8][1]; //~| index out of bounds: the len is 1 but the index is 1 fn main() { - let a = -std::i8::MIN; - //~^ WARN this expression will panic at run-time - //~| attempt to negate with overflow - let b = 200u8 + 200u8 + 200u8; - //~^ WARN this expression will panic at run-time - //~^^ WARN this expression will panic at run-time - //~| attempt to add with overflow - let c = 200u8 * 4; - //~^ WARN this expression will panic at run-time - //~| attempt to multiply with overflow - let d = 42u8 - (42u8 + 1); - //~^ WARN this expression will panic at run-time - //~| attempt to subtract with overflow - let _e = [5u8][1]; - //~^ WARN this expression will panic at run-time - //~| index out of bounds: the len is 1 but the index is 1 - black_box(a); - black_box(b); - black_box(c); - black_box(d); - black_box((FOO, FOO)); } diff --git a/src/test/compile-fail/const-err2.rs b/src/test/compile-fail/const-err2.rs index 9889ca1392ac1..a0648993aac74 100644 --- a/src/test/compile-fail/const-err2.rs +++ b/src/test/compile-fail/const-err2.rs @@ -18,13 +18,11 @@ fn black_box(_: T) { fn main() { let a = -std::i8::MIN; - //~^ ERROR attempt to negate with overflow + //~^ ERROR const_err + //~| ERROR const_err let b = 200u8 + 200u8 + 200u8; - //~^ ERROR attempt to add with overflow let c = 200u8 * 4; - //~^ ERROR attempt to multiply with overflow let d = 42u8 - (42u8 + 1); - //~^ ERROR attempt to subtract with overflow let _e = [5u8][1]; black_box(a); black_box(b); diff --git a/src/test/compile-fail/const-err3.rs b/src/test/compile-fail/const-err3.rs new file mode 100644 index 0000000000000..636537d1df250 --- /dev/null +++ b/src/test/compile-fail/const-err3.rs @@ -0,0 +1,28 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_attrs)] +#![deny(const_err)] + +fn black_box(_: T) { + unimplemented!() +} + +fn main() { + let b = 200u8 + 200u8 + 200u8; + //~^ ERROR const_err + //~| ERROR const_err + let c = 200u8 * 4; + let d = 42u8 - (42u8 + 1); + let _e = [5u8][1]; + black_box(b); + black_box(c); + black_box(d); +} diff --git a/src/test/compile-fail/const-eval-overflow.rs b/src/test/compile-fail/const-eval-overflow.rs deleted file mode 100644 index 058a8d0a1bd4f..0000000000000 --- a/src/test/compile-fail/const-eval-overflow.rs +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(unused_imports)] - -// Note: the relevant lint pass here runs before some of the constant -// evaluation below (e.g. that performed by trans and llvm), so if you -// change this warn to a deny, then the compiler will exit before -// those errors are detected. - -#![warn(const_err)] - -use std::fmt; -use std::{i8, i16, i32, i64, isize}; -use std::{u8, u16, u32, u64, usize}; - -const VALS_I8: (i8, i8, i8, i8) = - (-i8::MIN, - //~^ ERROR constant evaluation error - //~| attempt to negate with overflow - i8::MIN - 1, - //~^ ERROR constant evaluation error - //~| attempt to subtract with overflow - i8::MAX + 1, - //~^ ERROR constant evaluation error - //~| attempt to add with overflow - i8::MIN * 2, - //~^ ERROR constant evaluation error - //~| attempt to multiply with overflow - ); - -const VALS_I16: (i16, i16, i16, i16) = - (-i16::MIN, - //~^ ERROR constant evaluation error - //~| attempt to negate with overflow - i16::MIN - 1, - //~^ ERROR constant evaluation error - //~| attempt to subtract with overflow - i16::MAX + 1, - //~^ ERROR constant evaluation error - //~| attempt to add with overflow - i16::MIN * 2, - //~^ ERROR constant evaluation error - //~| attempt to multiply with overflow - ); - -const VALS_I32: (i32, i32, i32, i32) = - (-i32::MIN, - //~^ ERROR constant evaluation error - //~| attempt to negate with overflow - i32::MIN - 1, - //~^ ERROR constant evaluation error - //~| attempt to subtract with overflow - i32::MAX + 1, - //~^ ERROR constant evaluation error - //~| attempt to add with overflow - i32::MIN * 2, - //~^ ERROR constant evaluation error - //~| attempt to multiply with overflow - ); - -const VALS_I64: (i64, i64, i64, i64) = - (-i64::MIN, - //~^ ERROR constant evaluation error - //~| attempt to negate with overflow - i64::MIN - 1, - //~^ ERROR constant evaluation error - //~| attempt to subtract with overflow - i64::MAX + 1, - //~^ ERROR constant evaluation error - //~| attempt to add with overflow - i64::MAX * 2, - //~^ ERROR constant evaluation error - //~| attempt to multiply with overflow - ); - -const VALS_U8: (u8, u8, u8, u8) = - ( //~ WARN constant evaluation error: attempt to subtract with overflow - -(u8::MIN as i8) as u8, - u8::MIN - 1, - //~^ ERROR constant evaluation error - //~| attempt to subtract with overflow - u8::MAX + 1, - //~^ ERROR constant evaluation error - //~| attempt to add with overflow - u8::MAX * 2, - //~^ ERROR constant evaluation error - //~| attempt to multiply with overflow - ); - -const VALS_U16: (u16, u16, u16, u16) = - ( //~ WARN constant evaluation error: attempt to subtract with overflow - -(u16::MIN as i16) as u16, - u16::MIN - 1, - //~^ ERROR constant evaluation error - //~| attempt to subtract with overflow - u16::MAX + 1, - //~^ ERROR constant evaluation error - //~| attempt to add with overflow - u16::MAX * 2, - //~^ ERROR constant evaluation error - //~| attempt to multiply with overflow - ); - -const VALS_U32: (u32, u32, u32, u32) = - ( //~ WARN constant evaluation error: attempt to subtract with overflow - -(u32::MIN as i32) as u32, - u32::MIN - 1, - //~^ ERROR constant evaluation error - //~| attempt to subtract with overflow - u32::MAX + 1, - //~^ ERROR constant evaluation error - //~| attempt to add with overflow - u32::MAX * 2, - //~^ ERROR constant evaluation error - //~| attempt to multiply with overflow - ); - -const VALS_U64: (u64, u64, u64, u64) = - ( //~ WARN constant evaluation error: attempt to subtract with overflow - -(u64::MIN as i64) as u64, - u64::MIN - 1, - //~^ ERROR constant evaluation error - //~| attempt to subtract with overflow - u64::MAX + 1, - //~^ ERROR constant evaluation error - //~| attempt to add with overflow - u64::MAX * 2, - //~^ ERROR constant evaluation error - //~| attempt to multiply with overflow - ); - -fn main() { - foo(VALS_I8); - foo(VALS_I16); - foo(VALS_I32); - foo(VALS_I64); - - foo(VALS_U8); - foo(VALS_U16); - foo(VALS_U32); - foo(VALS_U64); -} - -fn foo(x: T) { - println!("{:?}", x); -} diff --git a/src/test/compile-fail/const-eval-overflow2.rs b/src/test/compile-fail/const-eval-overflow2.rs new file mode 100644 index 0000000000000..61a653589fff9 --- /dev/null +++ b/src/test/compile-fail/const-eval-overflow2.rs @@ -0,0 +1,91 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_imports)] + +// Note: the relevant lint pass here runs before some of the constant +// evaluation below (e.g. that performed by trans and llvm), so if you +// change this warn to a deny, then the compiler will exit before +// those errors are detected. + +#![deny(const_err)] + +use std::fmt; +use std::{i8, i16, i32, i64, isize}; +use std::{u8, u16, u32, u64, usize}; + +const VALS_I8: (i8,) = + ( + i8::MIN - 1, + //~^ ERROR constant evaluation error + //~| attempt to subtract with overflow + ); + +const VALS_I16: (i16,) = + ( + i16::MIN - 1, + //~^ ERROR constant evaluation error + //~| attempt to subtract with overflow + ); + +const VALS_I32: (i32,) = + ( + i32::MIN - 1, + //~^ ERROR constant evaluation error + //~| attempt to subtract with overflow + ); + +const VALS_I64: (i64,) = + ( + i64::MIN - 1, + //~^ ERROR constant evaluation error + //~| attempt to subtract with overflow + ); + +const VALS_U8: (u8,) = + ( + u8::MIN - 1, + //~^ ERROR constant evaluation error + //~| attempt to subtract with overflow + ); + +const VALS_U16: (u16,) = ( + u16::MIN - 1, + //~^ ERROR constant evaluation error + //~| attempt to subtract with overflow + ); + +const VALS_U32: (u32,) = ( + u32::MIN - 1, + //~^ ERROR constant evaluation error + //~| attempt to subtract with overflow + ); + +const VALS_U64: (u64,) = + ( + u64::MIN - 1, + //~^ ERROR constant evaluation error + //~| attempt to subtract with overflow + ); + +fn main() { + foo(VALS_I8); + foo(VALS_I16); + foo(VALS_I32); + foo(VALS_I64); + + foo(VALS_U8); + foo(VALS_U16); + foo(VALS_U32); + foo(VALS_U64); +} + +fn foo(_: T) { +} diff --git a/src/test/compile-fail/const-eval-overflow2b.rs b/src/test/compile-fail/const-eval-overflow2b.rs new file mode 100644 index 0000000000000..b2e84486101bc --- /dev/null +++ b/src/test/compile-fail/const-eval-overflow2b.rs @@ -0,0 +1,91 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_imports)] + +// Note: the relevant lint pass here runs before some of the constant +// evaluation below (e.g. that performed by trans and llvm), so if you +// change this warn to a deny, then the compiler will exit before +// those errors are detected. + +#![deny(const_err)] + +use std::fmt; +use std::{i8, i16, i32, i64, isize}; +use std::{u8, u16, u32, u64, usize}; + +const VALS_I8: (i8,) = + ( + i8::MAX + 1, + //~^ ERROR constant evaluation error + //~| attempt to add with overflow + ); + +const VALS_I16: (i16,) = + ( + i16::MAX + 1, + //~^ ERROR constant evaluation error + //~| attempt to add with overflow + ); + +const VALS_I32: (i32,) = + ( + i32::MAX + 1, + //~^ ERROR constant evaluation error + //~| attempt to add with overflow + ); + +const VALS_I64: (i64,) = + ( + i64::MAX + 1, + //~^ ERROR constant evaluation error + //~| attempt to add with overflow + ); + +const VALS_U8: (u8,) = + ( + u8::MAX + 1, + //~^ ERROR constant evaluation error + //~| attempt to add with overflow + ); + +const VALS_U16: (u16,) = ( + u16::MAX + 1, + //~^ ERROR constant evaluation error + //~| attempt to add with overflow + ); + +const VALS_U32: (u32,) = ( + u32::MAX + 1, + //~^ ERROR constant evaluation error + //~| attempt to add with overflow + ); + +const VALS_U64: (u64,) = + ( + u64::MAX + 1, + //~^ ERROR constant evaluation error + //~| attempt to add with overflow + ); + +fn main() { + foo(VALS_I8); + foo(VALS_I16); + foo(VALS_I32); + foo(VALS_I64); + + foo(VALS_U8); + foo(VALS_U16); + foo(VALS_U32); + foo(VALS_U64); +} + +fn foo(_: T) { +} diff --git a/src/test/compile-fail/const-eval-overflow2c.rs b/src/test/compile-fail/const-eval-overflow2c.rs new file mode 100644 index 0000000000000..c4a8beaf0f057 --- /dev/null +++ b/src/test/compile-fail/const-eval-overflow2c.rs @@ -0,0 +1,91 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_imports)] + +// Note: the relevant lint pass here runs before some of the constant +// evaluation below (e.g. that performed by trans and llvm), so if you +// change this warn to a deny, then the compiler will exit before +// those errors are detected. + +#![deny(const_err)] + +use std::fmt; +use std::{i8, i16, i32, i64, isize}; +use std::{u8, u16, u32, u64, usize}; + +const VALS_I8: (i8,) = + ( + i8::MIN * 2, + //~^ ERROR constant evaluation error + //~| attempt to multiply with overflow + ); + +const VALS_I16: (i16,) = + ( + i16::MIN * 2, + //~^ ERROR constant evaluation error + //~| attempt to multiply with overflow + ); + +const VALS_I32: (i32,) = + ( + i32::MIN * 2, + //~^ ERROR constant evaluation error + //~| attempt to multiply with overflow + ); + +const VALS_I64: (i64,) = + ( + i64::MIN * 2, + //~^ ERROR constant evaluation error + //~| attempt to multiply with overflow + ); + +const VALS_U8: (u8,) = + ( + u8::MAX * 2, + //~^ ERROR constant evaluation error + //~| attempt to multiply with overflow + ); + +const VALS_U16: (u16,) = ( + u16::MAX * 2, + //~^ ERROR constant evaluation error + //~| attempt to multiply with overflow + ); + +const VALS_U32: (u32,) = ( + u32::MAX * 2, + //~^ ERROR constant evaluation error + //~| attempt to multiply with overflow + ); + +const VALS_U64: (u64,) = + ( + u64::MAX * 2, + //~^ ERROR constant evaluation error + //~| attempt to multiply with overflow + ); + +fn main() { + foo(VALS_I8); + foo(VALS_I16); + foo(VALS_I32); + foo(VALS_I64); + + foo(VALS_U8); + foo(VALS_U16); + foo(VALS_U32); + foo(VALS_U64); +} + +fn foo(_: T) { +} diff --git a/src/test/compile-fail/const-integer-bool-ops.rs b/src/test/compile-fail/const-integer-bool-ops.rs index 29bc665a22e7b..3065122af6a94 100644 --- a/src/test/compile-fail/const-integer-bool-ops.rs +++ b/src/test/compile-fail/const-integer-bool-ops.rs @@ -16,6 +16,7 @@ const X: usize = 42 && 39; //~| ERROR mismatched types //~| expected usize, found bool const ARR: [i32; X] = [99; 34]; +//~^ ERROR constant evaluation error const X1: usize = 42 || 39; //~^ ERROR mismatched types @@ -25,6 +26,7 @@ const X1: usize = 42 || 39; //~| ERROR mismatched types //~| expected usize, found bool const ARR1: [i32; X1] = [99; 47]; +//~^ ERROR constant evaluation error const X2: usize = -42 || -39; //~^ ERROR mismatched types @@ -34,6 +36,7 @@ const X2: usize = -42 || -39; //~| ERROR mismatched types //~| expected usize, found bool const ARR2: [i32; X2] = [99; 18446744073709551607]; +//~^ ERROR constant evaluation error const X3: usize = -42 && -39; //~^ ERROR mismatched types @@ -43,36 +46,43 @@ const X3: usize = -42 && -39; //~| ERROR mismatched types //~| expected usize, found bool const ARR3: [i32; X3] = [99; 6]; +//~^ ERROR constant evaluation error const Y: usize = 42.0 == 42.0; //~^ ERROR mismatched types //~| expected usize, found bool const ARRR: [i32; Y] = [99; 1]; +//~^ ERROR constant evaluation error const Y1: usize = 42.0 >= 42.0; //~^ ERROR mismatched types //~| expected usize, found bool const ARRR1: [i32; Y1] = [99; 1]; +//~^ ERROR constant evaluation error const Y2: usize = 42.0 <= 42.0; //~^ ERROR mismatched types //~| expected usize, found bool const ARRR2: [i32; Y2] = [99; 1]; +//~^ ERROR constant evaluation error const Y3: usize = 42.0 > 42.0; //~^ ERROR mismatched types //~| expected usize, found bool const ARRR3: [i32; Y3] = [99; 0]; +//~^ ERROR constant evaluation error const Y4: usize = 42.0 < 42.0; //~^ ERROR mismatched types //~| expected usize, found bool const ARRR4: [i32; Y4] = [99; 0]; +//~^ ERROR constant evaluation error const Y5: usize = 42.0 != 42.0; //~^ ERROR mismatched types //~| expected usize, found bool const ARRR5: [i32; Y5] = [99; 0]; +//~^ ERROR constant evaluation error fn main() { let _ = ARR; diff --git a/src/test/compile-fail/const-len-underflow-subspans.rs b/src/test/compile-fail/const-len-underflow-subspans.rs index 7f2229b5a6534..85cc893aa133c 100644 --- a/src/test/compile-fail/const-len-underflow-subspans.rs +++ b/src/test/compile-fail/const-len-underflow-subspans.rs @@ -16,6 +16,6 @@ const TWO: usize = 2; fn main() { let a: [i8; ONE - TWO] = unimplemented!(); - //~^ ERROR constant evaluation error [E0080] + //~^ ERROR constant evaluation error //~| attempt to subtract with overflow } diff --git a/src/test/compile-fail/const-slice-oob.rs b/src/test/compile-fail/const-slice-oob.rs index b1b4bfe2d1c39..179ea9e853f3a 100644 --- a/src/test/compile-fail/const-slice-oob.rs +++ b/src/test/compile-fail/const-slice-oob.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[deny(const_err)] + const FOO: &'static[u32] = &[1, 2, 3]; const BAR: u32 = FOO[5]; //~^ ERROR constant evaluation error [E0080] diff --git a/src/test/compile-fail/const-tup-index-span.rs b/src/test/compile-fail/const-tup-index-span.rs index b42c440f87d74..7596881ef9b9a 100644 --- a/src/test/compile-fail/const-tup-index-span.rs +++ b/src/test/compile-fail/const-tup-index-span.rs @@ -14,6 +14,7 @@ const TUP: (usize,) = 5usize << 64; //~^ ERROR mismatched types //~| expected tuple, found usize const ARR: [i32; TUP.0] = []; +//~^ ERROR constant evaluation error fn main() { } diff --git a/src/test/compile-fail/eval-enum.rs b/src/test/compile-fail/eval-enum.rs index 86cc2c144ac07..49f76c532df54 100644 --- a/src/test/compile-fail/eval-enum.rs +++ b/src/test/compile-fail/eval-enum.rs @@ -8,12 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -enum test { - div_zero = 1/0, //~ ERROR E0080 - //~| attempt to divide by zero - rem_zero = 1%0, - //~^ ERROR E0080 - //~| attempt to calculate the remainder with a divisor of zero +enum Test { + DivZero = 1/0, + //~^ attempt to divide by zero + //~| ERROR constant evaluation error + //~| WARN constant evaluation error + RemZero = 1%0, + //~^ attempt to calculate the remainder with a divisor of zero + //~| ERROR constant evaluation error + //~| WARN constant evaluation error } fn main() {} diff --git a/src/test/compile-fail/float-int-invalid-const-cast.rs b/src/test/compile-fail/float-int-invalid-const-cast.rs deleted file mode 100644 index 1f07422e21be4..0000000000000 --- a/src/test/compile-fail/float-int-invalid-const-cast.rs +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![feature(i128_type)] -#![allow(const_err)] // this test is only about hard errors - -use std::{f32, f64}; - -// Forces evaluation of constants, triggering hard error -fn force(_: T) {} - -fn main() { - { const X: u16 = -1. as u16; force(X); } //~ ERROR constant evaluation error - { const X: u128 = -100. as u128; force(X); } //~ ERROR constant evaluation error - - { const X: i8 = f32::NAN as i8; force(X); } //~ ERROR constant evaluation error - { const X: i32 = f32::NAN as i32; force(X); } //~ ERROR constant evaluation error - { const X: u64 = f32::NAN as u64; force(X); } //~ ERROR constant evaluation error - { const X: u128 = f32::NAN as u128; force(X); } //~ ERROR constant evaluation error - - { const X: i8 = f32::INFINITY as i8; force(X); } //~ ERROR constant evaluation error - { const X: u32 = f32::INFINITY as u32; force(X); } //~ ERROR constant evaluation error - { const X: i128 = f32::INFINITY as i128; force(X); } //~ ERROR constant evaluation error - { const X: u128 = f32::INFINITY as u128; force(X); } //~ ERROR constant evaluation error - - { const X: u8 = f32::NEG_INFINITY as u8; force(X); } //~ ERROR constant evaluation error - { const X: u16 = f32::NEG_INFINITY as u16; force(X); } //~ ERROR constant evaluation error - { const X: i64 = f32::NEG_INFINITY as i64; force(X); } //~ ERROR constant evaluation error - { const X: i128 = f32::NEG_INFINITY as i128; force(X); } //~ ERROR constant evaluation error - - { const X: i8 = f64::NAN as i8; force(X); } //~ ERROR constant evaluation error - { const X: i32 = f64::NAN as i32; force(X); } //~ ERROR constant evaluation error - { const X: u64 = f64::NAN as u64; force(X); } //~ ERROR constant evaluation error - { const X: u128 = f64::NAN as u128; force(X); } //~ ERROR constant evaluation error - - { const X: i8 = f64::INFINITY as i8; force(X); } //~ ERROR constant evaluation error - { const X: u32 = f64::INFINITY as u32; force(X); } //~ ERROR constant evaluation error - { const X: i128 = f64::INFINITY as i128; force(X); } //~ ERROR constant evaluation error - { const X: u128 = f64::INFINITY as u128; force(X); } //~ ERROR constant evaluation error - - { const X: u8 = f64::NEG_INFINITY as u8; force(X); } //~ ERROR constant evaluation error - { const X: u16 = f64::NEG_INFINITY as u16; force(X); } //~ ERROR constant evaluation error - { const X: i64 = f64::NEG_INFINITY as i64; force(X); } //~ ERROR constant evaluation error - { const X: i128 = f64::NEG_INFINITY as i128; force(X); } //~ ERROR constant evaluation error - - { const X: u8 = 256. as u8; force(X); } //~ ERROR constant evaluation error - { const X: i8 = -129. as i8; force(X); } //~ ERROR constant evaluation error - { const X: i8 = 128. as i8; force(X); } //~ ERROR constant evaluation error - { const X: i32 = 2147483648. as i32; force(X); } //~ ERROR constant evaluation error - { const X: i32 = -2147483904. as i32; force(X); } //~ ERROR constant evaluation error - { const X: u32 = 4294967296. as u32; force(X); } //~ ERROR constant evaluation error - { const X: u128 = 1e40 as u128; force(X); } //~ ERROR constant evaluation error - { const X: i128 = 1e40 as i128; force(X); } //~ ERROR constant evaluation error -} diff --git a/src/test/compile-fail/issue-27895.rs b/src/test/compile-fail/issue-27895.rs index ca8d5a1f70473..be76796c5c465 100644 --- a/src/test/compile-fail/issue-27895.rs +++ b/src/test/compile-fail/issue-27895.rs @@ -14,8 +14,7 @@ fn main() { match i { 0...index => println!("winner"), - //~^ ERROR constant evaluation error - //~| non-constant path in constant expression + //~^ ERROR runtime values cannot be referenced in patterns _ => println!("hello"), } } diff --git a/src/test/compile-fail/issue-41255.rs b/src/test/compile-fail/issue-41255.rs index 823ece2bdca01..ac85e43cf4f05 100644 --- a/src/test/compile-fail/issue-41255.rs +++ b/src/test/compile-fail/issue-41255.rs @@ -18,33 +18,33 @@ fn main() { let x = 42.0; match x { - 5.0 => {}, //~ ERROR floating-point cannot be used + 5.0 => {}, //~ ERROR floating-point types cannot be used in patterns //~| WARNING hard error - 5.0f32 => {}, //~ ERROR floating-point cannot be used + 5.0f32 => {}, //~ ERROR floating-point types cannot be used in patterns //~| WARNING hard error - -5.0 => {}, //~ ERROR floating-point cannot be used + -5.0 => {}, //~ ERROR floating-point types cannot be used in patterns //~| WARNING hard error - 1.0 .. 33.0 => {}, //~ ERROR floating-point cannot be used + 1.0 .. 33.0 => {}, //~ ERROR floating-point types cannot be used in patterns //~| WARNING hard error - //~| ERROR floating-point cannot be used + //~| ERROR floating-point types cannot be used in patterns //~| WARNING hard error - 39.0 ... 70.0 => {}, //~ ERROR floating-point cannot be used + 39.0 ... 70.0 => {}, //~ ERROR floating-point types cannot be used in patterns //~| WARNING hard error - //~| ERROR floating-point cannot be used + //~| ERROR floating-point types cannot be used in patterns //~| WARNING hard error _ => {}, }; let y = 5.0; // Same for tuples match (x, 5) { - (3.14, 1) => {}, //~ ERROR floating-point cannot be used + (3.14, 1) => {}, //~ ERROR floating-point types cannot be used //~| WARNING hard error _ => {}, } // Or structs struct Foo { x: f32 }; match (Foo { x }) { - Foo { x: 2.0 } => {}, //~ ERROR floating-point cannot be used + Foo { x: 2.0 } => {}, //~ ERROR floating-point types cannot be used //~| WARNING hard error _ => {}, } diff --git a/src/test/compile-fail/issue-44578.rs b/src/test/compile-fail/issue-44578.rs index a6ae21c3b5497..9efe3d90a93c5 100644 --- a/src/test/compile-fail/issue-44578.rs +++ b/src/test/compile-fail/issue-44578.rs @@ -18,7 +18,7 @@ enum Bar { } impl Foo for Bar { - const AMT: usize = [A::AMT][(A::AMT > B::AMT) as usize]; //~ ERROR constant evaluation + const AMT: usize = [A::AMT][(A::AMT > B::AMT) as usize]; //~ E0080 } impl Foo for u8 { @@ -30,5 +30,5 @@ impl Foo for u16 { } fn main() { - println!("{}", as Foo>::AMT); + println!("{}", as Foo>::AMT); //~ E0080 } diff --git a/src/test/compile-fail/issue-6804.rs b/src/test/compile-fail/issue-6804.rs index 2a97945f26648..9191dfa155c65 100644 --- a/src/test/compile-fail/issue-6804.rs +++ b/src/test/compile-fail/issue-6804.rs @@ -12,18 +12,21 @@ #![feature(slice_patterns)] #![allow(unused)] +#![deny(illegal_floating_point_literal_pattern)] use std::f64::NAN; fn main() { let x = NAN; match x { - NAN => {}, //~ ERROR floating point constants cannot be used + NAN => {}, //~ ERROR floating-point types cannot be used + //~^ WARN this was previously accepted by the compiler but is being phased out _ => {}, }; match [x, 1.0] { - [NAN, _] => {}, //~ ERROR floating point constants cannot be used + [NAN, _] => {}, //~ ERROR floating-point types cannot be used + //~^ WARN this was previously accepted by the compiler but is being phased out _ => {}, }; } diff --git a/src/test/compile-fail/issue-8460-const.rs b/src/test/compile-fail/issue-8460-const.rs index d8ab48d1ec3e6..1d59e75a0f0f0 100644 --- a/src/test/compile-fail/issue-8460-const.rs +++ b/src/test/compile-fail/issue-8460-const.rs @@ -16,42 +16,62 @@ use std::thread; fn main() { assert!(thread::spawn(move|| { isize::MIN / -1; }).join().is_err()); //~^ ERROR attempt to divide with overflow + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i8::MIN / -1; }).join().is_err()); //~^ ERROR attempt to divide with overflow + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i16::MIN / -1; }).join().is_err()); //~^ ERROR attempt to divide with overflow + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i32::MIN / -1; }).join().is_err()); //~^ ERROR attempt to divide with overflow + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i64::MIN / -1; }).join().is_err()); //~^ ERROR attempt to divide with overflow + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1isize / 0; }).join().is_err()); //~^ ERROR attempt to divide by zero + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i8 / 0; }).join().is_err()); //~^ ERROR attempt to divide by zero + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i16 / 0; }).join().is_err()); //~^ ERROR attempt to divide by zero + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i32 / 0; }).join().is_err()); //~^ ERROR attempt to divide by zero + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i64 / 0; }).join().is_err()); //~^ ERROR attempt to divide by zero + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with overflow + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with overflow + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with overflow + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with overflow + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with overflow + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1isize % 0; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with a divisor of zero + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i8 % 0; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with a divisor of zero + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i16 % 0; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with a divisor of zero + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i32 % 0; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with a divisor of zero + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i64 % 0; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with a divisor of zero + //~| ERROR constant evaluation error } diff --git a/src/test/compile-fail/lint-exceeding-bitshifts.rs b/src/test/compile-fail/lint-exceeding-bitshifts.rs index e94d1ff779367..2634157c80671 100644 --- a/src/test/compile-fail/lint-exceeding-bitshifts.rs +++ b/src/test/compile-fail/lint-exceeding-bitshifts.rs @@ -10,7 +10,7 @@ #![deny(exceeding_bitshifts)] #![allow(unused_variables)] -#![allow(dead_code)] +#![allow(dead_code, const_err)] #![feature(const_indexing)] fn main() { @@ -54,21 +54,5 @@ fn main() { let n = 1u8 << -8; //~ ERROR: bitshift exceeds the type's number of bits - - let n = 1u8 << (4+3); - let n = 1u8 << (4+4); //~ ERROR: bitshift exceeds the type's number of bits - - #[cfg(target_pointer_width = "32")] - const BITS: usize = 32; - #[cfg(target_pointer_width = "64")] - const BITS: usize = 64; - - let n = 1_isize << BITS; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1_usize << BITS; //~ ERROR: bitshift exceeds the type's number of bits - - let n = 1i8<<(1isize+-1); - - let n = 1i64 >> [63][0]; - let n = 1i64 >> [64][0]; //~ ERROR: bitshift exceeds the type's number of bits } diff --git a/src/test/compile-fail/lint-exceeding-bitshifts2.rs b/src/test/compile-fail/lint-exceeding-bitshifts2.rs new file mode 100644 index 0000000000000..d95e1b370aafd --- /dev/null +++ b/src/test/compile-fail/lint-exceeding-bitshifts2.rs @@ -0,0 +1,27 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![deny(exceeding_bitshifts)] +#![allow(unused_variables, const_err)] +#![allow(dead_code)] + +fn main() { + let n = 1u8 << (4+3); + let n = 1u8 << (4+4); //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i64 >> [63][0]; + let n = 1i64 >> [64][0]; // should be linting, needs to wait for const propagation + + #[cfg(target_pointer_width = "32")] + const BITS: usize = 32; + #[cfg(target_pointer_width = "64")] + const BITS: usize = 64; + let n = 1_isize << BITS; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1_usize << BITS; //~ ERROR: bitshift exceeds the type's number of bits +} diff --git a/src/test/compile-fail/lint-type-overflow2.rs b/src/test/compile-fail/lint-type-overflow2.rs index f7cf8a68d5684..9178d8d2f6ed2 100644 --- a/src/test/compile-fail/lint-type-overflow2.rs +++ b/src/test/compile-fail/lint-type-overflow2.rs @@ -17,6 +17,7 @@ #[rustc_error] fn main() { //~ ERROR: compilation successful let x2: i8 = --128; //~ warn: literal out of range for i8 + //~^ WARN constant evaluation error let x = -3.40282357e+38_f32; //~ warn: literal out of range for f32 let x = 3.40282357e+38_f32; //~ warn: literal out of range for f32 diff --git a/src/test/compile-fail/non-constant-in-const-path.rs b/src/test/compile-fail/non-constant-in-const-path.rs index 737f80372debf..fe88ab4e11798 100644 --- a/src/test/compile-fail/non-constant-in-const-path.rs +++ b/src/test/compile-fail/non-constant-in-const-path.rs @@ -12,7 +12,6 @@ fn main() { let x = 0; match 1 { 0 ... x => {} - //~^ ERROR constant evaluation error - //~| non-constant path in constant expression + //~^ ERROR runtime values cannot be referenced in patterns }; } diff --git a/src/test/compile-fail/rfc1445/match-forbidden-without-eq.rs b/src/test/compile-fail/rfc1445/match-forbidden-without-eq.rs index fd888b659f142..ca9af78dd9936 100644 --- a/src/test/compile-fail/rfc1445/match-forbidden-without-eq.rs +++ b/src/test/compile-fail/rfc1445/match-forbidden-without-eq.rs @@ -28,7 +28,7 @@ fn main() { let x = 0.0; match x { f32::INFINITY => { } - //~^ WARNING floating-point cannot be used in patterns + //~^ WARNING floating-point types cannot be used in patterns //~| WARNING will become a hard error in a future release _ => { } } diff --git a/src/test/compile-fail/const-index-feature-gate.rs b/src/test/run-pass/const-index-feature-gate.rs similarity index 83% rename from src/test/compile-fail/const-index-feature-gate.rs rename to src/test/run-pass/const-index-feature-gate.rs index 4f92770df289c..2e60634d15eed 100644 --- a/src/test/compile-fail/const-index-feature-gate.rs +++ b/src/test/run-pass/const-index-feature-gate.rs @@ -9,8 +9,7 @@ // except according to those terms. const ARR: [usize; 1] = [2]; -const ARR2: [i32; ARR[0]] = [5, 6]; //~ ERROR E0080 - //~| unstable +const ARR2: [i32; ARR[0]] = [5, 6]; fn main() { } diff --git a/src/test/run-pass/float-int-invalid-const-cast.rs b/src/test/run-pass/float-int-invalid-const-cast.rs new file mode 100644 index 0000000000000..80ab12482cbb7 --- /dev/null +++ b/src/test/run-pass/float-int-invalid-const-cast.rs @@ -0,0 +1,61 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(i128_type)] +#![deny(const_err)] + +use std::{f32, f64}; + +// Forces evaluation of constants, triggering hard error +fn force(_: T) {} + +fn main() { + { const X: u16 = -1. as u16; force(X); } + { const X: u128 = -100. as u128; force(X); } + + { const X: i8 = f32::NAN as i8; force(X); } + { const X: i32 = f32::NAN as i32; force(X); } + { const X: u64 = f32::NAN as u64; force(X); } + { const X: u128 = f32::NAN as u128; force(X); } + + { const X: i8 = f32::INFINITY as i8; force(X); } + { const X: u32 = f32::INFINITY as u32; force(X); } + { const X: i128 = f32::INFINITY as i128; force(X); } + { const X: u128 = f32::INFINITY as u128; force(X); } + + { const X: u8 = f32::NEG_INFINITY as u8; force(X); } + { const X: u16 = f32::NEG_INFINITY as u16; force(X); } + { const X: i64 = f32::NEG_INFINITY as i64; force(X); } + { const X: i128 = f32::NEG_INFINITY as i128; force(X); } + + { const X: i8 = f64::NAN as i8; force(X); } + { const X: i32 = f64::NAN as i32; force(X); } + { const X: u64 = f64::NAN as u64; force(X); } + { const X: u128 = f64::NAN as u128; force(X); } + + { const X: i8 = f64::INFINITY as i8; force(X); } + { const X: u32 = f64::INFINITY as u32; force(X); } + { const X: i128 = f64::INFINITY as i128; force(X); } + { const X: u128 = f64::INFINITY as u128; force(X); } + + { const X: u8 = f64::NEG_INFINITY as u8; force(X); } + { const X: u16 = f64::NEG_INFINITY as u16; force(X); } + { const X: i64 = f64::NEG_INFINITY as i64; force(X); } + { const X: i128 = f64::NEG_INFINITY as i128; force(X); } + + { const X: u8 = 256. as u8; force(X); } + { const X: i8 = -129. as i8; force(X); } + { const X: i8 = 128. as i8; force(X); } + { const X: i32 = 2147483648. as i32; force(X); } + { const X: i32 = -2147483904. as i32; force(X); } + { const X: u32 = 4294967296. as u32; force(X); } + { const X: u128 = 1e40 as u128; force(X); } + { const X: i128 = 1e40 as i128; force(X); } +} diff --git a/src/test/ui/const-eval/issue-43197.rs b/src/test/ui/const-eval/issue-43197.rs index 86c5e873df866..e87b2caa49f90 100644 --- a/src/test/ui/const-eval/issue-43197.rs +++ b/src/test/ui/const-eval/issue-43197.rs @@ -18,4 +18,6 @@ fn main() { const X: u32 = 0-1; //~ ERROR constant evaluation error const Y: u32 = foo(0-1); //~ ERROR constant evaluation error println!("{} {}", X, Y); + //~^ ERROR constant evaluation error + //~| ERROR constant evaluation error } diff --git a/src/test/ui/const-len-underflow-separate-spans.rs b/src/test/ui/const-len-underflow-separate-spans.rs index 7582d0efa812e..35e3ba9ce0945 100644 --- a/src/test/ui/const-len-underflow-separate-spans.rs +++ b/src/test/ui/const-len-underflow-separate-spans.rs @@ -15,6 +15,7 @@ const ONE: usize = 1; const TWO: usize = 2; const LEN: usize = ONE - TWO; +//~^ ERROR E0080 fn main() { let a: [i8; LEN] = unimplemented!(); diff --git a/src/test/ui/error-codes/E0030.rs b/src/test/ui/error-codes/E0030.rs index ef3bded4beffb..896dd2599bf53 100644 --- a/src/test/ui/error-codes/E0030.rs +++ b/src/test/ui/error-codes/E0030.rs @@ -10,7 +10,7 @@ fn main() { - match 5u32 { + match 5u32 { //~ ERROR non-exhaustive patterns 1000 ... 5 => {} //~^ ERROR lower range bound must be less than or equal to upper } diff --git a/src/test/ui/error-codes/E0080.rs b/src/test/ui/error-codes/E0080.rs index 2f199c48e46e7..6e525d4ea9ec7 100644 --- a/src/test/ui/error-codes/E0080.rs +++ b/src/test/ui/error-codes/E0080.rs @@ -10,9 +10,10 @@ enum Enum { X = (1 << 500), //~ ERROR E0080 - //~| WARNING shift left with overflow + //~| shift left with overflow Y = (1 / 0) //~ ERROR E0080 - //~| WARNING divide by zero + //~| const_err + //~| divide by zero } fn main() { From 246d05b6247429df433b3f01491b90b6e47a6ad8 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Sun, 14 Jan 2018 13:04:02 +0100 Subject: [PATCH 010/110] Evaluate 128 lowering lang items manually --- src/librustc_mir/const_eval/pattern.rs | 2 +- src/librustc_mir/interpret/const_eval.rs | 28 ++++++++++++++++++------ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/librustc_mir/const_eval/pattern.rs b/src/librustc_mir/const_eval/pattern.rs index 4ecf0d6e638e7..c1618d8e8a959 100644 --- a/src/librustc_mir/const_eval/pattern.rs +++ b/src/librustc_mir/const_eval/pattern.rs @@ -836,7 +836,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } }, ty::TyAdt(adt_def, _) => { - let struct_var = adt_def.struct_variant(); + let struct_var = adt_def.non_enum_variant(); PatternKind::Leaf { subpatterns: struct_var.fields.iter().enumerate().map(|(i, _)| { let field = Field::new(i); diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index 2e2246f9ab927..1b4cbecab3b06 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -205,15 +205,29 @@ impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator { ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, instance: ty::Instance<'tcx>, destination: Option<(Place, mir::BasicBlock)>, - _args: &[ValTy<'tcx>], + args: &[ValTy<'tcx>], span: Span, - _sig: ty::FnSig<'tcx>, + sig: ty::FnSig<'tcx>, ) -> EvalResult<'tcx, bool> { debug!("eval_fn_call: {:?}", instance); if !ecx.tcx.is_const_fn(instance.def_id()) { - return Err( - ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into(), - ); + let def_id = instance.def_id(); + let (op, oflo) = if let Some(op) = ecx.tcx.is_binop_lang_item(def_id) { + op + } else { + return Err( + ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into(), + ); + }; + let (dest, bb) = destination.expect("128 lowerings can't diverge"); + let dest_ty = sig.output(); + if oflo { + ecx.intrinsic_with_overflow(op, args[0], args[1], dest, dest_ty)?; + } else { + ecx.intrinsic_overflowing(op, args[0], args[1], dest, dest_ty)?; + } + ecx.goto_block(bb); + return Ok(true); } let mir = match ecx.load_mir(instance.def) { Ok(mir) => mir, @@ -472,8 +486,8 @@ pub fn const_eval_provider<'a, 'tcx>( ecx.report(&mut err, true); } ConstEvalErr { - kind: err.into(), - span, + kind: err.into(), + span, } }) } From f73d4ac9e093801eec92096684061ce7e189ca79 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Sun, 14 Jan 2018 20:29:57 +0100 Subject: [PATCH 011/110] Implement on disk cache for AllocId --- src/librustc/ty/maps/on_disk_cache.rs | 81 +++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 4 deletions(-) diff --git a/src/librustc/ty/maps/on_disk_cache.rs b/src/librustc/ty/maps/on_disk_cache.rs index 77e022fe73003..7f126976a2ec1 100644 --- a/src/librustc/ty/maps/on_disk_cache.rs +++ b/src/librustc/ty/maps/on_disk_cache.rs @@ -187,6 +187,7 @@ impl<'sess> OnDiskCache<'sess> { type_shorthands: FxHashMap(), predicate_shorthands: FxHashMap(), expn_info_shorthands: FxHashMap(), + interpret_alloc_shorthands: FxHashMap(), codemap: CachingCodemapView::new(tcx.sess.codemap()), file_to_file_index, }; @@ -361,7 +362,7 @@ impl<'sess> OnDiskCache<'sess> { cnum_map: cnum_map.as_ref().unwrap(), file_index_to_file: &self.file_index_to_file, file_index_to_stable_id: &self.file_index_to_stable_id, - synthetic_expansion_infos: &self.synthetic_expansion_infos, + interpret_alloc_cache: FxHashMap::default(), }; match decode_tagged(&mut decoder, dep_node_index) { @@ -423,6 +424,7 @@ struct CacheDecoder<'a, 'tcx: 'a, 'x> { synthetic_expansion_infos: &'x RefCell>, file_index_to_file: &'x RefCell>>, file_index_to_stable_id: &'x FxHashMap, + interpret_alloc_cache: FxHashMap, } impl<'a, 'tcx, 'x> CacheDecoder<'a, 'tcx, 'x> { @@ -542,13 +544,51 @@ impl<'a, 'tcx: 'a, 'x> ty_codec::TyDecoder<'a, 'tcx> for CacheDecoder<'a, 'tcx, implement_ty_decoder!( CacheDecoder<'a, 'tcx, 'x> ); - impl<'a, 'tcx, 'x> SpecializedDecoder for CacheDecoder<'a, 'tcx, 'x> { fn specialized_decode(&mut self) -> Result { - unimplemented!() + const MAX1: usize = usize::max_value() - 1; + let tcx = self.tcx; + let interpret_interner = || tcx.interpret_interner.borrow_mut(); + let pos = TyDecoder::position(self); + match usize::decode(self)? { + ::std::usize::MAX => { + 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.intern_const_alloc(allocation); + interpret_interner().intern_at_reserved(alloc_id, allocation); + + if let Some(glob) = Option::::decode(self)? { + interpret_interner().cache(glob, alloc_id); + } + + Ok(alloc_id) + }, + MAX1 => { + trace!("creating fn alloc id at {}", pos); + let instance = ty::Instance::decode(self)?; + trace!("decoded fn alloc instance: {:?}", instance); + let id = interpret_interner().create_fn_alloc(instance); + trace!("created fn alloc id: {:?}", id); + self.interpret_alloc_cache.insert(pos, id); + Ok(id) + }, + shorthand => { + trace!("loading shorthand {}", shorthand); + if let Some(&alloc_id) = self.interpret_alloc_cache.get(&shorthand) { + return Ok(alloc_id); + } + trace!("shorthand {} not cached, loading entire allocation", shorthand); + // need to load allocation + self.with_position(shorthand, |this| interpret::AllocId::decode(this)) + }, + } } } - impl<'a, 'tcx, 'x> SpecializedDecoder for CacheDecoder<'a, 'tcx, 'x> { fn specialized_decode(&mut self) -> Result { let tag: u8 = Decodable::decode(self)?; @@ -710,6 +750,7 @@ struct CacheEncoder<'enc, 'a, 'tcx, E> type_shorthands: FxHashMap, usize>, predicate_shorthands: FxHashMap, usize>, expn_info_shorthands: FxHashMap, + interpret_alloc_shorthands: FxHashMap, codemap: CachingCodemapView<'tcx>, file_to_file_index: FxHashMap<*const FileMap, FileMapIndex>, } @@ -742,6 +783,38 @@ impl<'enc, 'a, 'tcx, E> CacheEncoder<'enc, 'a, 'tcx, E> } } +impl<'enc, 'a, 'tcx, E> SpecializedEncoder for CacheEncoder<'enc, 'a, 'tcx, E> + where E: 'enc + ty_codec::TyEncoder +{ + fn specialized_encode(&mut self, alloc_id: &interpret::AllocId) -> Result<(), Self::Error> { + trace!("encoding {:?} at {}", alloc_id, self.position()); + if let Some(shorthand) = self.interpret_alloc_shorthands.get(alloc_id).cloned() { + trace!("encoding {:?} as shorthand to {}", alloc_id, shorthand); + return shorthand.encode(self); + } + let start = self.position(); + // cache the allocation shorthand now, because the allocation itself might recursively + // 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) { + trace!("encoding {:?} with {:#?}", alloc_id, alloc); + usize::max_value().encode(self)?; + alloc.encode(self)?; + interpret_interner + .get_corresponding_static_def_id(*alloc_id) + .encode(self)?; + } else if let Some(fn_instance) = interpret_interner.get_fn(*alloc_id) { + trace!("encoding {:?} with {:#?}", alloc_id, fn_instance); + (usize::max_value() - 1).encode(self)?; + fn_instance.encode(self)?; + } else { + bug!("alloc id without corresponding allocation: {}", alloc_id); + } + Ok(()) + } +} + impl<'enc, 'a, 'tcx, E> SpecializedEncoder for CacheEncoder<'enc, 'a, 'tcx, E> where E: 'enc + ty_codec::TyEncoder { From 0595ff1009be0b4838b8efd0225c9601eff694eb Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 15 Jan 2018 10:31:28 +0100 Subject: [PATCH 012/110] Codegen tests --- src/test/codegen/consts.rs | 10 +++++----- src/test/codegen/link_section.rs | 2 +- src/test/codegen/remap_path_prefix/main.rs | 2 +- src/test/compile-fail/issue-43105.rs | 1 + 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/test/codegen/consts.rs b/src/test/codegen/consts.rs index a75b8f3992d07..13c037e96cbe3 100644 --- a/src/test/codegen/consts.rs +++ b/src/test/codegen/consts.rs @@ -9,6 +9,7 @@ // except according to those terms. // compile-flags: -C no-prepopulate-passes +// ignore-tidy-linelength #![crate_type = "lib"] @@ -19,12 +20,11 @@ // CHECK: @STATIC = {{.*}}, align 4 // This checks the constants from inline_enum_const -// CHECK: @ref.{{[0-9]+}} = {{.*}}, align 2 +// CHECK: @byte_str.{{[0-9]+}} = {{.*}}, align 2 // This checks the constants from {low,high}_align_const, they share the same // constant, but the alignment differs, so the higher one should be used -// CHECK: [[LOW_HIGH:@ref.[0-9]+]] = {{.*}}, align 4 -// CHECK: [[LOW_HIGH_REF:@const.[0-9]+]] = {{.*}} [[LOW_HIGH]] +// CHECK: [[LOW_HIGH:@byte_str.[0-9]+]] = {{.*}}, align 4 #[derive(Copy, Clone)] @@ -54,7 +54,7 @@ pub fn inline_enum_const() -> E { #[no_mangle] pub fn low_align_const() -> E { // Check that low_align_const and high_align_const use the same constant -// CHECK: load {{.*}} bitcast ({ i16, [0 x i8], i16, [4 x i8] }** [[LOW_HIGH_REF]] +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %1, i8* getelementptr inbounds (<{ [8 x i8] }>, <{ [8 x i8] }>* [[LOW_HIGH]], i32 0, i32 0, i32 0), i64 8, i32 2, i1 false) *&E::A(0) } @@ -62,6 +62,6 @@ pub fn low_align_const() -> E { #[no_mangle] pub fn high_align_const() -> E { // Check that low_align_const and high_align_const use the same constant -// CHECK: load {{.*}} bitcast ({ i16, [0 x i8], i16, [4 x i8] }** [[LOW_HIGH_REF]] +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %1, i8* getelementptr inbounds (<{ [8 x i8] }>, <{ [8 x i8] }>* [[LOW_HIGH]], i32 0, i32 0, i32 0), i64 8, i32 4, i1 false) *&E::A(0) } diff --git a/src/test/codegen/link_section.rs b/src/test/codegen/link_section.rs index 1879002e7f3d7..9c56a316b341c 100644 --- a/src/test/codegen/link_section.rs +++ b/src/test/codegen/link_section.rs @@ -12,7 +12,7 @@ #![crate_type = "lib"] -// CHECK: @VAR1 = constant i32 1, section ".test_one" +// CHECK: @VAR1 = constant <{ [4 x i8] }> <{ [4 x i8] c"\01\00\00\00" }>, section ".test_one" #[no_mangle] #[link_section = ".test_one"] pub static VAR1: u32 = 1; diff --git a/src/test/codegen/remap_path_prefix/main.rs b/src/test/codegen/remap_path_prefix/main.rs index 2f46b6c5d48da..4fb8c37558d48 100644 --- a/src/test/codegen/remap_path_prefix/main.rs +++ b/src/test/codegen/remap_path_prefix/main.rs @@ -22,7 +22,7 @@ mod aux_mod; include!("aux_mod.rs"); // Here we check that the expansion of the file!() macro is mapped. -// CHECK: internal constant [34 x i8] c"/the/src/remap_path_prefix/main.rs" +// CHECK: @byte_str.1 = private unnamed_addr constant <{ [34 x i8] }> <{ [34 x i8] c"/the/src/remap_path_prefix/main.rs" }>, align 1 pub static FILE_PATH: &'static str = file!(); fn main() { diff --git a/src/test/compile-fail/issue-43105.rs b/src/test/compile-fail/issue-43105.rs index fb419d751b4e8..c0af3b4b9e695 100644 --- a/src/test/compile-fail/issue-43105.rs +++ b/src/test/compile-fail/issue-43105.rs @@ -12,6 +12,7 @@ fn xyz() -> u8 { 42 } const NUM: u8 = xyz(); //~^ ERROR calls in constants are limited to constant functions, struct and enum constructors +//~| ERROR constant evaluation error fn main() { match 1 { From e0045ab8915c85e35dd04e9f9d00d6d011237bfa Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 16 Jan 2018 10:15:41 +0100 Subject: [PATCH 013/110] Add tests for fixed issues --- .../ctfe/chained-constants-stackoverflow.rs | 364 ++++++++++++++++++ src/test/run-pass/ctfe/deref_in_pattern.rs | 20 + src/test/run-pass/ctfe/issue-broken-mir.rs | 18 + .../run-pass/ctfe/match-const-fn-structs.rs | 31 ++ src/test/run-pass/ctfe/non-scalar-cast.rs | 17 + src/test/run-pass/ctfe/return-in-const-fn.rs | 19 + .../ctfe/tuple-struct-constructors.rs | 20 + src/test/ui/infinite-recursion-const-fn.rs | 18 + .../ui/infinite-recursion-const-fn.stderr | 8 + 9 files changed, 515 insertions(+) create mode 100644 src/test/run-pass/ctfe/chained-constants-stackoverflow.rs create mode 100644 src/test/run-pass/ctfe/deref_in_pattern.rs create mode 100644 src/test/run-pass/ctfe/issue-broken-mir.rs create mode 100644 src/test/run-pass/ctfe/match-const-fn-structs.rs create mode 100644 src/test/run-pass/ctfe/non-scalar-cast.rs create mode 100644 src/test/run-pass/ctfe/return-in-const-fn.rs create mode 100644 src/test/run-pass/ctfe/tuple-struct-constructors.rs create mode 100644 src/test/ui/infinite-recursion-const-fn.rs create mode 100644 src/test/ui/infinite-recursion-const-fn.stderr diff --git a/src/test/run-pass/ctfe/chained-constants-stackoverflow.rs b/src/test/run-pass/ctfe/chained-constants-stackoverflow.rs new file mode 100644 index 0000000000000..813dd5fbb9941 --- /dev/null +++ b/src/test/run-pass/ctfe/chained-constants-stackoverflow.rs @@ -0,0 +1,364 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// https://github.com/rust-lang/rust/issues/34997 + +pub const CST_1: u32 = 0; +pub const CST_2: u32 = CST_1+1; +pub const CST_3: u32 = CST_2+1; +pub const CST_4: u32 = CST_3+1; +pub const CST_5: u32 = CST_4+1; +pub const CST_6: u32 = CST_5+1; +pub const CST_7: u32 = CST_6+1; +pub const CST_8: u32 = CST_7+1; +pub const CST_9: u32 = CST_8+1; +pub const CST_10: u32 = CST_9+1; +pub const CST_11: u32 = CST_10+1; +pub const CST_12: u32 = CST_11+1; +pub const CST_13: u32 = CST_12+1; +pub const CST_14: u32 = CST_13+1; +pub const CST_15: u32 = CST_14+1; +pub const CST_16: u32 = CST_15+1; +pub const CST_17: u32 = CST_16+1; +pub const CST_18: u32 = CST_17+1; +pub const CST_19: u32 = CST_18+1; +pub const CST_20: u32 = CST_19+1; +pub const CST_21: u32 = CST_20+1; +pub const CST_22: u32 = CST_21+1; +pub const CST_23: u32 = CST_22+1; +pub const CST_24: u32 = CST_23+1; +pub const CST_25: u32 = CST_24+1; +pub const CST_26: u32 = CST_25+1; +pub const CST_27: u32 = CST_26+1; +pub const CST_28: u32 = CST_27+1; +pub const CST_29: u32 = CST_28+1; +pub const CST_30: u32 = CST_29+1; +pub const CST_31: u32 = CST_30+1; +pub const CST_32: u32 = CST_31+1; +pub const CST_33: u32 = CST_32+1; +pub const CST_34: u32 = CST_33+1; +pub const CST_35: u32 = CST_34+1; +pub const CST_36: u32 = CST_35+1; +pub const CST_37: u32 = CST_36+1; +pub const CST_38: u32 = CST_37+1; +pub const CST_39: u32 = CST_38+1; +pub const CST_40: u32 = CST_39+1; +pub const CST_41: u32 = CST_40+1; +pub const CST_42: u32 = CST_41+1; +pub const CST_43: u32 = CST_42+1; +pub const CST_44: u32 = CST_43+1; +pub const CST_45: u32 = CST_44+1; +pub const CST_46: u32 = CST_45+1; +pub const CST_47: u32 = CST_46+1; +pub const CST_48: u32 = CST_47+1; +pub const CST_49: u32 = CST_48+1; +pub const CST_50: u32 = CST_49+1; +pub const CST_51: u32 = CST_50+1; +pub const CST_52: u32 = CST_51+1; +pub const CST_53: u32 = CST_52+1; +pub const CST_54: u32 = CST_53+1; +pub const CST_55: u32 = CST_54+1; +pub const CST_56: u32 = CST_55+1; +pub const CST_57: u32 = CST_56+1; +pub const CST_58: u32 = CST_57+1; +pub const CST_59: u32 = CST_58+1; +pub const CST_60: u32 = CST_59+1; +pub const CST_61: u32 = CST_60+1; +pub const CST_62: u32 = CST_61+1; +pub const CST_63: u32 = CST_62+1; +pub const CST_64: u32 = CST_63+1; +pub const CST_65: u32 = CST_64+1; +pub const CST_66: u32 = CST_65+1; +pub const CST_67: u32 = CST_66+1; +pub const CST_68: u32 = CST_67+1; +pub const CST_69: u32 = CST_68+1; +pub const CST_70: u32 = CST_69+1; +pub const CST_71: u32 = CST_70+1; +pub const CST_72: u32 = CST_71+1; +pub const CST_73: u32 = CST_72+1; +pub const CST_74: u32 = CST_73+1; +pub const CST_75: u32 = CST_74+1; +pub const CST_76: u32 = CST_75+1; +pub const CST_77: u32 = CST_76+1; +pub const CST_78: u32 = CST_77+1; +pub const CST_79: u32 = CST_78+1; +pub const CST_80: u32 = CST_79+1; +pub const CST_81: u32 = CST_80+1; +pub const CST_82: u32 = CST_81+1; +pub const CST_83: u32 = CST_82+1; +pub const CST_84: u32 = CST_83+1; +pub const CST_85: u32 = CST_84+1; +pub const CST_86: u32 = CST_85+1; +pub const CST_87: u32 = CST_86+1; +pub const CST_88: u32 = CST_87+1; +pub const CST_89: u32 = CST_88+1; +pub const CST_90: u32 = CST_89+1; +pub const CST_91: u32 = CST_90+1; +pub const CST_92: u32 = CST_91+1; +pub const CST_93: u32 = CST_92+1; +pub const CST_94: u32 = CST_93+1; +pub const CST_95: u32 = CST_94+1; +pub const CST_96: u32 = CST_95+1; +pub const CST_97: u32 = CST_96+1; +pub const CST_98: u32 = CST_97+1; +pub const CST_99: u32 = CST_98+1; +pub const CST_100: u32 = CST_99+1; +pub const CST_101: u32 = CST_100+1; +pub const CST_102: u32 = CST_101+1; +pub const CST_103: u32 = CST_102+1; +pub const CST_104: u32 = CST_103+1; +pub const CST_105: u32 = CST_104+1; +pub const CST_106: u32 = CST_105+1; +pub const CST_107: u32 = CST_106+1; +pub const CST_108: u32 = CST_107+1; +pub const CST_109: u32 = CST_108+1; +pub const CST_110: u32 = CST_109+1; +pub const CST_111: u32 = CST_110+1; +pub const CST_112: u32 = CST_111+1; +pub const CST_113: u32 = CST_112+1; +pub const CST_114: u32 = CST_113+1; +pub const CST_115: u32 = CST_114+1; +pub const CST_116: u32 = CST_115+1; +pub const CST_117: u32 = CST_116+1; +pub const CST_118: u32 = CST_117+1; +pub const CST_119: u32 = CST_118+1; +pub const CST_120: u32 = CST_119+1; +pub const CST_121: u32 = CST_120+1; +pub const CST_122: u32 = CST_121+1; +pub const CST_123: u32 = CST_122+1; +pub const CST_124: u32 = CST_123+1; +pub const CST_125: u32 = CST_124+1; +pub const CST_126: u32 = CST_125+1; +pub const CST_127: u32 = CST_126+1; +pub const CST_128: u32 = CST_127+1; +pub const CST_129: u32 = CST_128+1; +pub const CST_130: u32 = CST_129+1; +pub const CST_131: u32 = CST_130+1; +pub const CST_132: u32 = CST_131+1; +pub const CST_133: u32 = CST_132+1; +pub const CST_134: u32 = CST_133+1; +pub const CST_135: u32 = CST_134+1; +pub const CST_136: u32 = CST_135+1; +pub const CST_137: u32 = CST_136+1; +pub const CST_138: u32 = CST_137+1; +pub const CST_139: u32 = CST_138+1; +pub const CST_140: u32 = CST_139+1; +pub const CST_141: u32 = CST_140+1; +pub const CST_142: u32 = CST_141+1; +pub const CST_143: u32 = CST_142+1; +pub const CST_144: u32 = CST_143+1; +pub const CST_145: u32 = CST_144+1; +pub const CST_146: u32 = CST_145+1; +pub const CST_147: u32 = CST_146+1; +pub const CST_148: u32 = CST_147+1; +pub const CST_149: u32 = CST_148+1; +pub const CST_150: u32 = CST_149+1; +pub const CST_151: u32 = CST_150+1; +pub const CST_152: u32 = CST_151+1; +pub const CST_153: u32 = CST_152+1; +pub const CST_154: u32 = CST_153+1; +pub const CST_155: u32 = CST_154+1; +pub const CST_156: u32 = CST_155+1; +pub const CST_157: u32 = CST_156+1; +pub const CST_158: u32 = CST_157+1; +pub const CST_159: u32 = CST_158+1; +pub const CST_160: u32 = CST_159+1; +pub const CST_161: u32 = CST_160+1; +pub const CST_162: u32 = CST_161+1; +pub const CST_163: u32 = CST_162+1; +pub const CST_164: u32 = CST_163+1; +pub const CST_165: u32 = CST_164+1; +pub const CST_166: u32 = CST_165+1; +pub const CST_167: u32 = CST_166+1; +pub const CST_168: u32 = CST_167+1; +pub const CST_169: u32 = CST_168+1; +pub const CST_170: u32 = CST_169+1; +pub const CST_171: u32 = CST_170+1; +pub const CST_172: u32 = CST_171+1; +pub const CST_173: u32 = CST_172+1; +pub const CST_174: u32 = CST_173+1; +pub const CST_175: u32 = CST_174+1; +pub const CST_176: u32 = CST_175+1; +pub const CST_177: u32 = CST_176+1; +pub const CST_178: u32 = CST_177+1; +pub const CST_179: u32 = CST_178+1; +pub const CST_180: u32 = CST_179+1; +pub const CST_181: u32 = CST_180+1; +pub const CST_182: u32 = CST_181+1; +pub const CST_183: u32 = CST_182+1; +pub const CST_184: u32 = CST_183+1; +pub const CST_185: u32 = CST_184+1; +pub const CST_186: u32 = CST_185+1; +pub const CST_187: u32 = CST_186+1; +pub const CST_188: u32 = CST_187+1; +pub const CST_189: u32 = CST_188+1; +pub const CST_190: u32 = CST_189+1; +pub const CST_191: u32 = CST_190+1; +pub const CST_192: u32 = CST_191+1; +pub const CST_193: u32 = CST_192+1; +pub const CST_194: u32 = CST_193+1; +pub const CST_195: u32 = CST_194+1; +pub const CST_196: u32 = CST_195+1; +pub const CST_197: u32 = CST_196+1; +pub const CST_198: u32 = CST_197+1; +pub const CST_199: u32 = CST_198+1; +pub const CST_200: u32 = CST_199+1; +pub const CST_201: u32 = CST_200+1; +pub const CST_202: u32 = CST_201+1; +pub const CST_203: u32 = CST_202+1; +pub const CST_204: u32 = CST_203+1; +pub const CST_205: u32 = CST_204+1; +pub const CST_206: u32 = CST_205+1; +pub const CST_207: u32 = CST_206+1; +pub const CST_208: u32 = CST_207+1; +pub const CST_209: u32 = CST_208+1; +pub const CST_210: u32 = CST_209+1; +pub const CST_211: u32 = CST_210+1; +pub const CST_212: u32 = CST_211+1; +pub const CST_213: u32 = CST_212+1; +pub const CST_214: u32 = CST_213+1; +pub const CST_215: u32 = CST_214+1; +pub const CST_216: u32 = CST_215+1; +pub const CST_217: u32 = CST_216+1; +pub const CST_218: u32 = CST_217+1; +pub const CST_219: u32 = CST_218+1; +pub const CST_220: u32 = CST_219+1; +pub const CST_221: u32 = CST_220+1; +pub const CST_222: u32 = CST_221+1; +pub const CST_223: u32 = CST_222+1; +pub const CST_224: u32 = CST_223+1; +pub const CST_225: u32 = CST_224+1; +pub const CST_226: u32 = CST_225+1; +pub const CST_227: u32 = CST_226+1; +pub const CST_228: u32 = CST_227+1; +pub const CST_229: u32 = CST_228+1; +pub const CST_230: u32 = CST_229+1; +pub const CST_231: u32 = CST_230+1; +pub const CST_232: u32 = CST_231+1; +pub const CST_233: u32 = CST_232+1; +pub const CST_234: u32 = CST_233+1; +pub const CST_235: u32 = CST_234+1; +pub const CST_236: u32 = CST_235+1; +pub const CST_237: u32 = CST_236+1; +pub const CST_238: u32 = CST_237+1; +pub const CST_239: u32 = CST_238+1; +pub const CST_240: u32 = CST_239+1; +pub const CST_241: u32 = CST_240+1; +pub const CST_242: u32 = CST_241+1; +pub const CST_243: u32 = CST_242+1; +pub const CST_244: u32 = CST_243+1; +pub const CST_245: u32 = CST_244+1; +pub const CST_246: u32 = CST_245+1; +pub const CST_247: u32 = CST_246+1; +pub const CST_248: u32 = CST_247+1; +pub const CST_249: u32 = CST_248+1; +pub const CST_250: u32 = CST_249+1; +pub const CST_251: u32 = CST_250+1; +pub const CST_252: u32 = CST_251+1; +pub const CST_253: u32 = CST_252+1; +pub const CST_254: u32 = CST_253+1; +pub const CST_255: u32 = CST_254+1; +pub const CST_256: u32 = CST_255+1; +pub const CST_257: u32 = CST_256+1; +pub const CST_258: u32 = CST_257+1; +pub const CST_259: u32 = CST_258+1; +pub const CST_260: u32 = CST_259+1; +pub const CST_261: u32 = CST_260+1; +pub const CST_262: u32 = CST_261+1; +pub const CST_263: u32 = CST_262+1; +pub const CST_264: u32 = CST_263+1; +pub const CST_265: u32 = CST_264+1; +pub const CST_266: u32 = CST_265+1; +pub const CST_267: u32 = CST_266+1; +pub const CST_268: u32 = CST_267+1; +pub const CST_269: u32 = CST_268+1; +pub const CST_270: u32 = CST_269+1; +pub const CST_271: u32 = CST_270+1; +pub const CST_272: u32 = CST_271+1; +pub const CST_273: u32 = CST_272+1; +pub const CST_274: u32 = CST_273+1; +pub const CST_275: u32 = CST_274+1; +pub const CST_276: u32 = CST_275+1; +pub const CST_277: u32 = CST_276+1; +pub const CST_278: u32 = CST_277+1; +pub const CST_279: u32 = CST_278+1; +pub const CST_280: u32 = CST_279+1; +pub const CST_281: u32 = CST_280+1; +pub const CST_282: u32 = CST_281+1; +pub const CST_283: u32 = CST_282+1; +pub const CST_284: u32 = CST_283+1; +pub const CST_285: u32 = CST_284+1; +pub const CST_286: u32 = CST_285+1; +pub const CST_287: u32 = CST_286+1; +pub const CST_288: u32 = CST_287+1; +pub const CST_289: u32 = CST_288+1; +pub const CST_290: u32 = CST_289+1; +pub const CST_291: u32 = CST_290+1; +pub const CST_292: u32 = CST_291+1; +pub const CST_293: u32 = CST_292+1; +pub const CST_294: u32 = CST_293+1; +pub const CST_295: u32 = CST_294+1; +pub const CST_296: u32 = CST_295+1; +pub const CST_297: u32 = CST_296+1; +pub const CST_298: u32 = CST_297+1; +pub const CST_299: u32 = CST_298+1; +pub const CST_300: u32 = CST_299+1; +pub const CST_301: u32 = CST_300+1; +pub const CST_302: u32 = CST_301+1; +pub const CST_303: u32 = CST_302+1; +pub const CST_304: u32 = CST_303+1; +pub const CST_305: u32 = CST_304+1; +pub const CST_306: u32 = CST_305+1; +pub const CST_307: u32 = CST_306+1; +pub const CST_308: u32 = CST_307+1; +pub const CST_309: u32 = CST_308+1; +pub const CST_310: u32 = CST_309+1; +pub const CST_311: u32 = CST_310+1; +pub const CST_312: u32 = CST_311+1; +pub const CST_313: u32 = CST_312+1; +pub const CST_314: u32 = CST_313+1; +pub const CST_315: u32 = CST_314+1; +pub const CST_316: u32 = CST_315+1; +pub const CST_317: u32 = CST_316+1; +pub const CST_318: u32 = CST_317+1; +pub const CST_319: u32 = CST_318+1; +pub const CST_320: u32 = CST_319+1; +pub const CST_321: u32 = CST_320+1; +pub const CST_322: u32 = CST_321+1; +pub const CST_323: u32 = CST_322+1; +pub const CST_324: u32 = CST_323+1; +pub const CST_325: u32 = CST_324+1; +pub const CST_326: u32 = CST_325+1; +pub const CST_327: u32 = CST_326+1; +pub const CST_328: u32 = CST_327+1; +pub const CST_329: u32 = CST_328+1; +pub const CST_330: u32 = CST_329+1; +pub const CST_331: u32 = CST_330+1; +pub const CST_332: u32 = CST_331+1; +pub const CST_333: u32 = CST_332+1; +pub const CST_334: u32 = CST_333+1; +pub const CST_335: u32 = CST_334+1; +pub const CST_336: u32 = CST_335+1; +pub const CST_337: u32 = CST_336+1; +pub const CST_338: u32 = CST_337+1; +pub const CST_339: u32 = CST_338+1; +pub const CST_340: u32 = CST_339+1; +pub const CST_341: u32 = CST_340+1; +pub const CST_342: u32 = CST_341+1; +pub const CST_343: u32 = CST_342+1; +pub const CST_344: u32 = CST_343+1; +pub const CST_345: u32 = CST_344+1; +pub const CST_346: u32 = CST_345+1; +pub const CST_347: u32 = CST_346+1; +pub const CST_348: u32 = CST_347+1; +pub const CST_349: u32 = CST_348+1; +pub const CST_350: u32 = CST_349+1; + +fn main() {} diff --git a/src/test/run-pass/ctfe/deref_in_pattern.rs b/src/test/run-pass/ctfe/deref_in_pattern.rs new file mode 100644 index 0000000000000..4ccfa0338f393 --- /dev/null +++ b/src/test/run-pass/ctfe/deref_in_pattern.rs @@ -0,0 +1,20 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// https://github.com/rust-lang/rust/issues/25574 + +const A: [u8; 4] = *b"fooo"; + +fn main() { + match *b"xxxx" { + A => {}, + _ => {} + } +} diff --git a/src/test/run-pass/ctfe/issue-broken-mir.rs b/src/test/run-pass/ctfe/issue-broken-mir.rs new file mode 100644 index 0000000000000..6ed0c7c0d5dc4 --- /dev/null +++ b/src/test/run-pass/ctfe/issue-broken-mir.rs @@ -0,0 +1,18 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// https://github.com/rust-lang/rust/issues/27918 + +fn main() { + match b" " { + b"1234" => {}, + _ => {}, + } +} diff --git a/src/test/run-pass/ctfe/match-const-fn-structs.rs b/src/test/run-pass/ctfe/match-const-fn-structs.rs new file mode 100644 index 0000000000000..0bb253d1a6455 --- /dev/null +++ b/src/test/run-pass/ctfe/match-const-fn-structs.rs @@ -0,0 +1,31 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// https://github.com/rust-lang/rust/issues/46114 + +#![feature(const_fn)] + +#[derive(Eq, PartialEq)] +struct A { value: u32 } + +const fn new(value: u32) -> A { + A { value } +} + +const A_1: A = new(1); +const A_2: A = new(2); + +fn main() { + let a_str = match new(42) { + A_1 => "A 1", + A_2 => "A 2", + _ => "Unknown A", + }; +} diff --git a/src/test/run-pass/ctfe/non-scalar-cast.rs b/src/test/run-pass/ctfe/non-scalar-cast.rs new file mode 100644 index 0000000000000..ff4474f47c9f3 --- /dev/null +++ b/src/test/run-pass/ctfe/non-scalar-cast.rs @@ -0,0 +1,17 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// https://github.com/rust-lang/rust/issues/37448 + +fn main() { + struct A; + const FOO: &A = &(A as A); + let _x = FOO; +} diff --git a/src/test/run-pass/ctfe/return-in-const-fn.rs b/src/test/run-pass/ctfe/return-in-const-fn.rs new file mode 100644 index 0000000000000..d57d3bcb49aa8 --- /dev/null +++ b/src/test/run-pass/ctfe/return-in-const-fn.rs @@ -0,0 +1,19 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// https://github.com/rust-lang/rust/issues/43754 + +#![feature(const_fn)] +const fn foo(x: usize) -> usize { + return x; +} +fn main() { + [0; foo(2)]; +} diff --git a/src/test/run-pass/ctfe/tuple-struct-constructors.rs b/src/test/run-pass/ctfe/tuple-struct-constructors.rs new file mode 100644 index 0000000000000..ecc5d37663679 --- /dev/null +++ b/src/test/run-pass/ctfe/tuple-struct-constructors.rs @@ -0,0 +1,20 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// https://github.com/rust-lang/rust/issues/41898 + +#![feature(nonzero, const_fn)] +extern crate core; +use core::nonzero::NonZero; + +fn main() { + const FOO: NonZero = unsafe { NonZero::new_unchecked(2) }; + if let FOO = FOO {} +} diff --git a/src/test/ui/infinite-recursion-const-fn.rs b/src/test/ui/infinite-recursion-const-fn.rs new file mode 100644 index 0000000000000..05e40abdc0f2f --- /dev/null +++ b/src/test/ui/infinite-recursion-const-fn.rs @@ -0,0 +1,18 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//https://github.com/rust-lang/rust/issues/31364 + +#![feature(const_fn)] +const fn a() -> usize { b() } +const fn b() -> usize { a() } +const ARR: [i32; a()] = [5; 6]; //~ ERROR constant evaluation error + +fn main(){} diff --git a/src/test/ui/infinite-recursion-const-fn.stderr b/src/test/ui/infinite-recursion-const-fn.stderr new file mode 100644 index 0000000000000..ce1b35aac7ecc --- /dev/null +++ b/src/test/ui/infinite-recursion-const-fn.stderr @@ -0,0 +1,8 @@ +error[E0080]: constant evaluation error + --> $DIR/infinite-recursion-const-fn.rs:16:18 + | +16 | const ARR: [i32; a()] = [5; 6]; //~ ERROR constant evaluation error + | ^^^ miri failed: reached the configured maximum number of stack frames + +error: aborting due to previous error + From 7d2d4e320294d69f6fff3002200b7ea8809b95d1 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 16 Jan 2018 10:16:38 +0100 Subject: [PATCH 014/110] Add InterpretInterner to StableHashingContext for AllocId serialization --- src/librustc/dep_graph/dep_node.rs | 2 +- src/librustc/hir/map/collector.rs | 2 +- src/librustc/ich/hcx.rs | 64 +++--- src/librustc/ich/impls_hir.rs | 132 +++++------ src/librustc/ich/impls_mir.rs | 98 ++++----- src/librustc/ich/impls_syntax.rs | 48 ++-- src/librustc/ich/impls_ty.rs | 206 +++++++++++------- src/librustc/lint/levels.rs | 4 +- src/librustc/macros.rs | 16 +- src/librustc/middle/borrowck.rs | 4 +- src/librustc/middle/region.rs | 4 +- src/librustc/mir/cache.rs | 4 +- src/librustc/mir/interpret/mod.rs | 2 + src/librustc/mir/mod.rs | 2 +- src/librustc/mir/mono.rs | 8 +- src/librustc/session/config.rs | 4 +- .../traits/specialize/specialization_graph.rs | 4 +- src/librustc/ty/context.rs | 6 +- src/librustc/ty/fast_reject.rs | 6 +- src/librustc/ty/layout.rs | 20 +- src/librustc/ty/mod.rs | 16 +- src/librustc/ty/trait_def.rs | 4 +- src/librustc_metadata/schema.rs | 8 +- src/librustc_mir/build/matches/test.rs | 144 ++++++------ src/librustc_mir/diagnostics.rs | 30 --- src/librustc_mir/interpret/const_eval.rs | 9 +- src/librustc_mir/interpret/eval_context.rs | 14 +- src/librustc_mir/interpret/memory.rs | 6 +- src/librustc_mir/interpret/traits.rs | 2 +- src/librustc_mir/transform/instcombine.rs | 54 ++--- src/librustc_mir/transform/mod.rs | 1 + src/librustc_trans/mir/analyze.rs | 2 +- src/librustc_trans/mir/block.rs | 2 +- src/librustc_trans/mir/constant.rs | 100 ++++----- src/librustc_trans/mir/operand.rs | 24 +- src/librustdoc/clean/mod.rs | 53 +++-- src/test/compile-fail/const-err2.rs | 4 +- src/test/compile-fail/const-err3.rs | 3 +- src/test/run-pass/ctfe/references.rs | 34 +++ 39 files changed, 618 insertions(+), 528 deletions(-) create mode 100644 src/test/run-pass/ctfe/references.rs diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 6824c015eabcb..7d8709a82f4d3 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -661,7 +661,7 @@ trait DepNodeParams<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> : fmt::Debug { } impl<'a, 'gcx: 'tcx + 'a, 'tcx: 'a, T> DepNodeParams<'a, 'gcx, 'tcx> for T - where T: HashStable> + fmt::Debug + where T: HashStable> + fmt::Debug { default const CAN_RECONSTRUCT_QUERY_KEY: bool = false; diff --git a/src/librustc/hir/map/collector.rs b/src/librustc/hir/map/collector.rs index 9bbda9d74479b..3c523f5633e11 100644 --- a/src/librustc/hir/map/collector.rs +++ b/src/librustc/hir/map/collector.rs @@ -529,7 +529,7 @@ struct HirItemLike { hash_bodies: bool, } -impl<'hir, T> HashStable> for HirItemLike +impl<'a, 'hir, T> HashStable> for HirItemLike where T: HashStable> { fn hash_stable(&self, diff --git a/src/librustc/ich/hcx.rs b/src/librustc/ich/hcx.rs index 4dcab6a04ee20..6ae588b2a07b6 100644 --- a/src/librustc/ich/hcx.rs +++ b/src/librustc/ich/hcx.rs @@ -46,19 +46,19 @@ pub fn compute_ignored_attr_names() -> FxHashSet { /// a reference to the TyCtxt) and it holds a few caches for speeding up various /// things (e.g. each DefId/DefPath is only hashed once). #[derive(Clone)] -pub struct StableHashingContext<'gcx> { - sess: &'gcx Session, - definitions: &'gcx Definitions, - cstore: &'gcx dyn CrateStore, - body_resolver: BodyResolver<'gcx>, +pub struct StableHashingContext<'a> { + sess: &'a Session, + definitions: &'a Definitions, + cstore: &'a dyn CrateStore, + body_resolver: BodyResolver<'a>, hash_spans: bool, hash_bodies: bool, node_id_hashing_mode: NodeIdHashingMode, // Very often, we are hashing something that does not need the // CachingCodemapView, so we initialize it lazily. - raw_codemap: &'gcx CodeMap, - caching_codemap: Option>, + raw_codemap: &'a CodeMap, + caching_codemap: Option>, } #[derive(PartialEq, Eq, Clone, Copy)] @@ -81,14 +81,14 @@ impl<'gcx> BodyResolver<'gcx> { } } -impl<'gcx> StableHashingContext<'gcx> { +impl<'a> StableHashingContext<'a> { // The `krate` here is only used for mapping BodyIds to Bodies. // Don't use it for anything else or you'll run the risk of // leaking data out of the tracking system. - pub fn new(sess: &'gcx Session, - krate: &'gcx hir::Crate, - definitions: &'gcx Definitions, - cstore: &'gcx dyn CrateStore) + pub fn new(sess: &'a Session, + krate: &'a hir::Crate, + definitions: &'a Definitions, + cstore: &'a dyn CrateStore) -> Self { let hash_spans_initial = !sess.opts.debugging_opts.incremental_ignore_spans; @@ -106,7 +106,7 @@ impl<'gcx> StableHashingContext<'gcx> { } #[inline] - pub fn sess(&self) -> &'gcx Session { + pub fn sess(&self) -> &'a Session { self.sess } @@ -165,7 +165,7 @@ impl<'gcx> StableHashingContext<'gcx> { } #[inline] - pub fn codemap(&mut self) -> &mut CachingCodemapView<'gcx> { + pub fn codemap(&mut self) -> &mut CachingCodemapView<'a> { match self.caching_codemap { Some(ref mut cm) => { cm @@ -193,27 +193,27 @@ impl<'gcx> StableHashingContext<'gcx> { } impl<'a, 'gcx, 'lcx> StableHashingContextProvider for TyCtxt<'a, 'gcx, 'lcx> { - type ContextType = StableHashingContext<'gcx>; + type ContextType = StableHashingContext<'a>; fn create_stable_hashing_context(&self) -> Self::ContextType { (*self).create_stable_hashing_context() } } -impl<'gcx> StableHashingContextProvider for StableHashingContext<'gcx> { - type ContextType = StableHashingContext<'gcx>; +impl<'a> StableHashingContextProvider for StableHashingContext<'a> { + type ContextType = StableHashingContext<'a>; fn create_stable_hashing_context(&self) -> Self::ContextType { self.clone() } } -impl<'gcx> ::dep_graph::DepGraphSafe for StableHashingContext<'gcx> { +impl<'a> ::dep_graph::DepGraphSafe for StableHashingContext<'a> { } -impl<'gcx> HashStable> for hir::BodyId { +impl<'a> HashStable> for hir::BodyId { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { if hcx.hash_bodies() { hcx.body_resolver.body(*self).hash_stable(hcx, hasher); @@ -221,10 +221,10 @@ impl<'gcx> HashStable> for hir::BodyId { } } -impl<'gcx> HashStable> for hir::HirId { +impl<'a> HashStable> for hir::HirId { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { match hcx.node_id_hashing_mode { NodeIdHashingMode::Ignore => { @@ -243,21 +243,21 @@ impl<'gcx> HashStable> for hir::HirId { } } -impl<'gcx> ToStableHashKey> for hir::HirId { +impl<'a> ToStableHashKey> for hir::HirId { type KeyType = (DefPathHash, hir::ItemLocalId); #[inline] fn to_stable_hash_key(&self, - hcx: &StableHashingContext<'gcx>) + hcx: &StableHashingContext<'a>) -> (DefPathHash, hir::ItemLocalId) { let def_path_hash = hcx.local_def_path_hash(self.owner); (def_path_hash, self.local_id) } } -impl<'gcx> HashStable> for ast::NodeId { +impl<'a> HashStable> for ast::NodeId { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { match hcx.node_id_hashing_mode { NodeIdHashingMode::Ignore => { @@ -270,18 +270,18 @@ impl<'gcx> HashStable> for ast::NodeId { } } -impl<'gcx> ToStableHashKey> for ast::NodeId { +impl<'a> ToStableHashKey> for ast::NodeId { type KeyType = (DefPathHash, hir::ItemLocalId); #[inline] fn to_stable_hash_key(&self, - hcx: &StableHashingContext<'gcx>) + hcx: &StableHashingContext<'a>) -> (DefPathHash, hir::ItemLocalId) { hcx.definitions.node_to_hir_id(*self).to_stable_hash_key(hcx) } } -impl<'gcx> HashStable> for Span { +impl<'a> HashStable> for Span { // Hash a span in a stable way. We can't directly hash the span's BytePos // fields (that would be similar to hashing pointers, since those are just @@ -293,7 +293,7 @@ impl<'gcx> HashStable> for Span { // Also, hashing filenames is expensive so we avoid doing it twice when the // span starts and ends in the same file, which is almost always the case. fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { const TAG_VALID_SPAN: u8 = 0; const TAG_INVALID_SPAN: u8 = 1; @@ -373,8 +373,8 @@ impl<'gcx> HashStable> for Span { } } -pub fn hash_stable_trait_impls<'gcx, W, R>( - hcx: &mut StableHashingContext<'gcx>, +pub fn hash_stable_trait_impls<'a, 'gcx, W, R>( + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher, blanket_impls: &Vec, non_blanket_impls: &HashMap, R>) diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc/ich/impls_hir.rs index faad3f3563164..c085b803085a8 100644 --- a/src/librustc/ich/impls_hir.rs +++ b/src/librustc/ich/impls_hir.rs @@ -21,46 +21,46 @@ use std::mem; use syntax::ast; use syntax::attr; -impl<'gcx> HashStable> for DefId { +impl<'a> HashStable> for DefId { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.def_path_hash(*self).hash_stable(hcx, hasher); } } -impl<'gcx> ToStableHashKey> for DefId { +impl<'a> ToStableHashKey> for DefId { type KeyType = DefPathHash; #[inline] - fn to_stable_hash_key(&self, hcx: &StableHashingContext<'gcx>) -> DefPathHash { + fn to_stable_hash_key(&self, hcx: &StableHashingContext<'a>) -> DefPathHash { hcx.def_path_hash(*self) } } -impl<'gcx> HashStable> for LocalDefId { +impl<'a> HashStable> for LocalDefId { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.def_path_hash(self.to_def_id()).hash_stable(hcx, hasher); } } -impl<'gcx> ToStableHashKey> for LocalDefId { +impl<'a> ToStableHashKey> for LocalDefId { type KeyType = DefPathHash; #[inline] - fn to_stable_hash_key(&self, hcx: &StableHashingContext<'gcx>) -> DefPathHash { + fn to_stable_hash_key(&self, hcx: &StableHashingContext<'a>) -> DefPathHash { hcx.def_path_hash(self.to_def_id()) } } -impl<'gcx> HashStable> for CrateNum { +impl<'a> HashStable> for CrateNum { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.def_path_hash(DefId { krate: *self, @@ -69,11 +69,11 @@ impl<'gcx> HashStable> for CrateNum { } } -impl<'gcx> ToStableHashKey> for CrateNum { +impl<'a> ToStableHashKey> for CrateNum { type KeyType = DefPathHash; #[inline] - fn to_stable_hash_key(&self, hcx: &StableHashingContext<'gcx>) -> DefPathHash { + fn to_stable_hash_key(&self, hcx: &StableHashingContext<'a>) -> DefPathHash { let def_id = DefId { krate: *self, index: CRATE_DEF_INDEX }; def_id.to_stable_hash_key(hcx) } @@ -81,13 +81,13 @@ impl<'gcx> ToStableHashKey> for CrateNum { impl_stable_hash_for!(tuple_struct hir::ItemLocalId { index }); -impl<'gcx> ToStableHashKey> +impl<'a> ToStableHashKey> for hir::ItemLocalId { type KeyType = hir::ItemLocalId; #[inline] fn to_stable_hash_key(&self, - _: &StableHashingContext<'gcx>) + _: &StableHashingContext<'a>) -> hir::ItemLocalId { *self } @@ -100,9 +100,9 @@ for hir::ItemLocalId { // want to pick up on a reference changing its target, so we hash the NodeIds // in "DefPath Mode". -impl<'gcx> HashStable> for hir::ItemId { +impl<'a> HashStable> for hir::ItemId { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::ItemId { id @@ -114,9 +114,9 @@ impl<'gcx> HashStable> for hir::ItemId { } } -impl<'gcx> HashStable> for hir::TraitItemId { +impl<'a> HashStable> for hir::TraitItemId { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::TraitItemId { node_id @@ -128,9 +128,9 @@ impl<'gcx> HashStable> for hir::TraitItemId { } } -impl<'gcx> HashStable> for hir::ImplItemId { +impl<'a> HashStable> for hir::ImplItemId { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::ImplItemId { node_id @@ -271,9 +271,9 @@ impl_stable_hash_for!(struct hir::TypeBinding { span }); -impl<'gcx> HashStable> for hir::Ty { +impl<'a> HashStable> for hir::Ty { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.while_hashing_hir_bodies(true, |hcx| { let hir::Ty { @@ -339,9 +339,9 @@ impl_stable_hash_for!(enum hir::FunctionRetTy { Return(t) }); -impl<'gcx> HashStable> for hir::TraitRef { +impl<'a> HashStable> for hir::TraitRef { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::TraitRef { ref path, @@ -376,9 +376,9 @@ impl_stable_hash_for!(struct hir::MacroDef { }); -impl<'gcx> HashStable> for hir::Block { +impl<'a> HashStable> for hir::Block { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::Block { ref stmts, @@ -400,9 +400,9 @@ impl<'gcx> HashStable> for hir::Block { } } -impl<'gcx> HashStable> for hir::Pat { +impl<'a> HashStable> for hir::Pat { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::Pat { id: _, @@ -527,9 +527,9 @@ impl_stable_hash_for!(enum hir::UnsafeSource { UserProvided }); -impl<'gcx> HashStable> for hir::Expr { +impl<'a> HashStable> for hir::Expr { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.while_hashing_hir_bodies(true, |hcx| { let hir::Expr { @@ -591,9 +591,9 @@ impl_stable_hash_for!(enum hir::LoopSource { ForLoop }); -impl<'gcx> HashStable> for hir::MatchSource { +impl<'a> HashStable> for hir::MatchSource { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use hir::MatchSource; @@ -647,9 +647,9 @@ impl_stable_hash_for!(enum hir::ScopeTarget { Loop(loop_id_result) }); -impl<'gcx> HashStable> for ast::Ident { +impl<'a> HashStable> for ast::Ident { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ast::Ident { ref name, @@ -660,9 +660,9 @@ impl<'gcx> HashStable> for ast::Ident { } } -impl<'gcx> HashStable> for hir::TraitItem { +impl<'a> HashStable> for hir::TraitItem { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::TraitItem { id: _, @@ -695,9 +695,9 @@ impl_stable_hash_for!(enum hir::TraitItemKind { Type(bounds, rhs) }); -impl<'gcx> HashStable> for hir::ImplItem { +impl<'a> HashStable> for hir::ImplItem { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::ImplItem { id: _, @@ -729,9 +729,9 @@ impl_stable_hash_for!(enum hir::ImplItemKind { Type(t) }); -impl<'gcx> HashStable> for hir::Visibility { +impl<'a> HashStable> for hir::Visibility { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -750,9 +750,9 @@ impl<'gcx> HashStable> for hir::Visibility { } } -impl<'gcx> HashStable> for hir::Defaultness { +impl<'a> HashStable> for hir::Defaultness { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -771,9 +771,9 @@ impl_stable_hash_for!(enum hir::ImplPolarity { Negative }); -impl<'gcx> HashStable> for hir::Mod { +impl<'a> HashStable> for hir::Mod { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::Mod { inner, @@ -826,9 +826,9 @@ impl_stable_hash_for!(enum hir::VariantData { Unit(id) }); -impl<'gcx> HashStable> for hir::Item { +impl<'a> HashStable> for hir::Item { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::Item { name, @@ -885,10 +885,10 @@ impl_stable_hash_for!(struct hir::ImplItemRef { defaultness }); -impl<'gcx> HashStable> +impl<'a> HashStable> for hir::AssociatedItemKind { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -930,9 +930,9 @@ impl_stable_hash_for!(struct hir::Arg { hir_id }); -impl<'gcx> HashStable> for hir::Body { +impl<'a> HashStable> for hir::Body { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::Body { ref arguments, @@ -948,12 +948,12 @@ impl<'gcx> HashStable> for hir::Body { } } -impl<'gcx> ToStableHashKey> for hir::BodyId { +impl<'a> ToStableHashKey> for hir::BodyId { type KeyType = (DefPathHash, hir::ItemLocalId); #[inline] fn to_stable_hash_key(&self, - hcx: &StableHashingContext<'gcx>) + hcx: &StableHashingContext<'a>) -> (DefPathHash, hir::ItemLocalId) { let hir::BodyId { node_id } = *self; node_id.to_stable_hash_key(hcx) @@ -966,9 +966,9 @@ impl_stable_hash_for!(struct hir::InlineAsmOutput { is_indirect }); -impl<'gcx> HashStable> for hir::GlobalAsm { +impl<'a> HashStable> for hir::GlobalAsm { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::GlobalAsm { asm, @@ -979,9 +979,9 @@ impl<'gcx> HashStable> for hir::GlobalAsm { } } -impl<'gcx> HashStable> for hir::InlineAsm { +impl<'a> HashStable> for hir::InlineAsm { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::InlineAsm { asm, @@ -1062,22 +1062,22 @@ impl_stable_hash_for!(enum hir::Constness { NotConst }); -impl<'gcx> HashStable> +impl<'a> HashStable> for hir::def_id::DefIndex { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.local_def_path_hash(*self).hash_stable(hcx, hasher); } } -impl<'gcx> ToStableHashKey> +impl<'a> ToStableHashKey> for hir::def_id::DefIndex { type KeyType = DefPathHash; #[inline] - fn to_stable_hash_key(&self, hcx: &StableHashingContext<'gcx>) -> DefPathHash { + fn to_stable_hash_key(&self, hcx: &StableHashingContext<'a>) -> DefPathHash { hcx.local_def_path_hash(*self) } } @@ -1090,10 +1090,10 @@ impl_stable_hash_for!(struct hir::def::Export { is_import }); -impl<'gcx> HashStable> +impl<'a> HashStable> for ::middle::lang_items::LangItem { fn hash_stable(&self, - _: &mut StableHashingContext<'gcx>, + _: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { ::std::hash::Hash::hash(self, hasher); } @@ -1104,10 +1104,10 @@ impl_stable_hash_for!(struct ::middle::lang_items::LanguageItems { missing }); -impl<'gcx> HashStable> +impl<'a> HashStable> for hir::TraitCandidate { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { let hir::TraitCandidate { @@ -1121,11 +1121,11 @@ for hir::TraitCandidate { } } -impl<'gcx> ToStableHashKey> for hir::TraitCandidate { +impl<'a> ToStableHashKey> for hir::TraitCandidate { type KeyType = (DefPathHash, Option<(DefPathHash, hir::ItemLocalId)>); fn to_stable_hash_key(&self, - hcx: &StableHashingContext<'gcx>) + hcx: &StableHashingContext<'a>) -> Self::KeyType { let hir::TraitCandidate { def_id, diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs index 40204943bb599..1e6dadae36371 100644 --- a/src/librustc/ich/impls_mir.rs +++ b/src/librustc/ich/impls_mir.rs @@ -35,11 +35,11 @@ impl_stable_hash_for!(struct mir::BasicBlockData<'tcx> { statements, terminator, impl_stable_hash_for!(struct mir::UnsafetyViolation { source_info, description, kind }); impl_stable_hash_for!(struct mir::UnsafetyCheckResult { violations, unsafe_blocks }); -impl<'gcx> HashStable> +impl<'a> HashStable> for mir::BorrowKind { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -54,11 +54,11 @@ for mir::BorrowKind { } -impl<'gcx> HashStable> +impl<'a> HashStable> for mir::UnsafetyViolationKind { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -79,12 +79,12 @@ impl_stable_hash_for!(struct mir::Terminator<'tcx> { source_info }); -impl<'gcx, T> HashStable> for mir::ClearCrossCrate - where T: HashStable> +impl<'a, 'gcx, T> HashStable> for mir::ClearCrossCrate + where T: HashStable> { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -96,61 +96,61 @@ impl<'gcx, T> HashStable> for mir::ClearCrossCrate } } -impl<'gcx> HashStable> for mir::Local { +impl<'a> HashStable> for mir::Local { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use rustc_data_structures::indexed_vec::Idx; self.index().hash_stable(hcx, hasher); } } -impl<'gcx> HashStable> for mir::BasicBlock { +impl<'a> HashStable> for mir::BasicBlock { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use rustc_data_structures::indexed_vec::Idx; self.index().hash_stable(hcx, hasher); } } -impl<'gcx> HashStable> for mir::Field { +impl<'a> HashStable> for mir::Field { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use rustc_data_structures::indexed_vec::Idx; self.index().hash_stable(hcx, hasher); } } -impl<'gcx> HashStable> +impl<'a> HashStable> for mir::VisibilityScope { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use rustc_data_structures::indexed_vec::Idx; self.index().hash_stable(hcx, hasher); } } -impl<'gcx> HashStable> for mir::Promoted { +impl<'a> HashStable> for mir::Promoted { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use rustc_data_structures::indexed_vec::Idx; self.index().hash_stable(hcx, hasher); } } -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for mir::TerminatorKind<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -227,10 +227,10 @@ for mir::TerminatorKind<'gcx> { } } -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for mir::AssertMessage<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -250,10 +250,10 @@ for mir::AssertMessage<'gcx> { impl_stable_hash_for!(struct mir::Statement<'tcx> { source_info, kind }); -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for mir::StatementKind<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -287,12 +287,12 @@ for mir::StatementKind<'gcx> { } } -impl<'gcx, T> HashStable> +impl<'a, 'gcx, T> HashStable> for mir::ValidationOperand<'gcx, T> - where T: HashStable> + where T: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.place.hash_stable(hcx, hasher); @@ -304,9 +304,9 @@ impl<'gcx, T> HashStable> impl_stable_hash_for!(enum mir::ValidationOp { Acquire, Release, Suspend(region_scope) }); -impl<'gcx> HashStable> for mir::Place<'gcx> { +impl<'a, 'gcx> HashStable> for mir::Place<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -323,14 +323,14 @@ impl<'gcx> HashStable> for mir::Place<'gcx> { } } -impl<'gcx, B, V, T> HashStable> +impl<'a, 'gcx, B, V, T> HashStable> for mir::Projection<'gcx, B, V, T> - where B: HashStable>, - V: HashStable>, - T: HashStable> + where B: HashStable>, + V: HashStable>, + T: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let mir::Projection { ref base, @@ -342,13 +342,13 @@ for mir::Projection<'gcx, B, V, T> } } -impl<'gcx, V, T> HashStable> +impl<'a, 'gcx, V, T> HashStable> for mir::ProjectionElem<'gcx, V, T> - where V: HashStable>, - T: HashStable> + where V: HashStable>, + T: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -382,9 +382,9 @@ impl_stable_hash_for!(struct mir::VisibilityScopeInfo { lint_root, safety }); -impl<'gcx> HashStable> for mir::Safety { +impl<'a> HashStable> for mir::Safety { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -399,9 +399,9 @@ impl<'gcx> HashStable> for mir::Safety { } } -impl<'gcx> HashStable> for mir::Operand<'gcx> { +impl<'a, 'gcx> HashStable> for mir::Operand<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -419,9 +419,9 @@ impl<'gcx> HashStable> for mir::Operand<'gcx> { } } -impl<'gcx> HashStable> for mir::Rvalue<'gcx> { +impl<'a, 'gcx> HashStable> for mir::Rvalue<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -479,10 +479,10 @@ impl_stable_hash_for!(enum mir::CastKind { Unsize }); -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for mir::AggregateKind<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -541,9 +541,9 @@ impl_stable_hash_for!(enum mir::NullOp { impl_stable_hash_for!(struct mir::Constant<'tcx> { span, ty, literal }); -impl<'gcx> HashStable> for mir::Literal<'gcx> { +impl<'a, 'gcx> HashStable> for mir::Literal<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -570,9 +570,9 @@ impl_stable_hash_for!(struct mir::ClosureOutlivesRequirement<'tcx> { blame_span }); -impl<'gcx> HashStable> for mir::ClosureOutlivesSubject<'gcx> { +impl<'a, 'gcx> HashStable> for mir::ClosureOutlivesSubject<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc/ich/impls_syntax.rs index f935cbfcde992..52f43fbed7b0a 100644 --- a/src/librustc/ich/impls_syntax.rs +++ b/src/librustc/ich/impls_syntax.rs @@ -29,42 +29,42 @@ use rustc_data_structures::stable_hasher::{HashStable, ToStableHashKey, StableHasher, StableHasherResult}; use rustc_data_structures::accumulate_vec::AccumulateVec; -impl<'gcx> HashStable> for InternedString { +impl<'a> HashStable> for InternedString { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let s: &str = &**self; s.hash_stable(hcx, hasher); } } -impl<'gcx> ToStableHashKey> for InternedString { +impl<'a> ToStableHashKey> for InternedString { type KeyType = InternedString; #[inline] fn to_stable_hash_key(&self, - _: &StableHashingContext<'gcx>) + _: &StableHashingContext<'a>) -> InternedString { self.clone() } } -impl<'gcx> HashStable> for ast::Name { +impl<'a> HashStable> for ast::Name { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.as_str().hash_stable(hcx, hasher); } } -impl<'gcx> ToStableHashKey> for ast::Name { +impl<'a> ToStableHashKey> for ast::Name { type KeyType = InternedString; #[inline] fn to_stable_hash_key(&self, - _: &StableHashingContext<'gcx>) + _: &StableHashingContext<'a>) -> InternedString { self.as_str() } @@ -111,10 +111,10 @@ impl_stable_hash_for!(struct ::syntax::attr::Stability { rustc_const_unstable }); -impl<'gcx> HashStable> +impl<'a> HashStable> for ::syntax::attr::StabilityLevel { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -166,9 +166,9 @@ impl_stable_hash_for!(struct ::syntax::ast::Lifetime { id, span, ident }); impl_stable_hash_for!(enum ::syntax::ast::StrStyle { Cooked, Raw(pounds) }); impl_stable_hash_for!(enum ::syntax::ast::AttrStyle { Outer, Inner }); -impl<'gcx> HashStable> for [ast::Attribute] { +impl<'a> HashStable> for [ast::Attribute] { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { if self.len() == 0 { self.len().hash_stable(hcx, hasher); @@ -191,9 +191,9 @@ impl<'gcx> HashStable> for [ast::Attribute] { } } -impl<'gcx> HashStable> for ast::Attribute { +impl<'a> HashStable> for ast::Attribute { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { // Make sure that these have been filtered out. debug_assert!(self.name().map(|name| !hcx.is_ignored_attr(name)).unwrap_or(true)); @@ -220,10 +220,10 @@ impl<'gcx> HashStable> for ast::Attribute { } } -impl<'gcx> HashStable> +impl<'a> HashStable> for tokenstream::TokenTree { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -242,10 +242,10 @@ for tokenstream::TokenTree { } } -impl<'gcx> HashStable> +impl<'a> HashStable> for tokenstream::TokenStream { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { for sub_tt in self.trees() { sub_tt.hash_stable(hcx, hasher); @@ -253,9 +253,11 @@ for tokenstream::TokenStream { } } -fn hash_token<'gcx, W: StableHasherResult>(token: &token::Token, - hcx: &mut StableHashingContext<'gcx>, - hasher: &mut StableHasher) { +fn hash_token<'a, 'gcx, W: StableHasherResult>( + token: &token::Token, + hcx: &mut StableHashingContext<'a>, + hasher: &mut StableHasher, +) { mem::discriminant(token).hash_stable(hcx, hasher); match *token { token::Token::Eq | @@ -383,9 +385,9 @@ impl_stable_hash_for!(enum ::syntax_pos::FileName { Custom(s) }); -impl<'gcx> HashStable> for FileMap { +impl<'a> HashStable> for FileMap { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let FileMap { name: _, // We hash the smaller name_hash instead of this diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index a25741c95aaca..a92d9b16612a2 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -23,11 +23,11 @@ use traits; use ty; use mir; -impl<'gcx, T> HashStable> +impl<'a, 'gcx, T> HashStable> for &'gcx ty::Slice - where T: HashStable> { + where T: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { thread_local! { static CACHE: RefCell> = @@ -52,10 +52,10 @@ for &'gcx ty::Slice } } -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for ty::subst::Kind<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.unpack().hash_stable(hcx, hasher); } @@ -73,10 +73,10 @@ for ty::subst::UnpackedKind<'gcx> { } } -impl<'gcx> HashStable> +impl<'a> HashStable> for ty::RegionKind { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -120,20 +120,20 @@ for ty::RegionKind { } } -impl<'gcx> HashStable> for ty::RegionVid { +impl<'a> HashStable> for ty::RegionVid { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use rustc_data_structures::indexed_vec::Idx; self.index().hash_stable(hcx, hasher); } } -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for ty::adjustment::AutoBorrow<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -148,10 +148,10 @@ for ty::adjustment::AutoBorrow<'gcx> { } } -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for ty::adjustment::Adjust<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -197,10 +197,10 @@ impl_stable_hash_for!(enum ty::BorrowKind { MutBorrow }); -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for ty::UpvarCapture<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -224,11 +224,11 @@ impl_stable_hash_for!(struct ty::FnSig<'tcx> { abi }); -impl<'gcx, T> HashStable> for ty::Binder - where T: HashStable> +impl<'a, 'gcx, T> HashStable> for ty::Binder + where T: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ty::Binder(ref inner) = *self; inner.hash_stable(hcx, hasher); @@ -247,13 +247,13 @@ impl_stable_hash_for!(struct ty::TraitRef<'tcx> { def_id, substs }); impl_stable_hash_for!(struct ty::TraitPredicate<'tcx> { trait_ref }); impl_stable_hash_for!(struct ty::SubtypePredicate<'tcx> { a_is_expected, a, b }); -impl<'gcx, A, B> HashStable> +impl<'a, 'gcx, A, B> HashStable> for ty::OutlivesPredicate - where A: HashStable>, - B: HashStable>, + where A: HashStable>, + B: HashStable>, { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ty::OutlivesPredicate(ref a, ref b) = *self; a.hash_stable(hcx, hasher); @@ -265,9 +265,9 @@ impl_stable_hash_for!(struct ty::ProjectionPredicate<'tcx> { projection_ty, ty } impl_stable_hash_for!(struct ty::ProjectionTy<'tcx> { substs, item_def_id }); -impl<'gcx> HashStable> for ty::Predicate<'gcx> { +impl<'a, 'gcx> HashStable> for ty::Predicate<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -305,9 +305,9 @@ impl<'gcx> HashStable> for ty::Predicate<'gcx> { } } -impl<'gcx> HashStable> for ty::AdtFlags { +impl<'a> HashStable> for ty::AdtFlags { fn hash_stable(&self, - _: &mut StableHashingContext<'gcx>, + _: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { std_hash::Hash::hash(self, hasher); } @@ -332,10 +332,10 @@ impl_stable_hash_for!(struct ty::FieldDef { vis }); -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for ::middle::const_val::ConstVal<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use middle::const_val::ConstVal::*; @@ -368,7 +368,51 @@ impl_stable_hash_for!(struct mir::interpret::MemoryPointer { offset }); -impl_stable_hash_for!(tuple_struct mir::interpret::AllocId{id}); +impl<'a> HashStable> for mir::interpret::AllocId { + fn hash_stable( + &self, + hcx: &mut StableHashingContext<'a>, + hasher: &mut StableHasher, + ) { + ty::tls::with_opt(|tcx| { + let tcx = tcx.expect("can't hash AllocIds during hir lowering"); + let interner = tcx + .interpret_interner + .borrow(); + if let Some(def_id) = interner.get_corresponding_static_def_id(*self) { + 0.hash_stable(hcx, hasher); + // statics are unique via their DefId + def_id.hash_stable(hcx, hasher); + } else if let Some(alloc) = interner.get_alloc(*self) { + // not a static, can't be recursive, hash the allocation + 1.hash_stable(hcx, hasher); + alloc.hash_stable(hcx, hasher); + } else if let Some(inst) = interner.get_fn(*self) { + 2.hash_stable(hcx, hasher); + inst.hash_stable(hcx, hasher); + } else { + bug!("no allocation for {}", self); + } + }); + } +} + +impl<'a> HashStable> for mir::interpret::Allocation { + fn hash_stable( + &self, + hcx: &mut StableHashingContext<'a>, + hasher: &mut StableHasher, + ) { + self.bytes.hash_stable(hcx, hasher); + for reloc in self.relocations.iter() { + reloc.hash_stable(hcx, hasher); + } + self.undef_mask.hash_stable(hcx, hasher); + self.align.hash_stable(hcx, hasher); + self.mutable.hash_stable(hcx, hasher); + } +} + impl_stable_hash_for!(struct mir::interpret::Pointer{primval}); impl_stable_hash_for!(enum mir::interpret::PrimVal { @@ -387,10 +431,10 @@ impl_stable_hash_for!(struct ::middle::const_val::ConstEvalErr<'tcx> { kind }); -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for ::middle::const_val::ErrKind<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use middle::const_val::ErrKind::*; @@ -441,10 +485,10 @@ impl_stable_hash_for!(struct ty::GenericPredicates<'tcx> { predicates }); -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for ::mir::interpret::EvalError<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use mir::interpret::EvalErrorKind::*; @@ -621,9 +665,9 @@ impl_stable_hash_for!(enum ty::adjustment::CustomCoerceUnsized { Struct(index) }); -impl<'gcx> HashStable> for ty::Generics { +impl<'a> HashStable> for ty::Generics { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ty::Generics { parent, @@ -649,10 +693,10 @@ impl<'gcx> HashStable> for ty::Generics { } } -impl<'gcx> HashStable> +impl<'a> HashStable> for ty::RegionParameterDef { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ty::RegionParameterDef { name, @@ -678,12 +722,12 @@ impl_stable_hash_for!(struct ty::TypeParameterDef { synthetic }); -impl<'gcx, T> HashStable> +impl<'a, 'gcx, T> HashStable> for ::middle::resolve_lifetime::Set1 - where T: HashStable> + where T: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use middle::resolve_lifetime::Set1; @@ -734,11 +778,11 @@ impl_stable_hash_for!(enum ty::cast::CastKind { impl_stable_hash_for!(tuple_struct ::middle::region::FirstStatementIndex { idx }); impl_stable_hash_for!(struct ::middle::region::Scope { id, code }); -impl<'gcx> ToStableHashKey> for region::Scope { +impl<'a> ToStableHashKey> for region::Scope { type KeyType = region::Scope; #[inline] - fn to_stable_hash_key(&self, _: &StableHashingContext<'gcx>) -> region::Scope { + fn to_stable_hash_key(&self, _: &StableHashingContext<'a>) -> region::Scope { *self } } @@ -764,11 +808,11 @@ impl_stable_hash_for!(enum ty::BoundRegion { BrEnv }); -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for ty::TypeVariants<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use ty::TypeVariants::*; @@ -865,11 +909,11 @@ impl_stable_hash_for!(struct ty::TypeAndMut<'tcx> { mutbl }); -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for ty::ExistentialPredicate<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -902,9 +946,9 @@ impl_stable_hash_for!(struct ty::Instance<'tcx> { substs }); -impl<'gcx> HashStable> for ty::InstanceDef<'gcx> { +impl<'a, 'gcx> HashStable> for ty::InstanceDef<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -938,9 +982,9 @@ impl<'gcx> HashStable> for ty::InstanceDef<'gcx> { } } -impl<'gcx> HashStable> for ty::TraitDef { +impl<'a> HashStable> for ty::TraitDef { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ty::TraitDef { // We already have the def_path_hash below, no need to hash it twice @@ -968,9 +1012,9 @@ impl_stable_hash_for!(struct ty::DtorckConstraint<'tcx> { }); -impl<'gcx> HashStable> for ty::CrateVariancesMap { +impl<'a> HashStable> for ty::CrateVariancesMap { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ty::CrateVariancesMap { ref variances, @@ -1004,12 +1048,12 @@ impl_stable_hash_for!(enum ty::AssociatedItemContainer { }); -impl<'gcx, T> HashStable> +impl<'a, 'gcx, T> HashStable> for ty::steal::Steal - where T: HashStable> + where T: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.borrow().hash_stable(hcx, hasher); } @@ -1032,10 +1076,10 @@ impl_stable_hash_for!(enum ::middle::privacy::AccessLevel { Public }); -impl<'gcx> HashStable> +impl<'a> HashStable> for ::middle::privacy::AccessLevels { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { let ::middle::privacy::AccessLevels { @@ -1062,10 +1106,10 @@ impl_stable_hash_for!(tuple_struct ::middle::reachable::ReachableSet { reachable_set }); -impl<'gcx, N> HashStable> -for traits::Vtable<'gcx, N> where N: HashStable> { +impl<'a, 'gcx, N> HashStable> +for traits::Vtable<'gcx, N> where N: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use traits::Vtable::*; @@ -1084,10 +1128,10 @@ for traits::Vtable<'gcx, N> where N: HashStable> { } } -impl<'gcx, N> HashStable> -for traits::VtableImplData<'gcx, N> where N: HashStable> { +impl<'a, 'gcx, N> HashStable> +for traits::VtableImplData<'gcx, N> where N: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let traits::VtableImplData { impl_def_id, @@ -1100,10 +1144,10 @@ for traits::VtableImplData<'gcx, N> where N: HashStable HashStable> -for traits::VtableAutoImplData where N: HashStable> { +impl<'a, 'gcx, N> HashStable> +for traits::VtableAutoImplData where N: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let traits::VtableAutoImplData { trait_def_id, @@ -1114,10 +1158,10 @@ for traits::VtableAutoImplData where N: HashStable } } -impl<'gcx, N> HashStable> -for traits::VtableObjectData<'gcx, N> where N: HashStable> { +impl<'a, 'gcx, N> HashStable> +for traits::VtableObjectData<'gcx, N> where N: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let traits::VtableObjectData { upcast_trait_ref, @@ -1130,10 +1174,10 @@ for traits::VtableObjectData<'gcx, N> where N: HashStable HashStable> -for traits::VtableBuiltinData where N: HashStable> { +impl<'a, 'gcx, N> HashStable> +for traits::VtableBuiltinData where N: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let traits::VtableBuiltinData { ref nested, @@ -1142,10 +1186,10 @@ for traits::VtableBuiltinData where N: HashStable> } } -impl<'gcx, N> HashStable> -for traits::VtableClosureData<'gcx, N> where N: HashStable> { +impl<'a, 'gcx, N> HashStable> +for traits::VtableClosureData<'gcx, N> where N: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let traits::VtableClosureData { closure_def_id, @@ -1158,10 +1202,10 @@ for traits::VtableClosureData<'gcx, N> where N: HashStable HashStable> -for traits::VtableFnPointerData<'gcx, N> where N: HashStable> { +impl<'a, 'gcx, N> HashStable> +for traits::VtableFnPointerData<'gcx, N> where N: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let traits::VtableFnPointerData { fn_ty, @@ -1172,10 +1216,10 @@ for traits::VtableFnPointerData<'gcx, N> where N: HashStable HashStable> -for traits::VtableGeneratorData<'gcx, N> where N: HashStable> { +impl<'a, 'gcx, N> HashStable> +for traits::VtableGeneratorData<'gcx, N> where N: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let traits::VtableGeneratorData { closure_def_id, diff --git a/src/librustc/lint/levels.rs b/src/librustc/lint/levels.rs index 909904b4fc36c..8a899a35ecb54 100644 --- a/src/librustc/lint/levels.rs +++ b/src/librustc/lint/levels.rs @@ -394,10 +394,10 @@ impl LintLevelMap { } } -impl<'gcx> HashStable> for LintLevelMap { +impl<'a> HashStable> for LintLevelMap { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let LintLevelMap { ref sets, diff --git a/src/librustc/macros.rs b/src/librustc/macros.rs index f0285d6a93782..9a394e524817b 100644 --- a/src/librustc/macros.rs +++ b/src/librustc/macros.rs @@ -73,10 +73,10 @@ macro_rules! __impl_stable_hash_field { #[macro_export] macro_rules! impl_stable_hash_for { (enum $enum_name:path { $( $variant:ident $( ( $($arg:ident),* ) )* ),* }) => { - impl<'tcx> ::rustc_data_structures::stable_hasher::HashStable<$crate::ich::StableHashingContext<'tcx>> for $enum_name { + impl<'a, 'tcx> ::rustc_data_structures::stable_hasher::HashStable<$crate::ich::StableHashingContext<'a>> for $enum_name { #[inline] fn hash_stable(&self, - __ctx: &mut $crate::ich::StableHashingContext<'tcx>, + __ctx: &mut $crate::ich::StableHashingContext<'a>, __hasher: &mut ::rustc_data_structures::stable_hasher::StableHasher) { use $enum_name::*; ::std::mem::discriminant(self).hash_stable(__ctx, __hasher); @@ -92,10 +92,10 @@ macro_rules! impl_stable_hash_for { } }; (struct $struct_name:path { $($field:ident),* }) => { - impl<'tcx> ::rustc_data_structures::stable_hasher::HashStable<$crate::ich::StableHashingContext<'tcx>> for $struct_name { + impl<'a, 'tcx> ::rustc_data_structures::stable_hasher::HashStable<$crate::ich::StableHashingContext<'a>> for $struct_name { #[inline] fn hash_stable(&self, - __ctx: &mut $crate::ich::StableHashingContext<'tcx>, + __ctx: &mut $crate::ich::StableHashingContext<'a>, __hasher: &mut ::rustc_data_structures::stable_hasher::StableHasher) { let $struct_name { $(ref $field),* @@ -106,10 +106,10 @@ macro_rules! impl_stable_hash_for { } }; (tuple_struct $struct_name:path { $($field:ident),* }) => { - impl<'tcx> ::rustc_data_structures::stable_hasher::HashStable<$crate::ich::StableHashingContext<'tcx>> for $struct_name { + impl<'a, 'tcx> ::rustc_data_structures::stable_hasher::HashStable<$crate::ich::StableHashingContext<'a>> for $struct_name { #[inline] fn hash_stable(&self, - __ctx: &mut $crate::ich::StableHashingContext<'tcx>, + __ctx: &mut $crate::ich::StableHashingContext<'a>, __hasher: &mut ::rustc_data_structures::stable_hasher::StableHasher) { let $struct_name ( $(ref $field),* @@ -125,11 +125,11 @@ macro_rules! impl_stable_hash_for { macro_rules! impl_stable_hash_for_spanned { ($T:path) => ( - impl<'tcx> HashStable> for ::syntax::codemap::Spanned<$T> + impl<'a, 'tcx> HashStable> for ::syntax::codemap::Spanned<$T> { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'tcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.node.hash_stable(hcx, hasher); self.span.hash_stable(hcx, hasher); diff --git a/src/librustc/middle/borrowck.rs b/src/librustc/middle/borrowck.rs index 380f79361e27f..6f5791ed5d71b 100644 --- a/src/librustc/middle/borrowck.rs +++ b/src/librustc/middle/borrowck.rs @@ -20,9 +20,9 @@ pub struct BorrowCheckResult { pub used_mut_nodes: FxHashSet, } -impl<'gcx> HashStable> for BorrowCheckResult { +impl<'a> HashStable> for BorrowCheckResult { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let BorrowCheckResult { ref used_mut_nodes, diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 812ee0dc72fd6..c73930553cdea 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -1488,9 +1488,9 @@ pub fn provide(providers: &mut Providers) { }; } -impl<'gcx> HashStable> for ScopeTree { +impl<'a> HashStable> for ScopeTree { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ScopeTree { root_body, diff --git a/src/librustc/mir/cache.rs b/src/librustc/mir/cache.rs index efc2f647cfdf5..45cb70d007068 100644 --- a/src/librustc/mir/cache.rs +++ b/src/librustc/mir/cache.rs @@ -35,9 +35,9 @@ impl serialize::Decodable for Cache { } } -impl<'gcx> HashStable> for Cache { +impl<'a> HashStable> for Cache { fn hash_stable(&self, - _: &mut StableHashingContext<'gcx>, + _: &mut StableHashingContext<'a>, _: &mut StableHasher) { // do nothing } diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs index 394b3d96a4fb6..c395be549a397 100644 --- a/src/librustc/mir/interpret/mod.rs +++ b/src/librustc/mir/interpret/mod.rs @@ -200,6 +200,8 @@ pub struct UndefMask { len: u64, } +impl_stable_hash_for!(struct mir::interpret::UndefMask{blocks, len}); + impl UndefMask { pub fn new(size: u64) -> Self { let mut m = UndefMask { diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 9e9ba51620338..8da3a125d3ff5 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -1877,7 +1877,7 @@ fn fmt_const_val(fmt: &mut W, const_val: &ty::Const) -> fmt::Result { } } -fn print_miri_value(value: Value, ty: Ty, f: &mut W) -> fmt::Result { +pub fn print_miri_value(value: Value, ty: Ty, f: &mut W) -> fmt::Result { use ty::TypeVariants::*; use rustc_const_math::ConstFloat; match (value, &ty.sty) { diff --git a/src/librustc/mir/mono.rs b/src/librustc/mir/mono.rs index d8eac2b415989..d01059a3e0171 100644 --- a/src/librustc/mir/mono.rs +++ b/src/librustc/mir/mono.rs @@ -41,9 +41,9 @@ impl<'tcx> MonoItem<'tcx> { } } -impl<'tcx> HashStable> for MonoItem<'tcx> { +impl<'a, 'tcx> HashStable> for MonoItem<'tcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'tcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { ::std::mem::discriminant(self).hash_stable(hcx, hasher); @@ -171,9 +171,9 @@ impl<'tcx> CodegenUnit<'tcx> { } } -impl<'tcx> HashStable> for CodegenUnit<'tcx> { +impl<'a, 'tcx> HashStable> for CodegenUnit<'tcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'tcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let CodegenUnit { ref items, diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 157614f847a12..437369400ed11 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -179,10 +179,10 @@ impl_stable_hash_for!(enum self::OutputType { DepInfo }); -impl<'tcx> ToStableHashKey> for OutputType { +impl<'a, 'tcx> ToStableHashKey> for OutputType { type KeyType = OutputType; #[inline] - fn to_stable_hash_key(&self, _: &StableHashingContext<'tcx>) -> Self::KeyType { + fn to_stable_hash_key(&self, _: &StableHashingContext<'a>) -> Self::KeyType { *self } } diff --git a/src/librustc/traits/specialize/specialization_graph.rs b/src/librustc/traits/specialize/specialization_graph.rs index dbf15ad17079d..f8b895177f381 100644 --- a/src/librustc/traits/specialize/specialization_graph.rs +++ b/src/librustc/traits/specialize/specialization_graph.rs @@ -391,9 +391,9 @@ pub fn ancestors(tcx: TyCtxt, } } -impl<'gcx> HashStable> for Children { +impl<'a> HashStable> for Children { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let Children { ref nonblanket_impls, diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index bf75afcfa1a9f..3ec3f665db9c9 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -676,9 +676,9 @@ impl<'tcx> TypeckTables<'tcx> { } } -impl<'gcx> HashStable> for TypeckTables<'gcx> { +impl<'a, 'gcx> HashStable> for TypeckTables<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ty::TypeckTables { local_id_root, @@ -1366,7 +1366,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self.cstore.crate_data_as_rc_any(cnum) } - pub fn create_stable_hashing_context(self) -> StableHashingContext<'gcx> { + pub fn create_stable_hashing_context(self) -> StableHashingContext<'a> { let krate = self.dep_graph.with_ignore(|| self.gcx.hir.krate()); StableHashingContext::new(self.sess, diff --git a/src/librustc/ty/fast_reject.rs b/src/librustc/ty/fast_reject.rs index 97c259e6bf383..93d8a4d979de6 100644 --- a/src/librustc/ty/fast_reject.rs +++ b/src/librustc/ty/fast_reject.rs @@ -154,12 +154,12 @@ impl SimplifiedTypeGen { } } -impl<'gcx, D> HashStable> for SimplifiedTypeGen +impl<'a, 'gcx, D> HashStable> for SimplifiedTypeGen where D: Copy + Debug + Ord + Eq + Hash + - HashStable>, + HashStable>, { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 35d8fb2a67ac9..79f01a32cd975 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -2369,9 +2369,9 @@ impl<'a, 'tcx> TyLayout<'tcx> { } } -impl<'gcx> HashStable> for Variants { +impl<'a> HashStable> for Variants { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use ty::layout::Variants::*; mem::discriminant(self).hash_stable(hcx, hasher); @@ -2405,9 +2405,9 @@ impl<'gcx> HashStable> for Variants { } } -impl<'gcx> HashStable> for FieldPlacement { +impl<'a> HashStable> for FieldPlacement { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use ty::layout::FieldPlacement::*; mem::discriminant(self).hash_stable(hcx, hasher); @@ -2428,9 +2428,9 @@ impl<'gcx> HashStable> for FieldPlacement { } } -impl<'gcx> HashStable> for Abi { +impl<'a> HashStable> for Abi { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use ty::layout::Abi::*; mem::discriminant(self).hash_stable(hcx, hasher); @@ -2455,9 +2455,9 @@ impl<'gcx> HashStable> for Abi { } } -impl<'gcx> HashStable> for Scalar { +impl<'a> HashStable> for Scalar { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let Scalar { value, valid_range: RangeInclusive { start, end } } = *self; value.hash_stable(hcx, hasher); @@ -2498,10 +2498,10 @@ impl_stable_hash_for!(struct ::ty::layout::Size { raw }); -impl<'gcx> HashStable> for LayoutError<'gcx> +impl<'a, 'gcx> HashStable> for LayoutError<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use ty::layout::LayoutError::*; mem::discriminant(self).hash_stable(hcx, hasher); diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index f9071cff78f49..2c7ec5155a627 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -80,7 +80,7 @@ pub use self::binding::BindingMode; pub use self::binding::BindingMode::*; pub use self::context::{TyCtxt, GlobalArenas, AllArenas, tls, keep_local}; -pub use self::context::{Lift, TypeckTables}; +pub use self::context::{Lift, TypeckTables, InterpretInterner}; pub use self::instance::{Instance, InstanceDef}; @@ -529,9 +529,9 @@ impl<'tcx> TyS<'tcx> { } } -impl<'gcx> HashStable> for ty::TyS<'gcx> { +impl<'a, 'gcx> HashStable> for ty::TyS<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ty::TyS { ref sty, @@ -1440,11 +1440,11 @@ impl<'tcx, T> ParamEnvAnd<'tcx, T> { } } -impl<'gcx, T> HashStable> for ParamEnvAnd<'gcx, T> - where T: HashStable> +impl<'a, 'gcx, T> HashStable> for ParamEnvAnd<'gcx, T> + where T: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ParamEnvAnd { ref param_env, @@ -1545,9 +1545,9 @@ impl<'tcx> serialize::UseSpecializedEncodable for &'tcx AdtDef { impl<'tcx> serialize::UseSpecializedDecodable for &'tcx AdtDef {} -impl<'gcx> HashStable> for AdtDef { +impl<'a> HashStable> for AdtDef { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { thread_local! { static CACHE: RefCell> = diff --git a/src/librustc/ty/trait_def.rs b/src/librustc/ty/trait_def.rs index 62d3c8dc87da3..32f0d3384c4dc 100644 --- a/src/librustc/ty/trait_def.rs +++ b/src/librustc/ty/trait_def.rs @@ -186,9 +186,9 @@ pub(super) fn trait_impls_of_provider<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, }) } -impl<'gcx> HashStable> for TraitImpls { +impl<'a> HashStable> for TraitImpls { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let TraitImpls { ref blanket_impls, diff --git a/src/librustc_metadata/schema.rs b/src/librustc_metadata/schema.rs index ce94e4f912f4f..593f08e90bb3b 100644 --- a/src/librustc_metadata/schema.rs +++ b/src/librustc_metadata/schema.rs @@ -227,9 +227,9 @@ pub struct TraitImpls { pub impls: LazySeq, } -impl<'gcx> HashStable> for TraitImpls { +impl<'a, 'gcx> HashStable> for TraitImpls { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let TraitImpls { trait_id: (krate, def_index), @@ -310,9 +310,9 @@ pub enum EntryKind<'tcx> { AssociatedConst(AssociatedContainer, u8), } -impl<'gcx> HashStable> for EntryKind<'gcx> { +impl<'a, 'gcx> HashStable> for EntryKind<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index 30c70ac30a70b..067041b14f583 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -20,12 +20,10 @@ use build::matches::{Candidate, MatchPair, Test, TestKind}; use hair::*; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::bitvec::BitVector; -use rustc::middle::const_val::ConstVal; use rustc::ty::{self, Ty}; use rustc::ty::util::IntTypeExt; use rustc::mir::*; -use rustc::mir::interpret::{Value, PrimVal}; -use rustc::hir::RangeEnd; +use rustc::hir::{RangeEnd, Mutability}; use syntax_pos::Span; use std::cmp::Ordering; @@ -297,76 +295,86 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { TestKind::Eq { value, ty } => { let tcx = self.hir.tcx(); let mut val = Operand::Copy(place.clone()); - - let bytes = match value.val { - ConstVal::Value(Value::ByVal(PrimVal::Ptr(p))) => { - let is_array_ptr = ty - .builtin_deref(true, ty::NoPreference) - .and_then(|t| t.ty.builtin_index()) - .map_or(false, |t| t == self.hir.tcx().types.u8); - if is_array_ptr { - self.hir - .tcx() - .interpret_interner - .borrow() - .get_alloc(p.alloc_id) - .map(|alloc| &alloc.bytes[..]) - } else { - None - } - }, - _ => None, - }; - // If we're using b"..." as a pattern, we need to insert an - // unsizing coercion, as the byte string has the type &[u8; N]. - // - // We want to do this even when the scrutinee is a reference to an - // array, so we can call `<[u8]>::eq` rather than having to find an - // `<[u8; N]>::eq`. - let expect = if let Some(bytes) = bytes { - let tcx = self.hir.tcx(); - - // Unsize the place to &[u8], too, if necessary. - if let ty::TyRef(region, mt) = ty.sty { - if let ty::TyArray(_, _) = mt.ty.sty { - ty = tcx.mk_imm_ref(region, tcx.mk_slice(tcx.types.u8)); - let val_slice = self.temp(ty, test.span); - self.cfg.push_assign(block, source_info, &val_slice, - Rvalue::Cast(CastKind::Unsize, val, ty)); - val = Operand::Move(val_slice); - } - } - - assert!(ty.is_slice()); - - let array_ty = tcx.mk_array(tcx.types.u8, bytes.len() as u64); - let array_ref = tcx.mk_imm_ref(tcx.types.re_static, array_ty); - let array = self.literal_operand(test.span, array_ref, Literal::Value { - value - }); - - let val = self.to_slice_operand(block, source_info, val); - let slice = self.to_slice_operand(block, source_info, array); - (slice, val) - } else { - (self.literal_operand(test.span, ty, Literal::Value { - value - }), val) - }; - - // Use PartialEq::eq for &str and &[u8] slices, instead of BinOp::Eq. + let mut expect = self.literal_operand(test.span, ty, Literal::Value { + value + }); + // Use PartialEq::eq instead of BinOp::Eq + // (the binop can only handle primitives) let fail = self.cfg.start_new_block(); - let str_or_bytestr = ty - .builtin_deref(true, ty::NoPreference) - .and_then(|tam| match tam.ty.sty { - ty::TyStr => Some(tam.ty), - ty::TySlice(inner) if inner == self.hir.tcx().types.u8 => Some(tam.ty), + if !ty.is_scalar() { + // If we're using b"..." as a pattern, we need to insert an + // unsizing coercion, as the byte string has the type &[u8; N]. + // + // We want to do this even when the scrutinee is a reference to an + // array, so we can call `<[u8]>::eq` rather than having to find an + // `<[u8; N]>::eq`. + let unsize = |ty: Ty<'tcx>| match ty.sty { + ty::TyRef(region, tam) => match tam.ty.sty { + ty::TyArray(inner_ty, n) => Some((region, inner_ty, n)), + _ => None, + }, _ => None, - }); - if let Some(ty) = str_or_bytestr { + }; + let opt_ref_ty = unsize(ty); + let opt_ref_test_ty = unsize(value.ty); + let mut place = place.clone(); + match (opt_ref_ty, opt_ref_test_ty) { + // nothing to do, neither is an array + (None, None) => {}, + (Some((region, elem_ty, _)), _) | + (None, Some((region, elem_ty, _))) => { + let tcx = self.hir.tcx(); + // make both a slice + ty = tcx.mk_imm_ref(region, tcx.mk_slice(elem_ty)); + if opt_ref_ty.is_some() { + place = self.temp(ty, test.span); + self.cfg.push_assign(block, source_info, &place, + Rvalue::Cast(CastKind::Unsize, val, ty)); + } + if opt_ref_test_ty.is_some() { + let array = self.literal_operand( + test.span, + value.ty, + Literal::Value { + value + }, + ); + + let slice = self.temp(ty, test.span); + self.cfg.push_assign(block, source_info, &slice, + Rvalue::Cast(CastKind::Unsize, array, ty)); + expect = Operand::Move(slice); + } + }, + } let eq_def_id = self.hir.tcx().lang_items().eq_trait().unwrap(); let (mty, method) = self.hir.trait_method(eq_def_id, "eq", ty, &[ty]); + // take the argument by reference + let region_scope = self.topmost_scope(); + let region = self.hir.tcx().mk_region(ty::ReScope(region_scope)); + let tam = ty::TypeAndMut { + ty, + mutbl: Mutability::MutImmutable, + }; + let ref_ty = self.hir.tcx().mk_ref(region, tam); + + // let lhs_ref_place = &lhs; + let ref_rvalue = Rvalue::Ref(region, BorrowKind::Shared, place.clone()); + let lhs_ref_place = self.temp(ref_ty, test.span); + self.cfg.push_assign(block, source_info, &lhs_ref_place, ref_rvalue); + let val = Operand::Move(lhs_ref_place); + + // let rhs_place = rhs; + let rhs_place = self.temp(ty, test.span); + self.cfg.push_assign(block, source_info, &rhs_place, Rvalue::Use(expect)); + + // let rhs_ref_place = &rhs_place; + let ref_rvalue = Rvalue::Ref(region, BorrowKind::Shared, rhs_place); + let rhs_ref_place = self.temp(ref_ty, test.span); + self.cfg.push_assign(block, source_info, &rhs_ref_place, ref_rvalue); + let expect = Operand::Move(rhs_ref_place); + let bool_ty = self.hir.bool_ty(); let eq_result = self.temp(bool_ty, test.span); let eq_block = self.cfg.start_new_block(); diff --git a/src/librustc_mir/diagnostics.rs b/src/librustc_mir/diagnostics.rs index f253327664597..2000ebea25d7e 100644 --- a/src/librustc_mir/diagnostics.rs +++ b/src/librustc_mir/diagnostics.rs @@ -72,36 +72,6 @@ fn foo(x: Option) { ``` "##, -E0003: r##" -#### Note: this error code is no longer emitted by the compiler. - -Not-a-Number (NaN) values cannot be compared for equality and hence can never -match the input to a match expression. So, the following will not compile: - -```compile_fail -const NAN: f32 = 0.0 / 0.0; - -let number = 0.1f32; - -match number { - NAN => { /* ... */ }, - _ => {} -} -``` - -To match against NaN values, you should instead use the `is_nan()` method in a -guard, like so: - -``` -let number = 0.1f32; - -match number { - x if x.is_nan() => { /* ... */ } - _ => {} -} -``` -"##, - E0004: r##" This error indicates that the compiler cannot guarantee a matching pattern for one or more possible inputs to a match expression. Guaranteed matches are diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index 1b4cbecab3b06..2dcad403f522a 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -19,6 +19,7 @@ pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>, mir: &'mir mir::Mir<'tcx>, + span: Span, ) -> EvalResult<'tcx, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>> { debug!("mk_borrowck_eval_cx: {:?}", instance); let param_env = tcx.param_env(instance.def_id()); @@ -27,7 +28,7 @@ pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>( // insert a stack frame so any queries have the correct substs ecx.push_stack_frame( instance, - mir.span, + span, mir, Place::undef(), StackPopCleanup::None, @@ -65,7 +66,7 @@ pub fn eval_body_with_mir<'a, 'mir, 'tcx>( match res { Ok(val) => Some(val), Err(mut err) => { - ecx.report(&mut err, true); + ecx.report(&mut err, true, None); None } } @@ -80,7 +81,7 @@ pub fn eval_body<'a, 'tcx>( match res { Ok(val) => Some(val), Err(mut err) => { - ecx.report(&mut err, true); + ecx.report(&mut err, true, None); None } } @@ -483,7 +484,7 @@ pub fn const_eval_provider<'a, 'tcx>( }) }).map_err(|mut err| { if tcx.is_static(def_id).is_some() { - ecx.report(&mut err, true); + ecx.report(&mut err, true, None); } ConstEvalErr { kind: err.into(), diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 7d2b83e921748..7dafe846a334b 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -10,7 +10,7 @@ use rustc::ty::layout::{self, Size, Align, HasDataLayout, LayoutOf, TyLayout}; use rustc::ty::subst::{Subst, Substs}; use rustc::ty::{self, Ty, TyCtxt}; use rustc_data_structures::indexed_vec::Idx; -use syntax::codemap::{self, DUMMY_SP}; +use syntax::codemap::{self, DUMMY_SP, Span}; use syntax::ast::Mutability; use rustc::mir::interpret::{ GlobalId, Value, Pointer, PrimVal, PrimValKind, @@ -464,7 +464,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M StackPopCleanup::MarkStatic(mutable) => { if let Place::Ptr { ptr, .. } = frame.return_place { // FIXME: to_ptr()? might be too extreme here, static zsts might reach this under certain conditions - self.memory.mark_static_initalized( + self.memory.mark_static_initialized( ptr.to_ptr()?.alloc_id, mutable, )? @@ -1572,7 +1572,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M Ok(()) } - pub fn report(&self, e: &mut EvalError, as_err: bool) { + pub fn report(&self, e: &mut EvalError, as_err: bool, explicit_span: Option) { if let EvalErrorKind::TypeckError = e.kind { return; } @@ -1608,11 +1608,12 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M } if let Some(frame) = self.stack().last() { let block = &frame.mir.basic_blocks()[frame.block]; - let span = if frame.stmt < block.statements.len() { + let span = explicit_span.unwrap_or_else(|| if frame.stmt < block.statements.len() { block.statements[frame.stmt].source_info.span } else { block.terminator().source_info.span - }; + }); + trace!("reporting const eval failure at {:?}", span); let node_id = self .stack() .iter() @@ -1634,6 +1635,9 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M let mut last_span = None; for &Frame { instance, span, .. } in self.stack().iter().rev() { // make sure we don't emit frames that are duplicates of the previous + if explicit_span == Some(span) { + continue; + } if let Some(last) = last_span { if last == span { continue; diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 5ee84e0e02c41..eebd48f7bb95c 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -520,18 +520,18 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { // do not go into statics None => Ok(()), // just locals and machine allocs - Some(_) => self.mark_static_initalized(alloc, mutability), + Some(_) => self.mark_static_initialized(alloc, mutability), } } /// mark an allocation as static and initialized, either mutable or not - pub fn mark_static_initalized( + pub fn mark_static_initialized( &mut self, alloc_id: AllocId, mutability: Mutability, ) -> EvalResult<'tcx> { trace!( - "mark_static_initalized {:?}, mutability: {:?}", + "mark_static_initialized {:?}, mutability: {:?}", alloc_id, mutability ); diff --git a/src/librustc_mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs index 4dc0879c85d52..03b215fa0b8ea 100644 --- a/src/librustc_mir/interpret/traits.rs +++ b/src/librustc_mir/interpret/traits.rs @@ -52,7 +52,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { } } - self.memory.mark_static_initalized( + self.memory.mark_static_initialized( vtable.alloc_id, Mutability::Immutable, )?; diff --git a/src/librustc_mir/transform/instcombine.rs b/src/librustc_mir/transform/instcombine.rs index e27e7c72473be..6079e1929b578 100644 --- a/src/librustc_mir/transform/instcombine.rs +++ b/src/librustc_mir/transform/instcombine.rs @@ -23,7 +23,7 @@ use rustc_data_structures::indexed_vec::Idx; use std::mem; use std::collections::VecDeque; use transform::{MirPass, MirSource}; -use syntax::codemap::{Span, DUMMY_SP}; +use syntax::codemap::Span; use rustc_data_structures::control_flow_graph::ControlFlowGraph; use rustc::ty::subst::Substs; @@ -147,7 +147,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for InstCombineVisitor<'a, 'tcx> { TerminatorKind::SwitchInt { discr: value, .. } | TerminatorKind::Yield { value, .. } | TerminatorKind::Assert { cond: value, .. } => { - if let Some((new, ty, span)) = self.optimizations.const_prop.remove(&location) { + if let Some((new, ty, span)) = self.optimizations.terminators.remove(&block) { let new = self.tcx.mk_const(ty::Const { val: ConstVal::Value(new), ty, @@ -190,13 +190,13 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { } } - fn eval_constant(&mut self, c: &Constant<'tcx>, span: Span) -> Option> { + fn eval_constant(&mut self, c: &Constant<'tcx>) -> Option> { if let Some(&val) = self.optimizations.constants.get(c) { return Some(val); } match c.literal { Literal::Value { value } => match value.val { - ConstVal::Value(v) => Some((v, value.ty, span)), + ConstVal::Value(v) => Some((v, value.ty, c.span)), ConstVal::Unevaluated(did, substs) => { let param_env = self.tcx.param_env(self.source.def_id); let span = self.tcx.def_span(did); @@ -230,10 +230,9 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { instance, promoted: Some(index), }; - let span = self.tcx.def_span(self.source.def_id); let param_env = self.tcx.param_env(self.source.def_id); let (value, _, ty) = eval_body_with_mir(self.tcx, cid, self.mir, param_env)?; - let val = (value, ty, span); + let val = (value, ty, c.span); trace!("evaluated {:?} to {:?}", c, val); self.optimizations.constants.insert(c.clone(), val); Some(val) @@ -241,9 +240,9 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { } } - fn eval_operand(&mut self, op: &Operand<'tcx>, span: Span) -> Option> { + fn eval_operand(&mut self, op: &Operand<'tcx>) -> Option> { match *op { - Operand::Constant(ref c) => self.eval_constant(c, span), + Operand::Constant(ref c) => self.eval_constant(c), Operand::Move(ref place) | Operand::Copy(ref place) => match *place { Place::Local(loc) => self.optimizations.places.get(&loc).cloned(), // FIXME(oli-obk): field and index projections @@ -253,13 +252,13 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { } } - fn simplify_operand(&mut self, op: &Operand<'tcx>, span: Span) -> Option> { + fn simplify_operand(&mut self, op: &Operand<'tcx>) -> Option> { match *op { Operand::Constant(ref c) => match c.literal { Literal::Value { .. } => None, - _ => self.eval_operand(op, span), + _ => self.eval_operand(op), }, - _ => self.eval_operand(op, span), + _ => self.eval_operand(op), } } @@ -270,7 +269,7 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { span: Span, ) -> Option> { match *rvalue { - Rvalue::Use(ref op) => self.simplify_operand(op, span), + Rvalue::Use(ref op) => self.simplify_operand(op), Rvalue::Repeat(..) | Rvalue::Ref(..) | Rvalue::Cast(..) | @@ -300,15 +299,15 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { } let substs = Substs::identity_for_item(self.tcx, self.source.def_id); let instance = Instance::new(self.source.def_id, substs); - let ecx = mk_borrowck_eval_cx(self.tcx, instance, self.mir).unwrap(); + let ecx = mk_borrowck_eval_cx(self.tcx, instance, self.mir, span).unwrap(); - let val = self.eval_operand(arg, span)?; + let val = self.eval_operand(arg)?; let prim = ecx.value_to_primval(ValTy { value: val.0, ty: val.1 }).ok()?; let kind = ecx.ty_to_primval_kind(val.1).ok()?; match unary_op(op, prim, kind) { Ok(val) => Some((Value::ByVal(val), place_ty, span)), Err(mut err) => { - ecx.report(&mut err, false); + ecx.report(&mut err, false, Some(span)); None }, } @@ -316,8 +315,8 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { Rvalue::CheckedBinaryOp(op, ref left, ref right) | Rvalue::BinaryOp(op, ref left, ref right) => { trace!("rvalue binop {:?} for {:?} and {:?}", op, left, right); - let left = self.eval_operand(left, span)?; - let right = self.eval_operand(right, span)?; + let left = self.eval_operand(left)?; + let right = self.eval_operand(right)?; let def_id = if self.tcx.is_closure(self.source.def_id) { self.tcx.closure_base_def_id(self.source.def_id) } else { @@ -331,7 +330,7 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { } let substs = Substs::identity_for_item(self.tcx, self.source.def_id); let instance = Instance::new(self.source.def_id, substs); - let ecx = mk_borrowck_eval_cx(self.tcx, instance, self.mir).unwrap(); + let ecx = mk_borrowck_eval_cx(self.tcx, instance, self.mir, span).unwrap(); let l = ecx.value_to_primval(ValTy { value: left.0, ty: left.1 }).ok()?; let r = ecx.value_to_primval(ValTy { value: right.0, ty: right.1 }).ok()?; @@ -351,7 +350,7 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { kind: EvalErrorKind::OverflowingMath, backtrace: None, }; - ecx.report(&mut err, false); + ecx.report(&mut err, false, Some(span)); return None; } Value::ByVal(val) @@ -359,7 +358,7 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { Some((val, place_ty, span)) }, Err(mut err) => { - ecx.report(&mut err, false); + ecx.report(&mut err, false, Some(span)); None }, } @@ -500,7 +499,7 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for OptimizationFinder<'b, 'a, 'tcx> { ) { trace!("visit_constant: {:?}", constant); self.super_constant(constant, location); - self.eval_constant(constant, DUMMY_SP); + self.eval_constant(constant); } fn visit_statement( @@ -514,7 +513,7 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for OptimizationFinder<'b, 'a, 'tcx> { let place_ty = place .ty(&self.mir.local_decls, self.tcx) .to_ty(self.tcx); - let span = self.mir.source_info(location).span; + let span = statement.source_info.span; if let Some(value) = self.const_prop(rval, place_ty, span) { self.optimizations.const_prop.insert(location, value); if let Place::Local(local) = *place { @@ -554,17 +553,16 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for OptimizationFinder<'b, 'a, 'tcx> { fn visit_terminator_kind( &mut self, - _block: BasicBlock, + block: BasicBlock, kind: &TerminatorKind<'tcx>, - location: Location, + _location: Location, ) { - let span = self.mir.source_info(location).span; match kind { TerminatorKind::SwitchInt { discr: value, .. } | TerminatorKind::Yield { value, .. } | TerminatorKind::Assert { cond: value, .. } => { - if let Some(value) = self.simplify_operand(value, span) { - self.optimizations.const_prop.insert(location, value); + if let Some(value) = self.simplify_operand(value) { + self.optimizations.terminators.insert(block, value); } } // FIXME: do this optimization for function calls @@ -578,6 +576,8 @@ struct OptimizationList<'tcx> { and_stars: FxHashSet, arrays_lengths: FxHashMap>, const_prop: FxHashMap>, + /// Terminators that get their Operand(s) turned into constants. + terminators: FxHashMap>, places: FxHashMap>, constants: FxHashMap, Const<'tcx>>, } diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 9d1f76313759b..652069ab7c17e 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -265,6 +265,7 @@ fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx generator::StateTransform, instcombine::InstCombine, + simplify_branches::SimplifyBranches::new("after-const-prop"), deaggregator::Deaggregator, copy_prop::CopyPropagation, remove_noop_landing_pads::RemoveNoopLandingPads, diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs index da10fcffb4c64..cc5af9489b4cf 100644 --- a/src/librustc_trans/mir/analyze.rs +++ b/src/librustc_trans/mir/analyze.rs @@ -128,7 +128,7 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> { _ => None, }; if let Some((def_id, args)) = check { - if Some(def_id) == self.cx.ccx.tcx().lang_items().box_free_fn() { + if Some(def_id) == self.fx.cx.tcx.lang_items().box_free_fn() { // box_free(x) shares with `drop x` the property that it // is not guaranteed to be statically dominated by the // definition of x, so x must always be in an alloca. diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index 6d2f9c6c97fb0..cc344fdf063a2 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -204,7 +204,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { let (otherwise, targets) = targets.split_last().unwrap(); let switch = bx.switch(discr.immediate(), llblock(self, *otherwise), values.len()); - let switch_llty = bcx.ccx.layout_of(switch_ty).immediate_llvm_type(bcx.ccx); + let switch_llty = bx.cx.layout_of(switch_ty).immediate_llvm_type(bx.cx); for (&value, target) in values.iter().zip(targets) { let llval = C_uint_big(switch_llty, value); let llbb = llblock(self, *target); diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 5050628b024db..be5cb311b9cc2 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -29,7 +29,7 @@ use type_of::LayoutLlvmExt; use type_::Type; use super::super::callee; -use super::MirContext; +use super::FunctionCx; fn to_const_int(value: ValueRef, t: Ty, tcx: TyCtxt) -> Option { match t.sty { @@ -135,15 +135,15 @@ pub fn const_scalar_checked_binop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } -pub fn primval_to_llvm(ccx: &CrateContext, +pub fn primval_to_llvm(cx: &CodegenCx, cv: PrimVal, scalar: &Scalar, llty: Type) -> ValueRef { - let bits = if scalar.is_bool() { 1 } else { scalar.value.size(ccx).bits() }; + let bits = if scalar.is_bool() { 1 } else { scalar.value.size(cx).bits() }; match cv { - PrimVal::Undef => C_undef(Type::ix(ccx, bits)), + PrimVal::Undef => C_undef(Type::ix(cx, bits)), PrimVal::Bytes(b) => { - let llval = C_uint_big(Type::ix(ccx, bits), b); + let llval = C_uint_big(Type::ix(cx, bits), b); if scalar.value == layout::Pointer { unsafe { llvm::LLVMConstIntToPtr(llval, llty.to_ref()) } } else { @@ -151,28 +151,28 @@ pub fn primval_to_llvm(ccx: &CrateContext, } }, PrimVal::Ptr(ptr) => { - let interpret_interner = ccx.tcx().interpret_interner.borrow(); + let interpret_interner = cx.tcx.interpret_interner.borrow(); if let Some(fn_instance) = interpret_interner.get_fn(ptr.alloc_id) { - callee::get_fn(ccx, fn_instance) + callee::get_fn(cx, fn_instance) } else { let static_ = interpret_interner.get_corresponding_static_def_id(ptr.alloc_id); let base_addr = if let Some(def_id) = static_ { - assert!(ccx.tcx().is_static(def_id).is_some()); - consts::get_static(ccx, def_id) + assert!(cx.tcx.is_static(def_id).is_some()); + consts::get_static(cx, def_id) } else if let Some(alloc) = interpret_interner.get_alloc(ptr.alloc_id) { - let init = global_initializer(ccx, alloc); + let init = global_initializer(cx, alloc); if alloc.mutable { - consts::addr_of_mut(ccx, init, alloc.align, "byte_str") + consts::addr_of_mut(cx, init, alloc.align, "byte_str") } else { - consts::addr_of(ccx, init, alloc.align, "byte_str") + consts::addr_of(cx, init, alloc.align, "byte_str") } } else { bug!("missing allocation {:?}", ptr.alloc_id); }; let llval = unsafe { llvm::LLVMConstInBoundsGEP( - consts::bitcast(base_addr, Type::i8p(ccx)), - &C_usize(ccx, ptr.offset), + consts::bitcast(base_addr, Type::i8p(cx)), + &C_usize(cx, ptr.offset), 1, ) }; if scalar.value != layout::Pointer { @@ -185,9 +185,9 @@ pub fn primval_to_llvm(ccx: &CrateContext, } } -pub fn global_initializer(ccx: &CrateContext, alloc: &Allocation) -> ValueRef { +pub fn global_initializer(cx: &CodegenCx, alloc: &Allocation) -> ValueRef { let mut llvals = Vec::with_capacity(alloc.relocations.len() + 1); - let layout = ccx.data_layout(); + let layout = cx.data_layout(); let pointer_size = layout.pointer_size.bytes() as usize; let mut next_offset = 0; @@ -195,28 +195,28 @@ pub fn global_initializer(ccx: &CrateContext, alloc: &Allocation) -> ValueRef { assert_eq!(offset as usize as u64, offset); let offset = offset as usize; if offset > next_offset { - llvals.push(C_bytes(ccx, &alloc.bytes[next_offset..offset])); + llvals.push(C_bytes(cx, &alloc.bytes[next_offset..offset])); } let ptr_offset = read_target_uint( layout.endian, &alloc.bytes[offset..(offset + pointer_size)], ).expect("global_initializer: could not read relocation pointer") as u64; llvals.push(primval_to_llvm( - ccx, + cx, PrimVal::Ptr(MemoryPointer { alloc_id, offset: ptr_offset }), &Scalar { value: layout::Primitive::Pointer, valid_range: 0..=!0 }, - Type::i8p(ccx) + Type::i8p(cx) )); next_offset = offset + pointer_size; } if alloc.bytes.len() >= next_offset { - llvals.push(C_bytes(ccx, &alloc.bytes[next_offset ..])); + llvals.push(C_bytes(cx, &alloc.bytes[next_offset ..])); } - C_struct(ccx, &llvals, true) + C_struct(cx, &llvals, true) } pub fn trans_static_initializer<'a, 'tcx>( @@ -224,39 +224,39 @@ pub fn trans_static_initializer<'a, 'tcx>( def_id: DefId) -> Result> { - let instance = ty::Instance::mono(ccx.tcx(), def_id); + let instance = ty::Instance::mono(cx.tcx, def_id); let cid = GlobalId { instance, promoted: None }; let param_env = ty::ParamEnv::empty(traits::Reveal::All); - ccx.tcx().const_eval(param_env.and(cid))?; + cx.tcx.const_eval(param_env.and(cid))?; - let alloc_id = ccx - .tcx() + let alloc_id = cx + .tcx .interpret_interner .borrow() .get_cached(def_id) .expect("global not cached"); - let alloc = ccx - .tcx() + let alloc = cx + .tcx .interpret_interner .borrow() .get_alloc(alloc_id) .expect("miri allocation never successfully created"); - Ok(global_initializer(ccx, alloc)) + Ok(global_initializer(cx, alloc)) } impl<'a, 'tcx> FunctionCx<'a, 'tcx> { fn const_to_miri_value( &mut self, - bcx: &Builder<'a, 'tcx>, + bx: &Builder<'a, 'tcx>, constant: &'tcx ty::Const<'tcx>, ) -> Result> { match constant.val { ConstVal::Unevaluated(def_id, ref substs) => { - let tcx = bcx.tcx(); + let tcx = bx.tcx(); let param_env = ty::ParamEnv::empty(traits::Reveal::All); let instance = ty::Instance::resolve(tcx, param_env, def_id, substs).unwrap(); let cid = GlobalId { @@ -264,7 +264,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { promoted: None, }; let c = tcx.const_eval(param_env.and(cid))?; - self.const_to_miri_value(bcx, c) + self.const_to_miri_value(bx, c) }, ConstVal::Value(miri_val) => Ok(miri_val), } @@ -272,7 +272,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { pub fn mir_constant_to_miri_value( &mut self, - bcx: &Builder<'a, 'tcx>, + bx: &Builder<'a, 'tcx>, constant: &mir::Constant<'tcx>, ) -> Result> { match constant.literal { @@ -282,22 +282,22 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { instance: self.instance, promoted: Some(index), }; - bcx.tcx().const_eval(param_env.and(cid)) + bx.tcx().const_eval(param_env.and(cid)) } mir::Literal::Value { value } => { Ok(self.monomorphize(&value)) } - }.and_then(|c| self.const_to_miri_value(bcx, c)) + }.and_then(|c| self.const_to_miri_value(bx, c)) } // Old version of trans_constant now used just for SIMD shuffle pub fn remove_me_shuffle_indices(&mut self, - bcx: &Builder<'a, 'tcx>, + bx: &Builder<'a, 'tcx>, constant: &mir::Constant<'tcx>) -> (ValueRef, Ty<'tcx>) { - let layout = bcx.ccx.layout_of(constant.ty); - self.mir_constant_to_miri_value(bcx, constant) + let layout = bx.cx.layout_of(constant.ty); + self.mir_constant_to_miri_value(bx, constant) .and_then(|c| { let llval = match c { MiriValue::ByVal(val) => { @@ -305,7 +305,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { layout::Abi::Scalar(ref x) => x, _ => bug!("from_const: invalid ByVal layout: {:#?}", layout) }; - primval_to_llvm(bcx.ccx, val, scalar, layout.immediate_llvm_type(bcx.ccx)) + primval_to_llvm(bx.cx, val, scalar, layout.immediate_llvm_type(bx.cx)) }, MiriValue::ByValPair(a_val, b_val) => { let (a_scalar, b_scalar) = match layout.abi { @@ -313,18 +313,18 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { _ => bug!("from_const: invalid ByValPair layout: {:#?}", layout) }; let a_llval = primval_to_llvm( - bcx.ccx, + bx.cx, a_val, a_scalar, - layout.scalar_pair_element_llvm_type(bcx.ccx, 0), + layout.scalar_pair_element_llvm_type(bx.cx, 0), ); let b_llval = primval_to_llvm( - bcx.ccx, + bx.cx, b_val, b_scalar, - layout.scalar_pair_element_llvm_type(bcx.ccx, 1), + layout.scalar_pair_element_llvm_type(bx.cx, 1), ); - C_struct(bcx.ccx, &[a_llval, b_llval], false) + C_struct(bx.cx, &[a_llval, b_llval], false) }, MiriValue::ByRef(..) => { let field_ty = constant.ty.builtin_index().unwrap(); @@ -334,7 +334,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { }; let values: Result, _> = (0..fields).map(|field| { let field = const_val_field( - bcx.tcx(), + bx.tcx(), ty::ParamEnv::empty(traits::Reveal::All), self.instance, None, @@ -344,29 +344,29 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { )?; match field.val { ConstVal::Value(MiriValue::ByVal(prim)) => { - let layout = bcx.ccx.layout_of(field_ty); + let layout = bx.cx.layout_of(field_ty); let scalar = match layout.abi { layout::Abi::Scalar(ref x) => x, _ => bug!("from_const: invalid ByVal layout: {:#?}", layout) }; Ok(primval_to_llvm( - bcx.ccx, prim, scalar, - layout.immediate_llvm_type(bcx.ccx), + bx.cx, prim, scalar, + layout.immediate_llvm_type(bx.cx), )) }, other => bug!("simd shuffle field {:?}, {}", other, constant.ty), } }).collect(); - C_struct(bcx.ccx, &values?, false) + C_struct(bx.cx, &values?, false) }, }; Ok((llval, constant.ty)) }) .unwrap_or_else(|e| { - e.report(bcx.tcx(), constant.span, "shuffle_indices"); + e.report(bx.tcx(), constant.span, "shuffle_indices"); // We've errored, so we don't have to produce working code. let ty = self.monomorphize(&constant.ty); - let llty = bcx.ccx.layout_of(ty).llvm_type(bcx.ccx); + let llty = bx.cx.layout_of(ty).llvm_type(bx.cx); (C_undef(llty), ty) }) } diff --git a/src/librustc_trans/mir/operand.rs b/src/librustc_trans/mir/operand.rs index 01cf324124c4f..a66528858e14a 100644 --- a/src/librustc_trans/mir/operand.rs +++ b/src/librustc_trans/mir/operand.rs @@ -92,14 +92,14 @@ impl<'a, 'tcx> OperandRef<'tcx> { } } - pub fn from_const(bcx: &Builder<'a, 'tcx>, + pub fn from_const(bx: &Builder<'a, 'tcx>, miri_val: MiriValue, ty: ty::Ty<'tcx>) -> Result, ConstEvalErr<'tcx>> { - let layout = bcx.ccx.layout_of(ty); + let layout = bx.cx.layout_of(ty); if layout.is_zst() { - return Ok(OperandRef::new_zst(bcx.ccx, layout)); + return Ok(OperandRef::new_zst(bx.cx, layout)); } let val = match miri_val { @@ -109,10 +109,10 @@ impl<'a, 'tcx> OperandRef<'tcx> { _ => bug!("from_const: invalid ByVal layout: {:#?}", layout) }; let llval = primval_to_llvm( - bcx.ccx, + bx.cx, x, scalar, - layout.immediate_llvm_type(bcx.ccx), + layout.immediate_llvm_type(bx.cx), ); OperandValue::Immediate(llval) }, @@ -122,16 +122,16 @@ impl<'a, 'tcx> OperandRef<'tcx> { _ => bug!("from_const: invalid ByValPair layout: {:#?}", layout) }; let a_llval = primval_to_llvm( - bcx.ccx, + bx.cx, a, a_scalar, - layout.scalar_pair_element_llvm_type(bcx.ccx, 0), + layout.scalar_pair_element_llvm_type(bx.cx, 0), ); let b_llval = primval_to_llvm( - bcx.ccx, + bx.cx, b, b_scalar, - layout.scalar_pair_element_llvm_type(bcx.ccx, 1), + layout.scalar_pair_element_llvm_type(bx.cx, 1), ); OperandValue::Pair(a_llval, b_llval) }, @@ -141,12 +141,12 @@ impl<'a, 'tcx> OperandRef<'tcx> { valid_range: 0..=!0 }; let ptr = primval_to_llvm( - bcx.ccx, + bx.cx, ptr.into_inner_primval(), &scalar, - layout.llvm_type(bcx.ccx).ptr_to(), + layout.llvm_type(bx.cx).ptr_to(), ); - return Ok(PlaceRef::new_sized(ptr, layout, align).load(bcx)); + return Ok(PlaceRef::new_sized(ptr, layout, align).load(bx)); }, }; diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index b9e4ce661d0c4..2372153963a1c 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -34,6 +34,7 @@ use rustc::middle::privacy::AccessLevels; use rustc::middle::resolve_lifetime as rl; use rustc::ty::fold::TypeFolder; use rustc::middle::lang_items; +use rustc::mir::interpret::GlobalId; use rustc::hir::{self, HirVec}; use rustc::hir::def::{self, Def, CtorKind}; use rustc::hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX, LOCAL_CRATE}; @@ -2510,14 +2511,24 @@ impl Clean for hir::Ty { ty: cx.tcx.types.usize }) }); - let n = if let ConstVal::Unevaluated(def_id, _) = n.val { - if let Some(node_id) = cx.tcx.hir.as_local_node_id(def_id) { - print_const_expr(cx, cx.tcx.hir.body_owned_by(node_id)) - } else { - inline::print_inlined_const(cx, def_id) - } - } else { - format!("{:?}", n) + let n = match n.val { + ConstVal::Unevaluated(def_id, _) => { + if let Some(node_id) = cx.tcx.hir.as_local_node_id(def_id) { + print_const_expr(cx, cx.tcx.hir.body_owned_by(node_id)) + } else { + inline::print_inlined_const(cx, def_id) + } + }, + ConstVal::Value(val) => { + let mut s = String::new(); + ::rustc::mir::print_miri_value(val, n.ty, &mut s).unwrap(); + // array lengths are obviously usize + if s.ends_with("usize") { + let n = s.len() - "usize".len(); + s.truncate(n); + } + s + }, }; Array(box ty.clean(cx), n) }, @@ -2645,14 +2656,24 @@ impl<'tcx> Clean for Ty<'tcx> { n = new_n; } }; - let n = if let ConstVal::Unevaluated(def_id, _) = n.val { - if let Some(node_id) = cx.tcx.hir.as_local_node_id(def_id) { - print_const_expr(cx, cx.tcx.hir.body_owned_by(node_id)) - } else { - inline::print_inlined_const(cx, def_id) - } - } else { - format!("{:?}", n) + let n = match n.val { + ConstVal::Unevaluated(def_id, _) => { + if let Some(node_id) = cx.tcx.hir.as_local_node_id(def_id) { + print_const_expr(cx, cx.tcx.hir.body_owned_by(node_id)) + } else { + inline::print_inlined_const(cx, def_id) + } + }, + ConstVal::Value(val) => { + let mut s = String::new(); + ::rustc::mir::print_miri_value(val, n.ty, &mut s).unwrap(); + // array lengths are obviously usize + if s.ends_with("usize") { + let n = s.len() - "usize".len(); + s.truncate(n); + } + s + }, }; Array(box ty.clean(cx), n) } diff --git a/src/test/compile-fail/const-err2.rs b/src/test/compile-fail/const-err2.rs index a0648993aac74..09983f179266d 100644 --- a/src/test/compile-fail/const-err2.rs +++ b/src/test/compile-fail/const-err2.rs @@ -19,10 +19,12 @@ fn black_box(_: T) { fn main() { let a = -std::i8::MIN; //~^ ERROR const_err - //~| ERROR const_err let b = 200u8 + 200u8 + 200u8; + //~^ ERROR const_err let c = 200u8 * 4; + //~^ ERROR const_err let d = 42u8 - (42u8 + 1); + //~^ ERROR const_err let _e = [5u8][1]; black_box(a); black_box(b); diff --git a/src/test/compile-fail/const-err3.rs b/src/test/compile-fail/const-err3.rs index 636537d1df250..9656af6002442 100644 --- a/src/test/compile-fail/const-err3.rs +++ b/src/test/compile-fail/const-err3.rs @@ -18,9 +18,10 @@ fn black_box(_: T) { fn main() { let b = 200u8 + 200u8 + 200u8; //~^ ERROR const_err - //~| ERROR const_err let c = 200u8 * 4; + //~^ ERROR const_err let d = 42u8 - (42u8 + 1); + //~^ ERROR const_err let _e = [5u8][1]; black_box(b); black_box(c); diff --git a/src/test/run-pass/ctfe/references.rs b/src/test/run-pass/ctfe/references.rs new file mode 100644 index 0000000000000..ad7dbeb79c721 --- /dev/null +++ b/src/test/run-pass/ctfe/references.rs @@ -0,0 +1,34 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +const FOO: &[u8] = b"foo"; +const BAR: &[u8] = &[1, 2, 3]; + +const BOO: &i32 = &42; + +fn main() { + match &[1u8, 2, 3] as &[u8] { + FOO => panic!("a"), + BAR => println!("b"), + _ => panic!("c"), + } + + match b"foo" as &[u8] { + FOO => println!("a"), + BAR => panic!("b"), + _ => panic!("c"), + } + + match &43 { + &42 => panic!(), + BOO => panic!(), + _ => println!("d"), + } +} From 9d9a69d7fae610551508e29af79826a77f9431c5 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 22 Jan 2018 10:21:22 +0100 Subject: [PATCH 015/110] Print whether the stackframe is for a promoted --- src/librustc_mir/interpret/const_eval.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index 2dcad403f522a..596baabba1878 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -132,7 +132,9 @@ fn eval_body_and_ecx<'a, 'mir, 'tcx>( }; let cleanup = StackPopCleanup::MarkStatic(mutability); let name = ty::tls::with(|tcx| tcx.item_path_str(cid.instance.def_id())); - trace!("const_eval: pushing stack frame for global: {}", name); + let prom = cid.promoted.map_or(String::new(), |p| format!("::promoted[{:?}]", p)); + trace!("const_eval: pushing stack frame for global: {}{}", name, prom); + assert!(mir.arg_count == 0); ecx.push_stack_frame( cid.instance, mir.span, From 4caf6a92f3a50b4d8061c20b6b013e5a3fb8dfe4 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 22 Jan 2018 10:21:49 +0100 Subject: [PATCH 016/110] Additional sanity assertion --- src/librustc_mir/interpret/memory.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index eebd48f7bb95c..160786ea021f9 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -518,7 +518,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { ) -> EvalResult<'tcx> { match self.alloc_kind.get(&alloc) { // do not go into statics - None => Ok(()), + None => Ok(()), // just locals and machine allocs Some(_) => self.mark_static_initialized(alloc, mutability), } @@ -555,6 +555,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { for &alloc in alloc.relocations.values() { self.mark_inner_allocation_initialized(alloc, mutability)?; } + } else { + bug!("no allocation found for {:?}", alloc_id); } Ok(()) } From f14e746b2fb11c638904247ef6764f83448de641 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 23 Jan 2018 16:36:55 +0100 Subject: [PATCH 017/110] Enable const prop to also evaluate constants, not just literals --- src/librustc_mir/transform/instcombine.rs | 64 ++++++++++++++++------- 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/src/librustc_mir/transform/instcombine.rs b/src/librustc_mir/transform/instcombine.rs index 6079e1929b578..25787608e39ca 100644 --- a/src/librustc_mir/transform/instcombine.rs +++ b/src/librustc_mir/transform/instcombine.rs @@ -11,7 +11,7 @@ //! Performs various peephole optimizations. use rustc::mir::{Constant, Literal, Location, Place, Mir, Operand, ProjectionElem, Rvalue, Local}; -use rustc::mir::{NullOp, StatementKind, Statement, BasicBlock}; +use rustc::mir::{NullOp, StatementKind, Statement, BasicBlock, LocalKind}; use rustc::mir::{SourceInfo, ARGUMENT_VISIBILITY_SCOPE, TerminatorKind}; use rustc::mir::visit::{MutVisitor, Visitor, TyContext}; use rustc::middle::const_val::ConstVal; @@ -34,6 +34,7 @@ impl MirPass for InstCombine { tcx: TyCtxt<'a, 'tcx, 'tcx>, source: MirSource, mir: &mut Mir<'tcx>) { + trace!("InstCombine starting for {:?}", source.def_id); // First, find optimization opportunities. This is done in a pre-pass to keep the MIR // read-only so that we can do global analyses on the MIR in the process (e.g. @@ -46,6 +47,7 @@ impl MirPass for InstCombine { // Then carry out those optimizations. MutVisitor::visit_mir(&mut InstCombineVisitor { optimizations, tcx }, mir); + trace!("InstCombine done for {:?}", source.def_id); } } @@ -199,7 +201,6 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { ConstVal::Value(v) => Some((v, value.ty, c.span)), ConstVal::Unevaluated(did, substs) => { let param_env = self.tcx.param_env(self.source.def_id); - let span = self.tcx.def_span(did); let instance = Instance::resolve( self.tcx, param_env, @@ -211,7 +212,7 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { promoted: None, }; let (value, _, ty) = eval_body(self.tcx, cid, param_env)?; - let val = (value, ty, span); + let val = (value, ty, c.span); trace!("evaluated {:?} to {:?}", c, val); self.optimizations.constants.insert(c.clone(), val); Some(val) @@ -269,7 +270,24 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { span: Span, ) -> Option> { match *rvalue { - Rvalue::Use(ref op) => self.simplify_operand(op), + // No need to overwrite an already evaluated constant + Rvalue::Use(Operand::Constant(box Constant { + literal: Literal::Value { + value: &ty::Const { + val: ConstVal::Value(_), + .. + }, + }, + .. + })) => None, + // This branch exists for the sanity type check + Rvalue::Use(Operand::Constant(ref c)) => { + assert_eq!(c.ty, place_ty); + self.eval_constant(c) + }, + Rvalue::Use(ref op) => { + self.eval_operand(op) + }, Rvalue::Repeat(..) | Rvalue::Ref(..) | Rvalue::Cast(..) | @@ -392,6 +410,17 @@ impl ConstPropVisitor { cpv.visit_mir(mir); cpv.can_const_prop } + + fn is_our_local(&mut self, mut place: &Place) -> bool { + while let Place::Projection(ref proj) = place { + place = &proj.base; + } + if let Place::Local(local) = *place { + local == self.local + } else { + false + } + } } impl<'tcx> Visitor<'tcx> for ConstPropVisitor { @@ -403,9 +432,9 @@ impl<'tcx> Visitor<'tcx> for ConstPropVisitor { ) { self.super_statement(block, statement, location); match statement.kind { - StatementKind::SetDiscriminant { place: Place::Local(local), .. } | - StatementKind::Assign(Place::Local(local), _) => { - if local == self.local { + StatementKind::SetDiscriminant { ref place, .. } | + StatementKind::Assign(ref place, _) => { + if self.is_our_local(place) { if self.found_assignment { self.can_const_prop = false; } else { @@ -415,15 +444,13 @@ impl<'tcx> Visitor<'tcx> for ConstPropVisitor { }, StatementKind::InlineAsm { ref outputs, .. } => { for place in outputs { - if let Place::Local(local) = *place { - if local == self.local { - if self.found_assignment { - self.can_const_prop = false; - } else { - self.found_assignment = true - } - return; + if self.is_our_local(place) { + if self.found_assignment { + self.can_const_prop = false; + } else { + self.found_assignment = true } + return; } } } @@ -432,8 +459,8 @@ impl<'tcx> Visitor<'tcx> for ConstPropVisitor { } fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { self.super_rvalue(rvalue, location); - if let Rvalue::Ref(_, _, Place::Local(local)) = *rvalue { - if local == self.local { + if let Rvalue::Ref(_, _, ref place) = *rvalue { + if self.is_our_local(place) { self.can_const_prop = false; } } @@ -517,12 +544,13 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for OptimizationFinder<'b, 'a, 'tcx> { if let Some(value) = self.const_prop(rval, place_ty, span) { self.optimizations.const_prop.insert(location, value); if let Place::Local(local) = *place { - if !self.mir.local_decls[local].is_user_variable + if self.mir.local_kind(local) == LocalKind::Temp && ConstPropVisitor::check(local, self.mir) { trace!("storing {:?} to {:?}", value, local); assert!(self.optimizations.places.insert(local, value).is_none()); } } + return; } } self.super_statement(block, statement, location); From d3e2f48c8cb3ee32fecf5a7a525fa51d10ab4035 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 23 Jan 2018 16:37:39 +0100 Subject: [PATCH 018/110] More const eval sanity checks (invalid slice fat pointers) --- src/librustc_mir/build/matches/test.rs | 36 +--------------------- src/librustc_mir/interpret/eval_context.rs | 13 ++++++-- 2 files changed, 11 insertions(+), 38 deletions(-) diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index 067041b14f583..3b9fbc5c867a1 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -173,39 +173,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } } - /// Convert a byte array or byte slice to a byte slice. - fn to_slice_operand(&mut self, - block: BasicBlock, - source_info: SourceInfo, - operand: Operand<'tcx>) - -> Operand<'tcx> - { - let tcx = self.hir.tcx(); - let ty = operand.ty(&self.local_decls, tcx); - debug!("to_slice_operand({:?}, {:?}: {:?})", block, operand, ty); - match ty.sty { - ty::TyRef(region, mt) => match mt.ty.sty { - ty::TyArray(ety, _) => { - let ty = tcx.mk_imm_ref(region, tcx.mk_slice(ety)); - let temp = self.temp(ty, source_info.span); - self.cfg.push_assign(block, source_info, &temp, - Rvalue::Cast(CastKind::Unsize, operand, ty)); - Operand::Move(temp) - } - ty::TySlice(_) => operand, - _ => { - span_bug!(source_info.span, - "bad operand {:?}: {:?} to `to_slice_operand`", operand, ty) - } - } - _ => { - span_bug!(source_info.span, - "bad operand {:?}: {:?} to `to_slice_operand`", operand, ty) - } - } - - } - /// Generates the code to perform a test. pub fn perform_test(&mut self, block: BasicBlock, @@ -292,8 +259,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { ret } - TestKind::Eq { value, ty } => { - let tcx = self.hir.tcx(); + TestKind::Eq { value, mut ty } => { let mut val = Operand::Copy(place.clone()); let mut expect = self.literal_operand(test.span, ty, Literal::Value { value diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 7dafe846a334b..dc66365bccb57 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -1263,9 +1263,16 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M ty::TyDynamic(..) => Ok(p.to_value_with_vtable( self.memory.read_ptr_sized_unsigned(extra, ptr_align)?.to_ptr()?, )), - ty::TySlice(..) | ty::TyStr => Ok( - p.to_value_with_len(self.memory.read_ptr_sized_unsigned(extra, ptr_align)?.to_bytes()? as u64), - ), + ty::TySlice(..) | ty::TyStr => { + match p.primval { + PrimVal::Bytes(b) => bug!("slice ptr: {:x}", b), + PrimVal::Undef => bug!("undef slice ptr"), + _ => {}, + } + Ok( + p.to_value_with_len(self.memory.read_ptr_sized_unsigned(extra, ptr_align)?.to_bytes()? as u64), + ) + }, _ => bug!("unsized primval ptr read from {:?}", pointee_ty), } } From 34772aad63f9463f76ed5b16a21ae1d5a9729c6d Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 25 Jan 2018 08:06:53 +0100 Subject: [PATCH 019/110] Allow writing mutable statics in miri by adding them to the Machine --- src/librustc_mir/interpret/const_eval.rs | 24 +++++++++++++++++- src/librustc_mir/interpret/eval_context.rs | 27 +++++++++----------- src/librustc_mir/interpret/machine.rs | 9 ++++++- src/librustc_mir/interpret/place.rs | 29 +++------------------- 4 files changed, 47 insertions(+), 42 deletions(-) diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index 596baabba1878..2a9e1cb57e4ca 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -1,6 +1,7 @@ use rustc::hir; -use rustc::middle::const_val::{ConstEvalErr, ConstVal}; +use rustc::middle::const_val::{ConstEvalErr, ConstVal, ErrKind}; use rustc::middle::const_val::ErrKind::{TypeckError, CheckMatchError}; +use rustc::traits; use rustc::mir; use rustc::ty::{self, TyCtxt, Ty, Instance}; use rustc::ty::layout::{self, LayoutOf}; @@ -326,6 +327,27 @@ impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator { Ok(false) } + fn init_static<'a>( + ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, + cid: GlobalId<'tcx>, + ) -> EvalResult<'tcx, AllocId> { + let param_env = ty::ParamEnv::empty(traits::Reveal::All); + // ensure the static is computed + if let Err(err) = ecx.tcx.const_eval(param_env.and(cid)) { + match err.kind { + ErrKind::Miri(miri) => return Err(miri), + ErrKind::TypeckError => return err!(TypeckError), + other => bug!("const eval returned {:?}", other), + } + }; + Ok(ecx + .tcx + .interpret_interner + .borrow() + .get_cached(cid.instance.def_id()) + .expect("uncached static")) + } + fn box_alloc<'a>( _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, _ty: Ty<'tcx>, diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index dc66365bccb57..1c60cda095c43 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -1264,14 +1264,11 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M self.memory.read_ptr_sized_unsigned(extra, ptr_align)?.to_ptr()?, )), ty::TySlice(..) | ty::TyStr => { - match p.primval { - PrimVal::Bytes(b) => bug!("slice ptr: {:x}", b), - PrimVal::Undef => bug!("undef slice ptr"), - _ => {}, - } - Ok( - p.to_value_with_len(self.memory.read_ptr_sized_unsigned(extra, ptr_align)?.to_bytes()? as u64), - ) + let len = self + .memory + .read_ptr_sized_unsigned(extra, ptr_align)? + .to_bytes()?; + Ok(p.to_value_with_len(len as u64)) }, _ => bug!("unsized primval ptr read from {:?}", pointee_ty), } @@ -1621,16 +1618,16 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M block.terminator().source_info.span }); trace!("reporting const eval failure at {:?}", span); - let node_id = self - .stack() - .iter() - .rev() - .filter_map(|frame| self.tcx.hir.as_local_node_id(frame.instance.def_id())) - .next() - .expect("some part of a failing const eval must be local"); let mut err = if as_err { ::rustc::middle::const_val::struct_error(self.tcx, span, "constant evaluation error") } else { + let node_id = self + .stack() + .iter() + .rev() + .filter_map(|frame| self.tcx.hir.as_local_node_id(frame.instance.def_id())) + .next() + .expect("some part of a failing const eval must be local"); self.tcx.struct_span_lint_node( ::rustc::lint::builtin::CONST_ERR, node_id, diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs index d5e57d3317c55..5af0a053e9239 100644 --- a/src/librustc_mir/interpret/machine.rs +++ b/src/librustc_mir/interpret/machine.rs @@ -2,7 +2,7 @@ //! This separation exists to ensure that no fancy miri features like //! interpreting common C functions leak into CTFE. -use rustc::mir::interpret::{AllocId, EvalResult, PrimVal, MemoryPointer, AccessKind}; +use rustc::mir::interpret::{AllocId, EvalResult, PrimVal, MemoryPointer, AccessKind, GlobalId}; use super::{EvalContext, Place, ValTy, Memory}; use rustc::mir; @@ -66,6 +66,13 @@ pub trait Machine<'mir, 'tcx>: Sized { _mutability: Mutability, ) -> EvalResult<'tcx, bool>; + /// Called when requiring a pointer to a static. Non const eval can + /// create a mutable memory location for `static mut` + fn init_static<'a>( + ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, + cid: GlobalId<'tcx>, + ) -> EvalResult<'tcx, AllocId>; + /// Heap allocations via the `box` keyword /// /// Returns a pointer to the allocated memory diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 349ac63055992..12725fb6dff75 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -1,13 +1,11 @@ use rustc::mir; use rustc::ty::{self, Ty}; use rustc::ty::layout::{self, Align, LayoutOf, TyLayout}; -use rustc::traits; use rustc_data_structures::indexed_vec::Idx; use rustc::mir::interpret::{GlobalId, Value, PrimVal, EvalResult, Pointer, MemoryPointer}; use super::{EvalContext, Machine, ValTy}; use interpret::memory::HasMemory; -use rustc::middle::const_val::ErrKind; #[derive(Copy, Clone, Debug)] pub enum Place { @@ -105,14 +103,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { Local(mir::RETURN_PLACE) => err!(ReadFromReturnPointer), // Directly reading a local will always succeed Local(local) => self.frame().get_local(local).map(Some), - // Directly reading a static will always succeed - Static(ref static_) => { - let instance = ty::Instance::mono(self.tcx, static_.def_id); - self.read_global_as_value(GlobalId { - instance, - promoted: None, - }, self.place_ty(place)).map(Some) - } + // No fast path for statics. Reading from statics is rare and would require another + // Machine function to handle differently in miri. + Static(_) => Ok(None), Projection(ref proj) => self.try_read_place_projection(proj), } } @@ -219,21 +212,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { instance, promoted: None }; - let param_env = ty::ParamEnv::empty(traits::Reveal::All); - // ensure the static is computed - if let Err(err) = self.tcx.const_eval(param_env.and(cid)) { - match err.kind { - ErrKind::Miri(miri) => return Err(miri), - ErrKind::TypeckError => return err!(TypeckError), - other => bug!("const eval returned {:?}", other), - } - }; - let alloc = self - .tcx - .interpret_interner - .borrow() - .get_cached(static_.def_id) - .expect("uncached static"); + let alloc = Machine::init_static(self, cid)?; Place::Ptr { ptr: MemoryPointer::new(alloc, 0).into(), align: layout.align, From 1d438f87e7f2a7f61195a9b614f11449d06aaa85 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 25 Jan 2018 09:04:00 +0100 Subject: [PATCH 020/110] Allow tools (e.g. miri) to enable rust logging --- src/librustc_driver/lib.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 4953b85f994b3..8c0e89716cffd 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -1574,8 +1574,14 @@ pub fn diagnostics_registry() -> errors::registry::Registry { Registry::new(&all_errors) } -pub fn main() { +/// This allows tools to enable rust logging without having to magically match rustc's +/// log crate version +pub fn init_rustc_env_logger() { env_logger::init(); +} + +pub fn main() { + init_rustc_env_logger(); let result = run(|| { let args = env::args_os().enumerate() .map(|(i, arg)| arg.into_string().unwrap_or_else(|arg| { From 37d8b9a86c7352f99c4f65987bd881d090fdee00 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 25 Jan 2018 12:12:07 +0100 Subject: [PATCH 021/110] Don't borrow the interpret_interner for anything but a direct function call --- src/librustc/ty/maps/on_disk_cache.rs | 7 +++---- src/librustc_metadata/encoder.rs | 9 ++++----- src/librustc_mir/interpret/memory.rs | 5 ++--- src/librustc_mir/monomorphize/collector.rs | 5 ++--- src/librustc_trans/mir/constant.rs | 12 ++++++++---- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/librustc/ty/maps/on_disk_cache.rs b/src/librustc/ty/maps/on_disk_cache.rs index 7f126976a2ec1..84e566e57e626 100644 --- a/src/librustc/ty/maps/on_disk_cache.rs +++ b/src/librustc/ty/maps/on_disk_cache.rs @@ -796,15 +796,14 @@ impl<'enc, 'a, 'tcx, E> SpecializedEncoder for CacheEncoder< // cache the allocation shorthand now, because the allocation itself might recursively // 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) { + if let Some(alloc) = self.tcx.interpret_interner.borrow().get_alloc(*alloc_id) { trace!("encoding {:?} with {:#?}", alloc_id, alloc); usize::max_value().encode(self)?; alloc.encode(self)?; - interpret_interner + self.tcx.interpret_interner.borrow() .get_corresponding_static_def_id(*alloc_id) .encode(self)?; - } else if let Some(fn_instance) = interpret_interner.get_fn(*alloc_id) { + } else if let Some(fn_instance) = self.tcx.interpret_interner.borrow().get_fn(*alloc_id) { trace!("encoding {:?} with {:#?}", alloc_id, fn_instance); (usize::max_value() - 1).encode(self)?; fn_instance.encode(self)?; diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 71be3f2875928..c74063c66bdfa 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -205,15 +205,14 @@ impl<'a, 'tcx> SpecializedEncoder for EncodeContext<'a, 'tcx // cache the allocation shorthand now, because the allocation itself might recursively // 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) { + if let Some(alloc) = self.tcx.interpret_interner.borrow().get_alloc(*alloc_id) { trace!("encoding {:?} with {:#?}", alloc_id, alloc); usize::max_value().encode(self)?; alloc.encode(self)?; - interpret_interner + self.tcx.interpret_interner.borrow() .get_corresponding_static_def_id(*alloc_id) .encode(self)?; - } else if let Some(fn_instance) = interpret_interner.get_fn(*alloc_id) { + } else if let Some(fn_instance) = self.tcx.interpret_interner.borrow().get_fn(*alloc_id) { trace!("encoding {:?} with {:#?}", alloc_id, fn_instance); (usize::max_value() - 1).encode(self)?; fn_instance.encode(self)?; @@ -1155,7 +1154,7 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { _ => None, }, mir: match item.node { - hir::ItemStatic(..) if self.tcx.sess.opts.debugging_opts.always_encode_mir => { + hir::ItemStatic(..) => { self.encode_optimized_mir(def_id) } hir::ItemConst(..) => self.encode_optimized_mir(def_id), diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 160786ea021f9..a246898873c5c 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -375,11 +375,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { None => match self.uninitialized_statics.get(&id) { Some(a) => (a, " (static in the process of initialization)".to_owned()), None => { - let int = self.tcx.interpret_interner.borrow(); // static alloc? - match int.get_alloc(id) { + match self.tcx.interpret_interner.borrow().get_alloc(id) { Some(a) => (a, "(immutable)".to_owned()), - None => if let Some(func) = int.get_fn(id) { + None => if let Some(func) = self.tcx.interpret_interner.borrow().get_fn(id) { trace!("{} {}", msg, func); continue; } else { diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index 0a8fd022dd1ac..35c561e02e07e 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -1117,13 +1117,12 @@ fn collect_miri<'a, 'tcx>( alloc_id: AllocId, output: &mut Vec>, ) { - let interpret_interner = tcx.interpret_interner.borrow(); - if let Some(alloc) = interpret_interner.get_alloc(alloc_id) { + if let Some(alloc) = tcx.interpret_interner.borrow().get_alloc(alloc_id) { trace!("collecting {:?} with {:#?}", alloc_id, alloc); for &inner in alloc.relocations.values() { collect_miri(tcx, inner, output); } - } else if let Some(fn_instance) = interpret_interner.get_fn(alloc_id) { + } else if let Some(fn_instance) = tcx.interpret_interner.borrow().get_fn(alloc_id) { if should_monomorphize_locally(tcx, &fn_instance) { trace!("collecting {:?} with {:#?}", alloc_id, fn_instance); output.push(create_fn_mono_item(fn_instance)); diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index be5cb311b9cc2..1ca9bd81893aa 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -151,15 +151,19 @@ pub fn primval_to_llvm(cx: &CodegenCx, } }, PrimVal::Ptr(ptr) => { - let interpret_interner = cx.tcx.interpret_interner.borrow(); - if let Some(fn_instance) = interpret_interner.get_fn(ptr.alloc_id) { + if let Some(fn_instance) = cx.tcx.interpret_interner.borrow().get_fn(ptr.alloc_id) { callee::get_fn(cx, fn_instance) } else { - let static_ = interpret_interner.get_corresponding_static_def_id(ptr.alloc_id); + let static_ = cx + .tcx + .interpret_interner + .borrow() + .get_corresponding_static_def_id(ptr.alloc_id); let base_addr = if let Some(def_id) = static_ { assert!(cx.tcx.is_static(def_id).is_some()); consts::get_static(cx, def_id) - } else if let Some(alloc) = interpret_interner.get_alloc(ptr.alloc_id) { + } else if let Some(alloc) = cx.tcx.interpret_interner.borrow() + .get_alloc(ptr.alloc_id) { let init = global_initializer(cx, alloc); if alloc.mutable { consts::addr_of_mut(cx, init, alloc.align, "byte_str") From d26ebec81ce5d450f1bf3065ae990e6b1ca1c551 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 25 Jan 2018 12:59:24 +0100 Subject: [PATCH 022/110] Hide the RefCell inside InterpretInterner It was too easy to get this wrong --- src/librustc/ich/impls_ty.rs | 9 +-- src/librustc/mir/mod.rs | 1 - src/librustc/ty/context.rs | 58 +++++++++++-------- src/librustc/ty/maps/on_disk_cache.rs | 15 +++-- src/librustc_metadata/decoder.rs | 11 ++-- src/librustc_metadata/encoder.rs | 6 +- .../borrow_check/nll/type_check/mod.rs | 3 +- src/librustc_mir/const_eval/_match.rs | 4 -- src/librustc_mir/interpret/const_eval.rs | 11 ++-- src/librustc_mir/interpret/eval_context.rs | 1 - src/librustc_mir/interpret/memory.rs | 25 ++++---- src/librustc_mir/interpret/place.rs | 1 - src/librustc_mir/monomorphize/collector.rs | 4 +- src/librustc_trans/mir/constant.rs | 7 +-- 14 files changed, 72 insertions(+), 84 deletions(-) diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index a92d9b16612a2..54297a1fc398f 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -376,18 +376,15 @@ impl<'a> HashStable> for mir::interpret::AllocId { ) { ty::tls::with_opt(|tcx| { let tcx = tcx.expect("can't hash AllocIds during hir lowering"); - let interner = tcx - .interpret_interner - .borrow(); - if let Some(def_id) = interner.get_corresponding_static_def_id(*self) { + if let Some(def_id) = tcx.interpret_interner.get_corresponding_static_def_id(*self) { 0.hash_stable(hcx, hasher); // statics are unique via their DefId def_id.hash_stable(hcx, hasher); - } else if let Some(alloc) = interner.get_alloc(*self) { + } else if let Some(alloc) = tcx.interpret_interner.get_alloc(*self) { // not a static, can't be recursive, hash the allocation 1.hash_stable(hcx, hasher); alloc.hash_stable(hcx, hasher); - } else if let Some(inst) = interner.get_fn(*self) { + } else if let Some(inst) = tcx.interpret_interner.get_fn(*self) { 2.hash_stable(hcx, hasher); inst.hash_stable(hcx, hasher); } else { diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 8da3a125d3ff5..24f4a74b3ac38 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -1897,7 +1897,6 @@ pub fn print_miri_value(value: Value, ty: Ty, f: &mut W) -> fmt::Resul ty::tls::with(|tcx| { let alloc = tcx .interpret_interner - .borrow() .get_alloc(ptr.alloc_id); if let Some(alloc) = alloc { assert_eq!(len as usize as u128, len); diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 3ec3f665db9c9..a4e5f725837de 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -869,7 +869,7 @@ pub struct GlobalCtxt<'tcx> { stability_interner: RefCell>, - pub interpret_interner: RefCell>, + pub interpret_interner: InterpretInterner<'tcx>, layout_interner: RefCell>, @@ -893,6 +893,11 @@ pub struct GlobalCtxt<'tcx> { /// Everything needed to efficiently work with interned allocations #[derive(Debug, Default)] pub struct InterpretInterner<'tcx> { + inner: RefCell>, +} + +#[derive(Debug, Default)] +struct InterpretInternerInner<'tcx> { /// Stores the value of constants (and deduplicates the actual memory) allocs: FxHashSet<&'tcx interpret::Allocation>, @@ -925,14 +930,15 @@ pub struct InterpretInterner<'tcx> { } impl<'tcx> InterpretInterner<'tcx> { - pub fn create_fn_alloc(&mut self, instance: Instance<'tcx>) -> interpret::AllocId { - if let Some(&alloc_id) = self.function_cache.get(&instance) { + pub fn create_fn_alloc(&self, instance: Instance<'tcx>) -> interpret::AllocId { + if let Some(&alloc_id) = self.inner.borrow().function_cache.get(&instance) { return alloc_id; } let id = self.reserve(); debug!("creating fn ptr: {}", id); - self.functions.insert(id, instance); - self.function_cache.insert(instance, id); + let mut inner = self.inner.borrow_mut(); + inner.functions.insert(id, instance); + inner.function_cache.insert(instance, id); id } @@ -940,30 +946,31 @@ impl<'tcx> InterpretInterner<'tcx> { &self, id: interpret::AllocId, ) -> Option> { - self.functions.get(&id).cloned() + self.inner.borrow().functions.get(&id).cloned() } pub fn get_alloc( &self, id: interpret::AllocId, ) -> Option<&'tcx interpret::Allocation> { - self.alloc_by_id.get(&id).cloned() + self.inner.borrow().alloc_by_id.get(&id).cloned() } pub fn get_cached( &self, static_id: DefId, ) -> Option { - self.alloc_cache.get(&static_id).cloned() + self.inner.borrow().alloc_cache.get(&static_id).cloned() } pub fn cache( - &mut self, + &self, static_id: DefId, alloc_id: interpret::AllocId, ) { - self.global_cache.insert(alloc_id, static_id); - if let Some(old) = self.alloc_cache.insert(static_id, alloc_id) { + let mut inner = self.inner.borrow_mut(); + inner.global_cache.insert(alloc_id, static_id); + if let Some(old) = inner.alloc_cache.insert(static_id, alloc_id) { bug!("tried to cache {:?}, but was already existing as {:#?}", static_id, old); } } @@ -972,15 +979,15 @@ impl<'tcx> InterpretInterner<'tcx> { &self, ptr: interpret::AllocId, ) -> Option { - self.global_cache.get(&ptr).cloned() + self.inner.borrow().global_cache.get(&ptr).cloned() } pub fn intern_at_reserved( - &mut self, + &self, id: interpret::AllocId, alloc: &'tcx interpret::Allocation, ) { - if let Some(old) = self.alloc_by_id.insert(id, alloc) { + if let Some(old) = self.inner.borrow_mut().alloc_by_id.insert(id, alloc) { bug!("tried to intern allocation at {}, but was already existing as {:#?}", id, old); } } @@ -988,10 +995,11 @@ impl<'tcx> InterpretInterner<'tcx> { /// obtains a new allocation ID that can be referenced but does not /// yet have an allocation backing it. pub fn reserve( - &mut self, + &self, ) -> interpret::AllocId { - let next = self.next_id; - self.next_id.0 = self.next_id.0 + let mut inner = self.inner.borrow_mut(); + let next = inner.next_id; + inner.next_id.0 = inner.next_id.0 .checked_add(1) .expect("You overflowed a u64 by incrementing by 1... \ You've just earned yourself a free drink if we ever meet. \ @@ -1071,12 +1079,12 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self, alloc: interpret::Allocation, ) -> &'gcx interpret::Allocation { - if let Some(alloc) = self.interpret_interner.borrow().allocs.get(&alloc) { + if let Some(alloc) = self.interpret_interner.inner.borrow().allocs.get(&alloc) { return alloc; } let interned = self.global_arenas.const_allocs.alloc(alloc); - if let Some(prev) = self.interpret_interner.borrow_mut().allocs.replace(interned) { + if let Some(prev) = self.interpret_interner.inner.borrow_mut().allocs.replace(interned) { bug!("Tried to overwrite interned Allocation: {:#?}", prev) } interned @@ -1085,20 +1093,20 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { /// Allocates a byte or string literal for `mir::interpret` pub fn allocate_cached(self, bytes: &[u8]) -> interpret::AllocId { // check whether we already allocated this literal or a constant with the same memory - if let Some(&alloc_id) = self.interpret_interner.borrow().literal_alloc_cache.get(bytes) { + if let Some(&alloc_id) = self.interpret_interner.inner.borrow() + .literal_alloc_cache.get(bytes) { return alloc_id; } // create an allocation that just contains these bytes let alloc = interpret::Allocation::from_bytes(bytes); let alloc = self.intern_const_alloc(alloc); - let mut int = self.interpret_interner.borrow_mut(); // the next unique id - let id = int.reserve(); + let id = self.interpret_interner.reserve(); // make the allocation identifiable - int.alloc_by_id.insert(id, alloc); + self.interpret_interner.inner.borrow_mut().alloc_by_id.insert(id, alloc); // cache it for the future - int.literal_alloc_cache.insert(bytes.to_owned(), id); + self.interpret_interner.inner.borrow_mut().literal_alloc_cache.insert(bytes.to_owned(), id); id } @@ -1776,7 +1784,7 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { println!("Substs interner: #{}", self.interners.substs.borrow().len()); println!("Region interner: #{}", self.interners.region.borrow().len()); println!("Stability interner: #{}", self.stability_interner.borrow().len()); - println!("Interpret interner: #{}", self.interpret_interner.borrow().allocs.len()); + println!("Interpret interner: #{}", self.interpret_interner.inner.borrow().allocs.len()); println!("Layout interner: #{}", self.layout_interner.borrow().len()); } } diff --git a/src/librustc/ty/maps/on_disk_cache.rs b/src/librustc/ty/maps/on_disk_cache.rs index 84e566e57e626..5a24b1067b1ba 100644 --- a/src/librustc/ty/maps/on_disk_cache.rs +++ b/src/librustc/ty/maps/on_disk_cache.rs @@ -548,11 +548,10 @@ impl<'a, 'tcx, 'x> SpecializedDecoder for CacheDecoder<'a, ' fn specialized_decode(&mut self) -> Result { const MAX1: usize = usize::max_value() - 1; let tcx = self.tcx; - let interpret_interner = || tcx.interpret_interner.borrow_mut(); let pos = TyDecoder::position(self); match usize::decode(self)? { ::std::usize::MAX => { - let alloc_id = interpret_interner().reserve(); + let alloc_id = tcx.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); @@ -560,10 +559,10 @@ impl<'a, 'tcx, 'x> SpecializedDecoder for CacheDecoder<'a, ' let allocation = interpret::Allocation::decode(self)?; trace!("decoded alloc {:?} {:#?}", alloc_id, allocation); let allocation = self.tcx.intern_const_alloc(allocation); - interpret_interner().intern_at_reserved(alloc_id, allocation); + tcx.interpret_interner.intern_at_reserved(alloc_id, allocation); if let Some(glob) = Option::::decode(self)? { - interpret_interner().cache(glob, alloc_id); + tcx.interpret_interner.cache(glob, alloc_id); } Ok(alloc_id) @@ -572,7 +571,7 @@ impl<'a, 'tcx, 'x> SpecializedDecoder for CacheDecoder<'a, ' trace!("creating fn alloc id at {}", pos); let instance = ty::Instance::decode(self)?; trace!("decoded fn alloc instance: {:?}", instance); - let id = interpret_interner().create_fn_alloc(instance); + let id = tcx.interpret_interner.create_fn_alloc(instance); trace!("created fn alloc id: {:?}", id); self.interpret_alloc_cache.insert(pos, id); Ok(id) @@ -796,14 +795,14 @@ impl<'enc, 'a, 'tcx, E> SpecializedEncoder for CacheEncoder< // cache the allocation shorthand now, because the allocation itself might recursively // point to itself. self.interpret_alloc_shorthands.insert(*alloc_id, start); - if let Some(alloc) = self.tcx.interpret_interner.borrow().get_alloc(*alloc_id) { + if let Some(alloc) = self.tcx.interpret_interner.get_alloc(*alloc_id) { trace!("encoding {:?} with {:#?}", alloc_id, alloc); usize::max_value().encode(self)?; alloc.encode(self)?; - self.tcx.interpret_interner.borrow() + self.tcx.interpret_interner .get_corresponding_static_def_id(*alloc_id) .encode(self)?; - } else if let Some(fn_instance) = self.tcx.interpret_interner.borrow().get_fn(*alloc_id) { + } else if let Some(fn_instance) = self.tcx.interpret_interner.get_fn(*alloc_id) { trace!("encoding {:?} with {:#?}", alloc_id, fn_instance); (usize::max_value() - 1).encode(self)?; fn_instance.encode(self)?; diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 14984e8a6d758..f44703b9335e2 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -283,12 +283,11 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { fn specialized_decode(&mut self) -> Result { const MAX1: usize = usize::max_value() - 1; - let tcx = self.tcx; - let interpret_interner = || tcx.unwrap().interpret_interner.borrow_mut(); + let tcx = self.tcx.unwrap(); let pos = self.position(); match usize::decode(self)? { ::std::usize::MAX => { - let alloc_id = interpret_interner().reserve(); + let alloc_id = tcx.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); @@ -296,10 +295,10 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx let allocation = interpret::Allocation::decode(self)?; trace!("decoded alloc {:?} {:#?}", alloc_id, allocation); let allocation = self.tcx.unwrap().intern_const_alloc(allocation); - interpret_interner().intern_at_reserved(alloc_id, allocation); + tcx.interpret_interner.intern_at_reserved(alloc_id, allocation); if let Some(glob) = Option::::decode(self)? { - interpret_interner().cache(glob, alloc_id); + tcx.interpret_interner.cache(glob, alloc_id); } Ok(alloc_id) @@ -308,7 +307,7 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx trace!("creating fn alloc id at {}", pos); let instance = ty::Instance::decode(self)?; trace!("decoded fn alloc instance: {:?}", instance); - let id = interpret_interner().create_fn_alloc(instance); + let id = tcx.interpret_interner.create_fn_alloc(instance); trace!("created fn alloc id: {:?}", id); self.interpret_alloc_cache.insert(pos, id); Ok(id) diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index c74063c66bdfa..d959482417489 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -205,14 +205,14 @@ impl<'a, 'tcx> SpecializedEncoder for EncodeContext<'a, 'tcx // cache the allocation shorthand now, because the allocation itself might recursively // point to itself. self.interpret_alloc_shorthands.insert(*alloc_id, start); - if let Some(alloc) = self.tcx.interpret_interner.borrow().get_alloc(*alloc_id) { + if let Some(alloc) = self.tcx.interpret_interner.get_alloc(*alloc_id) { trace!("encoding {:?} with {:#?}", alloc_id, alloc); usize::max_value().encode(self)?; alloc.encode(self)?; - self.tcx.interpret_interner.borrow() + self.tcx.interpret_interner .get_corresponding_static_def_id(*alloc_id) .encode(self)?; - } else if let Some(fn_instance) = self.tcx.interpret_interner.borrow().get_fn(*alloc_id) { + } else if let Some(fn_instance) = self.tcx.interpret_interner.get_fn(*alloc_id) { trace!("encoding {:?} with {:#?}", alloc_id, fn_instance); (usize::max_value() - 1).encode(self)?; fn_instance.encode(self)?; diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 182ed52961929..35aa5adf76d7c 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -263,7 +263,6 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { ConstVal::Value(Value::ByVal(PrimVal::Ptr(p))) => { self.tcx() .interpret_interner - .borrow() .get_fn(p.alloc_id) .map(|instance| instance.def_id()) }, @@ -1044,7 +1043,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { .. }) => match val { ConstVal::Value(Value::ByVal(PrimVal::Ptr(p))) => { - let inst = self.tcx().interpret_interner.borrow().get_fn(p.alloc_id); + let inst = self.tcx().interpret_interner.get_fn(p.alloc_id); inst.map_or(false, |inst| { Some(inst.def_id()) == self.tcx().lang_items().box_free_fn() }) diff --git a/src/librustc_mir/const_eval/_match.rs b/src/librustc_mir/const_eval/_match.rs index 2057b0a5b4f7c..0f1eaf92ff12a 100644 --- a/src/librustc_mir/const_eval/_match.rs +++ b/src/librustc_mir/const_eval/_match.rs @@ -192,7 +192,6 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { assert!(is_array_ptr); let alloc = tcx .interpret_interner - .borrow() .get_alloc(ptr.alloc_id) .unwrap(); assert_eq!(ptr.offset, 0); @@ -568,7 +567,6 @@ fn max_slice_length<'p, 'a: 'p, 'tcx: 'a, I>( if is_array_ptr { let alloc = cx.tcx .interpret_interner - .borrow() .get_alloc(ptr.alloc_id) .unwrap(); max_fixed_len = cmp::max(max_fixed_len, alloc.bytes.len() as u64); @@ -958,7 +956,6 @@ fn slice_pat_covered_by_constructor(tcx: TyCtxt, _span: Span, assert!(is_array_ptr); tcx .interpret_interner - .borrow() .get_alloc(ptr.alloc_id) .unwrap() .bytes @@ -1099,7 +1096,6 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>( assert!(is_array_ptr); let data_len = cx.tcx .interpret_interner - .borrow() .get_alloc(ptr.alloc_id) .unwrap() .bytes diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index 2a9e1cb57e4ca..c2bb9f2efe126 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -106,7 +106,7 @@ fn eval_body_and_ecx<'a, 'mir, 'tcx>( mir = &mir.promoted[index]; } let layout = ecx.layout_of(mir.return_ty().subst(tcx, cid.instance.substs))?; - let alloc = tcx.interpret_interner.borrow().get_cached(cid.instance.def_id()); + let alloc = tcx.interpret_interner.get_cached(cid.instance.def_id()); let alloc = match alloc { Some(alloc) => { assert!(cid.promoted.is_none()); @@ -121,7 +121,7 @@ fn eval_body_and_ecx<'a, 'mir, 'tcx>( None, )?; if tcx.is_static(cid.instance.def_id()).is_some() { - tcx.interpret_interner.borrow_mut().cache(cid.instance.def_id(), ptr.alloc_id); + tcx.interpret_interner.cache(cid.instance.def_id(), ptr.alloc_id); } let span = tcx.def_span(cid.instance.def_id()); let internally_mutable = !layout.ty.is_freeze(tcx, param_env, span); @@ -343,7 +343,6 @@ impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator { Ok(ecx .tcx .interpret_interner - .borrow() .get_cached(cid.instance.def_id()) .expect("uncached static")) } @@ -457,13 +456,13 @@ pub fn const_eval_provider<'a, 'tcx>( let span = tcx.def_span(def_id); if tcx.is_foreign_item(def_id) { - let id = tcx.interpret_interner.borrow().get_cached(def_id); + let id = tcx.interpret_interner.get_cached(def_id); let id = match id { // FIXME: due to caches this shouldn't happen, add some assertions Some(id) => id, None => { - let id = tcx.interpret_interner.borrow_mut().reserve(); - tcx.interpret_interner.borrow_mut().cache(def_id, id); + let id = tcx.interpret_interner.reserve(); + tcx.interpret_interner.cache(def_id, id); id }, }; diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 1c60cda095c43..e759407f43157 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -945,7 +945,6 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M let cached = self .tcx .interpret_interner - .borrow() .get_cached(gid.instance.def_id()); if let Some(alloc_id) = cached { let layout = self.layout_of(ty)?; diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index a246898873c5c..a22158c73345c 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -75,7 +75,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { } pub fn create_fn_alloc(&mut self, instance: Instance<'tcx>) -> MemoryPointer { - let id = self.tcx.interpret_interner.borrow_mut().create_fn_alloc(instance); + let id = self.tcx.interpret_interner.create_fn_alloc(instance); MemoryPointer::new(id, 0) } @@ -107,7 +107,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { align, mutable: false, }; - let id = self.tcx.interpret_interner.borrow_mut().reserve(); + let id = self.tcx.interpret_interner.reserve(); M::add_lock(self, id); match kind { Some(kind @ MemoryKind::Stack) | @@ -186,12 +186,12 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { "uninitializedstatic".to_string(), format!("{:?}", kind), )) - } else if self.tcx.interpret_interner.borrow().get_fn(ptr.alloc_id).is_some() { + } else if self.tcx.interpret_interner.get_fn(ptr.alloc_id).is_some() { return err!(DeallocatedWrongMemoryKind( "function".to_string(), format!("{:?}", kind), )) - } else if self.tcx.interpret_interner.borrow().get_alloc(ptr.alloc_id).is_some() { + } else if self.tcx.interpret_interner.get_alloc(ptr.alloc_id).is_some() { return err!(DeallocatedWrongMemoryKind( "static".to_string(), format!("{:?}", kind), @@ -295,11 +295,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { None => match self.uninitialized_statics.get(&id) { Some(alloc) => Ok(alloc), None => { - let int = self.tcx.interpret_interner.borrow(); // static alloc? - int.get_alloc(id) + self.tcx.interpret_interner.get_alloc(id) // no alloc? produce an error - .ok_or_else(|| if int.get_fn(id).is_some() { + .ok_or_else(|| if self.tcx.interpret_interner.get_fn(id).is_some() { EvalErrorKind::DerefFunctionPointer.into() } else { EvalErrorKind::DanglingPointerDeref.into() @@ -320,11 +319,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { None => match self.uninitialized_statics.get_mut(&id) { Some(alloc) => Ok(alloc), None => { - let int = self.tcx.interpret_interner.borrow(); // no alloc or immutable alloc? produce an error - if int.get_alloc(id).is_some() { + if self.tcx.interpret_interner.get_alloc(id).is_some() { err!(ModifiedConstantMemory) - } else if int.get_fn(id).is_some() { + } else if self.tcx.interpret_interner.get_fn(id).is_some() { err!(DerefFunctionPointer) } else { err!(DanglingPointerDeref) @@ -341,7 +339,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { debug!("reading fn ptr: {}", ptr.alloc_id); self.tcx .interpret_interner - .borrow() .get_fn(ptr.alloc_id) .ok_or(EvalErrorKind::ExecuteMemory.into()) } @@ -376,9 +373,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { Some(a) => (a, " (static in the process of initialization)".to_owned()), None => { // static alloc? - match self.tcx.interpret_interner.borrow().get_alloc(id) { + match self.tcx.interpret_interner.get_alloc(id) { Some(a) => (a, "(immutable)".to_owned()), - None => if let Some(func) = self.tcx.interpret_interner.borrow().get_fn(id) { + None => if let Some(func) = self.tcx.interpret_interner.get_fn(id) { trace!("{} {}", msg, func); continue; } else { @@ -549,7 +546,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { // ensure llvm knows not to put this into immutable memroy alloc.mutable = mutability == Mutability::Mutable; let alloc = self.tcx.intern_const_alloc(alloc); - self.tcx.interpret_interner.borrow_mut().intern_at_reserved(alloc_id, alloc); + self.tcx.interpret_interner.intern_at_reserved(alloc_id, alloc); // recurse into inner allocations for &alloc in alloc.relocations.values() { self.mark_inner_allocation_initialized(alloc, mutability)?; diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 12725fb6dff75..e82a252a95b3a 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -197,7 +197,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { let alloc = self .tcx .interpret_interner - .borrow() .get_cached(static_.def_id); let layout = self.layout_of(self.place_ty(mir_place))?; if let Some(alloc) = alloc { diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index 35c561e02e07e..3d393b5696a3a 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -1117,12 +1117,12 @@ fn collect_miri<'a, 'tcx>( alloc_id: AllocId, output: &mut Vec>, ) { - if let Some(alloc) = tcx.interpret_interner.borrow().get_alloc(alloc_id) { + if let Some(alloc) = tcx.interpret_interner.get_alloc(alloc_id) { trace!("collecting {:?} with {:#?}", alloc_id, alloc); for &inner in alloc.relocations.values() { collect_miri(tcx, inner, output); } - } else if let Some(fn_instance) = tcx.interpret_interner.borrow().get_fn(alloc_id) { + } else if let Some(fn_instance) = tcx.interpret_interner.get_fn(alloc_id) { if should_monomorphize_locally(tcx, &fn_instance) { trace!("collecting {:?} with {:#?}", alloc_id, fn_instance); output.push(create_fn_mono_item(fn_instance)); diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 1ca9bd81893aa..1d04e30eb3e83 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -151,18 +151,17 @@ pub fn primval_to_llvm(cx: &CodegenCx, } }, PrimVal::Ptr(ptr) => { - if let Some(fn_instance) = cx.tcx.interpret_interner.borrow().get_fn(ptr.alloc_id) { + if let Some(fn_instance) = cx.tcx.interpret_interner.get_fn(ptr.alloc_id) { callee::get_fn(cx, fn_instance) } else { let static_ = cx .tcx .interpret_interner - .borrow() .get_corresponding_static_def_id(ptr.alloc_id); let base_addr = if let Some(def_id) = static_ { assert!(cx.tcx.is_static(def_id).is_some()); consts::get_static(cx, def_id) - } else if let Some(alloc) = cx.tcx.interpret_interner.borrow() + } else if let Some(alloc) = cx.tcx.interpret_interner .get_alloc(ptr.alloc_id) { let init = global_initializer(cx, alloc); if alloc.mutable { @@ -239,14 +238,12 @@ pub fn trans_static_initializer<'a, 'tcx>( let alloc_id = cx .tcx .interpret_interner - .borrow() .get_cached(def_id) .expect("global not cached"); let alloc = cx .tcx .interpret_interner - .borrow() .get_alloc(alloc_id) .expect("miri allocation never successfully created"); Ok(global_initializer(cx, alloc)) From 3d8c4d4fe9358d23d9edd6ac016477f91f1de997 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 25 Jan 2018 12:12:19 +0100 Subject: [PATCH 023/110] Don't read from zero sized fields --- src/librustc_mir/interpret/place.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index e82a252a95b3a..4ca68068728d7 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -123,6 +123,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { } let field_index = field.index(); let field = base_layout.field(self, field_index)?; + if field.size.bytes() == 0 { + return Ok(Some((Value::ByVal(PrimVal::Undef), field.ty))) + } let offset = base_layout.fields.offset(field_index); match base { // the field covers the entire type From 4f6c557c9d9c7cfade41cf473935a26ad54351d4 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 25 Jan 2018 13:45:57 +0100 Subject: [PATCH 024/110] Wrap the miri ErrorKind in an Rc to reduce work in queries --- src/librustc/ich/impls_ty.rs | 4 ++-- src/librustc/mir/interpret/error.rs | 9 +++++---- src/librustc/ty/structural_impls.rs | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 54297a1fc398f..74441d4694567 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -489,9 +489,9 @@ for ::mir::interpret::EvalError<'gcx> { hasher: &mut StableHasher) { use mir::interpret::EvalErrorKind::*; - mem::discriminant(&self.kind).hash_stable(hcx, hasher); + mem::discriminant(&*self.kind).hash_stable(hcx, hasher); - match self.kind { + match *self.kind { DanglingPointerDeref | DoubleFree | InvalidMemoryAccess | diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs index 977e617968aa4..702fd85794fff 100644 --- a/src/librustc/mir/interpret/error.rs +++ b/src/librustc/mir/interpret/error.rs @@ -1,5 +1,6 @@ use std::error::Error; use std::{fmt, env}; +use std::rc::Rc; use mir; use ty::{FnSig, Ty, layout}; @@ -14,7 +15,7 @@ use backtrace::Backtrace; #[derive(Debug, Clone)] pub struct EvalError<'tcx> { - pub kind: EvalErrorKind<'tcx>, + pub kind: Rc>, pub backtrace: Option, } @@ -25,7 +26,7 @@ impl<'tcx> From> for EvalError<'tcx> { _ => None }; EvalError { - kind, + kind: Rc::new(kind), backtrace, } } @@ -131,7 +132,7 @@ pub type EvalResult<'tcx, T = ()> = Result>; impl<'tcx> Error for EvalError<'tcx> { fn description(&self) -> &str { use self::EvalErrorKind::*; - match self.kind { + match *self.kind { MachineError(ref inner) => inner, FunctionPointerTyMismatch(..) => "tried to call a function through a function pointer of a different type", @@ -252,7 +253,7 @@ impl<'tcx> Error for EvalError<'tcx> { impl<'tcx> fmt::Display for EvalError<'tcx> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::EvalErrorKind::*; - match self.kind { + match *self.kind { PointerOutOfBounds { ptr, access, allocation_size } => { write!(f, "{} at offset {}, outside bounds of allocation {} which has size {}", if access { "memory access" } else { "pointer computed" }, diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 7cc509f691412..9394a853920e5 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -591,7 +591,7 @@ impl<'a, 'tcx> Lift<'tcx> for interpret::EvalError<'a> { type Lifted = interpret::EvalError<'tcx>; fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option { use ::mir::interpret::EvalErrorKind::*; - let kind = match self.kind { + let kind = match *self.kind { MachineError(ref err) => MachineError(err.clone()), FunctionPointerTyMismatch(a, b) => FunctionPointerTyMismatch( tcx.lift(&a)?, @@ -691,7 +691,7 @@ impl<'a, 'tcx> Lift<'tcx> for interpret::EvalError<'a> { TypeckError => TypeckError, }; Some(interpret::EvalError { - kind, + kind: Rc::new(kind), backtrace: self.backtrace.clone(), }) } From cf056615a85a14f7a07f30f9e2a7ec3d13e9237c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 25 Jan 2018 14:15:12 +0100 Subject: [PATCH 025/110] Add test for attempting to use array indexing for conditional const eval --- .../const-eval/conditional_array_execution.rs | 17 ++++++++++++ .../conditional_array_execution.stderr | 26 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 src/test/ui/const-eval/conditional_array_execution.rs create mode 100644 src/test/ui/const-eval/conditional_array_execution.stderr diff --git a/src/test/ui/const-eval/conditional_array_execution.rs b/src/test/ui/const-eval/conditional_array_execution.rs new file mode 100644 index 0000000000000..41ceb609df5bd --- /dev/null +++ b/src/test/ui/const-eval/conditional_array_execution.rs @@ -0,0 +1,17 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +const X: u32 = 5; +const Y: u32 = 6; +const FOO: u32 = [X - Y, Y - X][(X < Y) as usize]; //~ E0080 + +fn main() { + println!("{}", FOO); //~ E0080 +} diff --git a/src/test/ui/const-eval/conditional_array_execution.stderr b/src/test/ui/const-eval/conditional_array_execution.stderr new file mode 100644 index 0000000000000..0e26db7ac0858 --- /dev/null +++ b/src/test/ui/const-eval/conditional_array_execution.stderr @@ -0,0 +1,26 @@ +error[E0080]: constant evaluation error + --> $DIR/conditional_array_execution.rs:13:19 + | +13 | const FOO: u32 = [X - Y, Y - X][(X < Y) as usize]; //~ E0080 + | ^^^^^ attempt to subtract with overflow + | +note: inside call to FOO + --> $DIR/conditional_array_execution.rs:13:1 + | +13 | const FOO: u32 = [X - Y, Y - X][(X < Y) as usize]; //~ E0080 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0080]: constant evaluation error + --> $DIR/conditional_array_execution.rs:16:20 + | +16 | println!("{}", FOO); //~ E0080 + | ^^^ attempt to subtract with overflow + | +note: inside call to main + --> $DIR/conditional_array_execution.rs:16:20 + | +16 | println!("{}", FOO); //~ E0080 + | ^^^ + +error: aborting due to 2 previous errors + From 790d131ac23835b13c494e88495fcdf3e45d9b07 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 25 Jan 2018 14:15:28 +0100 Subject: [PATCH 026/110] Add test for #45044 --- src/test/run-pass/ctfe/repeat_match.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/test/run-pass/ctfe/repeat_match.rs diff --git a/src/test/run-pass/ctfe/repeat_match.rs b/src/test/run-pass/ctfe/repeat_match.rs new file mode 100644 index 0000000000000..dedf5defebb7c --- /dev/null +++ b/src/test/run-pass/ctfe/repeat_match.rs @@ -0,0 +1,20 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// https://github.com/rust-lang/rust/issues/45044 + +const X: [u8; 1] = [0; 1]; + +fn main() { + match &X { + &X => println!("a"), + _ => println!("b"), + }; +} From 2f2c90e733a703f69cf13ae79ec623901d42ba2c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 25 Jan 2018 14:15:56 +0100 Subject: [PATCH 027/110] Destructure Rc wrapped ErrorKind in miri --- src/librustc_mir/interpret/const_eval.rs | 14 ++++++++------ src/librustc_mir/interpret/eval_context.rs | 11 ++++++----- src/librustc_mir/transform/instcombine.rs | 6 +----- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index c2bb9f2efe126..611633163fad5 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -235,13 +235,15 @@ impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator { } let mir = match ecx.load_mir(instance.def) { Ok(mir) => mir, - Err(EvalError { kind: EvalErrorKind::NoMirFor(path), .. }) => { - return Err( - ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path)) - .into(), - ); + Err(err) => { + if let EvalErrorKind::NoMirFor(ref path) = *err.kind { + return Err( + ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path)) + .into(), + ); + } + return Err(err); } - Err(other) => return Err(other), }; let (return_place, return_to_block) = match destination { Some((place, block)) => (place, StackPopCleanup::Goto(block)), diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index e759407f43157..eaa1bcb96a903 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -1513,11 +1513,12 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M write!(msg, ":").unwrap(); match self.stack[frame].get_local(local) { - Err(EvalError { kind: EvalErrorKind::DeadLocal, .. }) => { - write!(msg, " is dead").unwrap(); - } Err(err) => { - panic!("Failed to access local: {:?}", err); + if let EvalErrorKind::DeadLocal = *err.kind { + write!(msg, " is dead").unwrap(); + } else { + panic!("Failed to access local: {:?}", err); + } } Ok(Value::ByRef(ptr, align)) => { match ptr.into_inner_primval() { @@ -1576,7 +1577,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M } pub fn report(&self, e: &mut EvalError, as_err: bool, explicit_span: Option) { - if let EvalErrorKind::TypeckError = e.kind { + if let EvalErrorKind::TypeckError = *e.kind { return; } if let Some(ref mut backtrace) = e.backtrace { diff --git a/src/librustc_mir/transform/instcombine.rs b/src/librustc_mir/transform/instcombine.rs index 25787608e39ca..640fd7cadab2c 100644 --- a/src/librustc_mir/transform/instcombine.rs +++ b/src/librustc_mir/transform/instcombine.rs @@ -362,12 +362,8 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { ) } else { if overflow { - use rustc::mir::interpret::EvalError; use rustc::mir::interpret::EvalErrorKind; - let mut err = EvalError { - kind: EvalErrorKind::OverflowingMath, - backtrace: None, - }; + let mut err = EvalErrorKind::OverflowingMath.into(); ecx.report(&mut err, false, Some(span)); return None; } From 3b8d2e0016884fdc56e3e2208235606d999990a8 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 25 Jan 2018 14:56:34 +0100 Subject: [PATCH 028/110] Rename ConstVal::to_u128 to to_raw_bits --- src/librustc/middle/const_val.rs | 4 ++-- src/librustc/middle/mem_categorization.rs | 2 +- src/librustc/ty/inhabitedness/mod.rs | 2 +- src/librustc_mir/build/matches/test.rs | 2 +- src/librustc_mir/const_eval/_match.rs | 2 +- src/librustc_mir/transform/simplify_branches.rs | 2 +- src/librustc_mir/util/elaborate_drops.rs | 2 +- src/librustc_typeck/check/mod.rs | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index 35c51c0c206b0..a40147aa2a1f4 100644 --- a/src/librustc/middle/const_val.rs +++ b/src/librustc/middle/const_val.rs @@ -39,7 +39,7 @@ pub struct ByteArray<'tcx> { impl<'tcx> serialize::UseSpecializedDecodable for ByteArray<'tcx> {} impl<'tcx> ConstVal<'tcx> { - pub fn to_u128(&self) -> Option { + pub fn to_raw_bits(&self) -> Option { match *self { ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))) => { Some(b) @@ -48,7 +48,7 @@ impl<'tcx> ConstVal<'tcx> { } } pub fn unwrap_u64(&self) -> u64 { - match self.to_u128() { + match self.to_raw_bits() { Some(val) => { assert_eq!(val as u64 as u128, val); val as u64 diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 1c60ae36cd334..30d63b8443e3d 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -913,7 +913,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { // Always promote `[T; 0]` (even when e.g. borrowed mutably). let promotable = match expr_ty.sty { - ty::TyArray(_, len) if len.val.to_u128() == Some(0) => true, + ty::TyArray(_, len) if len.val.to_raw_bits() == Some(0) => true, _ => promotable, }; diff --git a/src/librustc/ty/inhabitedness/mod.rs b/src/librustc/ty/inhabitedness/mod.rs index bfaa661b2432d..3e653cf126a8d 100644 --- a/src/librustc/ty/inhabitedness/mod.rs +++ b/src/librustc/ty/inhabitedness/mod.rs @@ -262,7 +262,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { })) }, TyArray(ty, len) => { - match len.val.to_u128() { + match len.val.to_raw_bits() { // If the array is definitely non-empty, it's uninhabited if // the type of its elements is uninhabited. Some(n) if n != 0 => ty.uninhabited_from(visited, tcx), diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index 3b9fbc5c867a1..4a5d9e228815e 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -127,7 +127,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { indices.entry(value) .or_insert_with(|| { - options.push(value.val.to_u128().expect("switching on int")); + options.push(value.val.to_raw_bits().expect("switching on int")); options.len() - 1 }); true diff --git a/src/librustc_mir/const_eval/_match.rs b/src/librustc_mir/const_eval/_match.rs index 0f1eaf92ff12a..3b9bbc0ba8b44 100644 --- a/src/librustc_mir/const_eval/_match.rs +++ b/src/librustc_mir/const_eval/_match.rs @@ -446,7 +446,7 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, })) }).collect() } - ty::TyArray(ref sub_ty, len) if len.val.to_u128().is_some() => { + ty::TyArray(ref sub_ty, len) if len.val.to_raw_bits().is_some() => { let len = len.val.unwrap_u64(); if len != 0 && cx.is_uninhabited(sub_ty) { vec![] diff --git a/src/librustc_mir/transform/simplify_branches.rs b/src/librustc_mir/transform/simplify_branches.rs index e39f5412355bf..9dd48952208a9 100644 --- a/src/librustc_mir/transform/simplify_branches.rs +++ b/src/librustc_mir/transform/simplify_branches.rs @@ -41,7 +41,7 @@ impl MirPass for SimplifyBranches { TerminatorKind::SwitchInt { discr: Operand::Constant(box Constant { literal: Literal::Value { ref value }, .. }), ref values, ref targets, .. } => { - if let Some(constint) = value.val.to_u128() { + if let Some(constint) = value.val.to_raw_bits() { let (otherwise, targets) = targets.split_last().unwrap(); let mut ret = TerminatorKind::Goto { target: *otherwise }; for (&v, t) in values.iter().zip(targets.iter()) { diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs index 2ad1580f75dcd..fbdeb3334abd9 100644 --- a/src/librustc_mir/util/elaborate_drops.rs +++ b/src/librustc_mir/util/elaborate_drops.rs @@ -804,7 +804,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> self.complete_drop(Some(DropFlagMode::Deep), succ, unwind) } ty::TyArray(ety, size) => self.open_drop_for_array( - ety, size.val.to_u128().map(|i| i as u64)), + ety, size.val.to_raw_bits().map(|i| i as u64)), ty::TySlice(ety) => self.open_drop_for_array(ety, None), _ => bug!("open drop from non-ADT `{:?}`", ty) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index f5303979f9c32..56f2b30190f1a 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -4040,7 +4040,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }; if let Ok(count) = count { - let zero_or_one = count.val.to_u128().map_or(false, |count| count <= 1); + let zero_or_one = count.val.to_raw_bits().map_or(false, |count| count <= 1); if !zero_or_one { // For [foo, ..n] where n > 1, `foo` must have // Copy type: From 9857eaa4dfc1a103369b423b12cac1993a8d9e96 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 25 Jan 2018 16:44:45 +0100 Subject: [PATCH 029/110] Nuke ConstInt and Const*size --- src/librustc/ich/impls_const_math.rs | 27 - src/librustc/middle/const_val.rs | 14 - src/librustc/mir/mod.rs | 4 +- src/librustc/mir/tcx.rs | 2 +- src/librustc/mir/visit.rs | 14 +- src/librustc/ty/context.rs | 8 +- src/librustc/ty/layout.rs | 2 +- src/librustc/ty/mod.rs | 42 +- src/librustc/ty/util.rs | 158 +++-- src/librustc_const_math/int.rs | 590 ------------------ src/librustc_const_math/isize.rs | 56 -- src/librustc_const_math/lib.rs | 6 - src/librustc_const_math/usize.rs | 56 -- .../borrow_check/nll/type_check/mod.rs | 2 +- src/librustc_mir/build/expr/as_rvalue.rs | 63 +- src/librustc_mir/build/matches/test.rs | 2 +- src/librustc_mir/const_eval/pattern.rs | 2 +- src/librustc_mir/hair/cx/expr.rs | 4 +- src/librustc_mir/hair/cx/mod.rs | 17 +- src/librustc_mir/hair/mod.rs | 3 +- src/librustc_mir/interpret/eval_context.rs | 4 +- src/librustc_mir/shim.rs | 9 +- src/librustc_mir/util/elaborate_drops.rs | 2 +- src/librustc_passes/mir_stats.rs | 8 - src/librustc_trans/base.rs | 8 - src/librustc_trans/debuginfo/metadata.rs | 2 +- src/librustc_trans/mir/constant.rs | 109 +--- src/librustc_trans/mir/place.rs | 2 +- src/librustc_trans/mir/rvalue.rs | 10 - src/librustc_typeck/check/mod.rs | 8 +- src/librustc_typeck/collect.rs | 19 +- src/test/compile-fail/issue-27592.rs | 1 + src/test/ui/issue-15524.rs | 6 +- 33 files changed, 159 insertions(+), 1101 deletions(-) delete mode 100644 src/librustc_const_math/int.rs delete mode 100644 src/librustc_const_math/isize.rs delete mode 100644 src/librustc_const_math/usize.rs diff --git a/src/librustc/ich/impls_const_math.rs b/src/librustc/ich/impls_const_math.rs index 6790c2ac7dece..5f3ff461c0c7e 100644 --- a/src/librustc/ich/impls_const_math.rs +++ b/src/librustc/ich/impls_const_math.rs @@ -16,33 +16,6 @@ impl_stable_hash_for!(struct ::rustc_const_math::ConstFloat { bits }); -impl_stable_hash_for!(enum ::rustc_const_math::ConstInt { - I8(val), - I16(val), - I32(val), - I64(val), - I128(val), - Isize(val), - U8(val), - U16(val), - U32(val), - U64(val), - U128(val), - Usize(val) -}); - -impl_stable_hash_for!(enum ::rustc_const_math::ConstIsize { - Is16(i16), - Is32(i32), - Is64(i64) -}); - -impl_stable_hash_for!(enum ::rustc_const_math::ConstUsize { - Us16(i16), - Us32(i32), - Us64(i64) -}); - impl_stable_hash_for!(enum ::rustc_const_math::ConstMathErr { NotInRange, CmpBetweenUnequalTypes, diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index a40147aa2a1f4..06d4540a455e8 100644 --- a/src/librustc/middle/const_val.rs +++ b/src/librustc/middle/const_val.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -pub use rustc_const_math::ConstInt; - use hir::def_id::DefId; use ty::{self, TyCtxt, layout}; use ty::subst::Substs; @@ -56,18 +54,6 @@ impl<'tcx> ConstVal<'tcx> { None => bug!("expected constant u64, got {:#?}", self), } } - pub fn unwrap_usize<'a, 'gcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> ConstUsize { - match *self { - 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) { - Ok(val) => val, - Err(e) => bug!("{:#?} is not a usize {:?}", self, e), - } - }, - _ => bug!("expected constant u64, got {:#?}", self), - } - } } #[derive(Clone, Debug)] diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 24f4a74b3ac38..e39765699f9be 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -15,7 +15,7 @@ use graphviz::IntoCow; use middle::const_val::ConstVal; use middle::region; -use rustc_const_math::{ConstUsize, ConstMathErr}; +use rustc_const_math::ConstMathErr; use rustc_data_structures::sync::{Lrc}; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; use rustc_data_structures::control_flow_graph::dominators::{Dominators, dominators}; @@ -1563,7 +1563,7 @@ pub enum Rvalue<'tcx> { Use(Operand<'tcx>), /// [x; 32] - Repeat(Operand<'tcx>, ConstUsize), + Repeat(Operand<'tcx>, u64), /// &x or &mut x Ref(Region<'tcx>, BorrowKind, Place<'tcx>), diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index 067c1742040bf..7d232ac20bfd5 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -149,7 +149,7 @@ impl<'tcx> Rvalue<'tcx> { match *self { Rvalue::Use(ref operand) => operand.ty(local_decls, tcx), Rvalue::Repeat(ref operand, count) => { - tcx.mk_array_const_usize(operand.ty(local_decls, tcx), count) + tcx.mk_array(operand.ty(local_decls, tcx), count) } Rvalue::Ref(reg, bk, ref place) => { let place_ty = place.ty(local_decls, tcx).to_ty(tcx); diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 54d3ed38d6537..650af8dc4d903 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -12,7 +12,6 @@ use hir::def_id::DefId; use ty::subst::Substs; use ty::{ClosureSubsts, Region, Ty, GeneratorInterior}; use mir::*; -use rustc_const_math::ConstUsize; use syntax_pos::Span; // # The MIR Visitor @@ -243,12 +242,6 @@ macro_rules! make_mir_visitor { self.super_generator_interior(interior); } - fn visit_const_usize(&mut self, - const_usize: & $($mutability)* ConstUsize, - _: Location) { - self.super_const_usize(const_usize); - } - fn visit_local_decl(&mut self, local: Local, local_decl: & $($mutability)* LocalDecl<'tcx>) { @@ -529,10 +522,8 @@ macro_rules! make_mir_visitor { self.visit_operand(operand, location); } - Rvalue::Repeat(ref $($mutability)* value, - ref $($mutability)* length) => { + Rvalue::Repeat(ref $($mutability)* value, _) => { self.visit_operand(value, location); - self.visit_const_usize(length, location); } Rvalue::Ref(ref $($mutability)* r, bk, ref $($mutability)* path) => { @@ -789,9 +780,6 @@ macro_rules! make_mir_visitor { _substs: & $($mutability)* ClosureSubsts<'tcx>) { } - fn super_const_usize(&mut self, _const_usize: & $($mutability)* ConstUsize) { - } - // Convenience methods fn visit_location(&mut self, mir: & $($mutability)* Mir<'tcx>, location: Location) { diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index a4e5f725837de..8c508c2f17c81 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -54,7 +54,6 @@ use rustc_data_structures::stable_hasher::{HashStable, hash_stable_hashmap, StableHasher, StableHasherResult, StableVec}; use arena::{TypedArena, DroplessArena}; -use rustc_const_math::ConstUsize; use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::sync::Lrc; use std::any::Any; @@ -2096,13 +2095,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } pub fn mk_array(self, ty: Ty<'tcx>, n: u64) -> Ty<'tcx> { - let n = ConstUsize::new(n, self.sess.target.usize_ty).unwrap(); - self.mk_array_const_usize(ty, n) - } - - 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: ConstVal::Value(Value::ByVal(PrimVal::Bytes(n.as_u64().into()))), + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(n.into()))), ty: self.types.usize }))) } diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 79f01a32cd975..b02f221bb8132 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -1537,7 +1537,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { if variants[i].iter().any(|f| f.abi == Abi::Uninhabited) { continue; } - let x = discr.to_u128_unchecked() as i128; + let x = discr.val as i128; if x < min { min = x; } if x > max { max = x; } } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 2c7ec5155a627..cb09687a6573c 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -32,7 +32,7 @@ use session::CrateDisambiguator; use traits; use ty; use ty::subst::{Subst, Substs}; -use ty::util::IntTypeExt; +use ty::util::{IntTypeExt, Discr}; use ty::walk::TypeWalker; use util::common::ErrorReported; use util::nodemap::{NodeSet, DefIdMap, FxHashMap, FxHashSet}; @@ -53,7 +53,6 @@ use syntax::attr; use syntax::ext::hygiene::{Mark, SyntaxContext}; use syntax::symbol::{Symbol, InternedString}; use syntax_pos::{DUMMY_SP, Span}; -use rustc_const_math::ConstInt; use rustc_data_structures::accumulate_vec::IntoIter as AccIntoIter; use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult, @@ -1826,13 +1825,13 @@ impl<'a, 'gcx, 'tcx> AdtDef { #[inline] pub fn discriminants(&'a self, tcx: TyCtxt<'a, 'gcx, 'tcx>) - -> impl Iterator + 'a { + -> impl Iterator> + 'a { let param_env = ParamEnv::empty(traits::Reveal::UserFacing); let repr_type = self.repr.discr_type(); let initial = repr_type.initial_discriminant(tcx.global_tcx()); - let mut prev_discr = None::; + let mut prev_discr = None::>; self.variants.iter().map(move |v| { - let mut discr = prev_discr.map_or(initial, |d| d.wrap_incr()); + let mut discr = prev_discr.map_or(initial, |d| d.wrap_incr(tcx)); if let VariantDiscr::Explicit(expr_did) = v.discr { let substs = Substs::identity_for_item(tcx.global_tcx(), expr_did); let instance = ty::Instance::new(expr_did, substs); @@ -1846,12 +1845,9 @@ impl<'a, 'gcx, 'tcx> AdtDef { .. }) => { trace!("discriminants: {} ({:?})", b, repr_type); - use syntax::attr::IntType; - discr = match repr_type { - IntType::SignedInt(int_type) => ConstInt::new_signed( - b as i128, int_type, tcx.sess.target.isize_ty).unwrap(), - IntType::UnsignedInt(uint_type) => ConstInt::new_unsigned( - b, uint_type, tcx.sess.target.usize_ty).unwrap(), + discr = Discr { + val: b, + ty: repr_type.to_ty(tcx), }; } _ => { @@ -1877,7 +1873,7 @@ impl<'a, 'gcx, 'tcx> AdtDef { pub fn discriminant_for_variant(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>, variant_index: usize) - -> ConstInt { + -> Discr<'tcx> { let param_env = ParamEnv::empty(traits::Reveal::UserFacing); let repr_type = self.repr.discr_type(); let mut explicit_value = repr_type.initial_discriminant(tcx.global_tcx()); @@ -1901,12 +1897,9 @@ impl<'a, 'gcx, 'tcx> AdtDef { .. }) => { trace!("discriminants: {} ({:?})", b, repr_type); - use syntax::attr::IntType; - explicit_value = match repr_type { - IntType::SignedInt(int_type) => ConstInt::new_signed( - b as i128, int_type, tcx.sess.target.isize_ty).unwrap(), - IntType::UnsignedInt(uint_type) => ConstInt::new_unsigned( - b, uint_type, tcx.sess.target.usize_ty).unwrap(), + explicit_value = Discr { + val: b, + ty: repr_type.to_ty(tcx), }; break; } @@ -1925,18 +1918,7 @@ impl<'a, 'gcx, 'tcx> AdtDef { } } } - let discr = explicit_value.to_u128_unchecked() - .wrapping_add((variant_index - explicit_index) as u128); - match repr_type { - attr::UnsignedInt(ty) => { - ConstInt::new_unsigned_truncating(discr, ty, - tcx.sess.target.usize_ty) - } - attr::SignedInt(ty) => { - ConstInt::new_signed_truncating(discr as i128, ty, - tcx.sess.target.isize_ty) - } - } + explicit_value.checked_add(tcx, (variant_index - explicit_index) as u128).0 } pub fn destructor(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option { diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index cdf619a1dfad0..e65a3b620c388 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -26,57 +26,95 @@ use util::common::ErrorReported; use middle::lang_items; use mir::interpret::{Value, PrimVal}; -use rustc_const_math::{ConstInt, ConstIsize, ConstUsize}; use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult, HashStable}; use rustc_data_structures::fx::FxHashMap; -use std::cmp; +use std::{cmp, fmt}; use std::hash::Hash; use std::intrinsics; -use syntax::ast::{self, Name}; +use syntax::ast::{self, Name, UintTy, IntTy}; use syntax::attr::{self, SignedInt, UnsignedInt}; use syntax_pos::{Span, DUMMY_SP}; -type Disr = ConstInt; - -pub trait IntTypeExt { - fn to_ty<'a, 'gcx, 'tcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx>; - fn disr_incr<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, val: Option) - -> Option; - fn assert_ty_matches(&self, val: Disr); - fn initial_discriminant<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Disr; +#[derive(Copy, Clone, Debug)] +pub struct Discr<'tcx> { + pub val: u128, + pub ty: Ty<'tcx> } +impl<'tcx> fmt::Display for Discr<'tcx> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + if self.ty.is_signed() { + write!(fmt, "{}", self.val as i128) + } else { + write!(fmt, "{}", self.val) + } + } +} -macro_rules! typed_literal { - ($tcx:expr, $ty:expr, $lit:expr) => { - match $ty { - SignedInt(ast::IntTy::I8) => ConstInt::I8($lit), - SignedInt(ast::IntTy::I16) => ConstInt::I16($lit), - SignedInt(ast::IntTy::I32) => ConstInt::I32($lit), - SignedInt(ast::IntTy::I64) => ConstInt::I64($lit), - SignedInt(ast::IntTy::I128) => ConstInt::I128($lit), - SignedInt(ast::IntTy::Isize) => match $tcx.sess.target.isize_ty { - ast::IntTy::I16 => ConstInt::Isize(ConstIsize::Is16($lit)), - ast::IntTy::I32 => ConstInt::Isize(ConstIsize::Is32($lit)), - ast::IntTy::I64 => ConstInt::Isize(ConstIsize::Is64($lit)), - _ => bug!(), - }, - UnsignedInt(ast::UintTy::U8) => ConstInt::U8($lit), - UnsignedInt(ast::UintTy::U16) => ConstInt::U16($lit), - UnsignedInt(ast::UintTy::U32) => ConstInt::U32($lit), - UnsignedInt(ast::UintTy::U64) => ConstInt::U64($lit), - UnsignedInt(ast::UintTy::U128) => ConstInt::U128($lit), - UnsignedInt(ast::UintTy::Usize) => match $tcx.sess.target.usize_ty { - ast::UintTy::U16 => ConstInt::Usize(ConstUsize::Us16($lit)), - ast::UintTy::U32 => ConstInt::Usize(ConstUsize::Us32($lit)), - ast::UintTy::U64 => ConstInt::Usize(ConstUsize::Us64($lit)), - _ => bug!(), - }, +impl<'tcx> Discr<'tcx> { + /// Adds 1 to the value and wraps around if the maximum for the type is reached + pub fn wrap_incr<'a, 'gcx>(self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Self { + self.checked_add(tcx, 1).0 + } + pub fn checked_add<'a, 'gcx>(self, tcx: TyCtxt<'a, 'gcx, 'tcx>, n: u128) -> (Self, bool) { + let ty = match self.ty.sty { + TyInt(IntTy::Isize) => tcx.mk_mach_int(tcx.sess.target.isize_ty), + TyUint(UintTy::Usize) => tcx.mk_mach_uint(tcx.sess.target.usize_ty), + _ => self.ty, + }; + let (min, max) = match ty.sty { + TyInt(IntTy::I8) => (i8::min_value() as i128 as u128, i8::max_value() as u128), + TyInt(IntTy::I16) => (i16::min_value() as i128 as u128, i16::max_value() as u128), + TyInt(IntTy::I32) => (i32::min_value() as i128 as u128, i32::max_value() as u128), + TyInt(IntTy::I64) => (i64::min_value() as i128 as u128, i64::max_value() as u128), + TyInt(IntTy::I128) => (i128::min_value() as i128 as u128, i128::max_value() as u128), + TyInt(IntTy::Isize) => unreachable!(), + TyUint(UintTy::U8) => (u8::min_value() as u128, u8::max_value() as u128), + TyUint(UintTy::U16) => (u16::min_value() as u128, u16::max_value() as u128), + TyUint(UintTy::U32) => (u32::min_value() as u128, u32::max_value() as u128), + TyUint(UintTy::U64) => (u64::min_value() as u128, u64::max_value() as u128), + TyUint(UintTy::U128) => (u128::min_value() as u128, u128::max_value()), + TyUint(UintTy::Usize) => unreachable!(), + _ => bug!("not a valid discriminant type: {}", ty) + }; + if ty.is_signed() { + let val = self.val as i128; + let n = n as i128; + let max = max as i128; + let min = min as i128; + let oflo = val > max - n; + let val = if oflo { + min + (n - (max - val)) + } else { + val + n + }; + (Self { + val: val as u128, + ty: self.ty, + }, oflo) + } else { + let oflo = self.val > max - n; + let val = if oflo { + min + (n - (max - self.val)) + } else { + self.val + n + }; + (Self { + val, + ty: self.ty, + }, oflo) } } } +pub trait IntTypeExt { + fn to_ty<'a, 'gcx, 'tcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx>; + fn disr_incr<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, val: Option>) + -> Option>; + fn initial_discriminant<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Discr<'tcx>; +} + impl IntTypeExt for attr::IntType { fn to_ty<'a, 'gcx, 'tcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> { match *self { @@ -95,33 +133,26 @@ impl IntTypeExt for attr::IntType { } } - fn initial_discriminant<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Disr { - typed_literal!(tcx, *self, 0) - } - - fn assert_ty_matches(&self, val: Disr) { - match (*self, val) { - (SignedInt(ast::IntTy::I8), ConstInt::I8(_)) => {}, - (SignedInt(ast::IntTy::I16), ConstInt::I16(_)) => {}, - (SignedInt(ast::IntTy::I32), ConstInt::I32(_)) => {}, - (SignedInt(ast::IntTy::I64), ConstInt::I64(_)) => {}, - (SignedInt(ast::IntTy::I128), ConstInt::I128(_)) => {}, - (SignedInt(ast::IntTy::Isize), ConstInt::Isize(_)) => {}, - (UnsignedInt(ast::UintTy::U8), ConstInt::U8(_)) => {}, - (UnsignedInt(ast::UintTy::U16), ConstInt::U16(_)) => {}, - (UnsignedInt(ast::UintTy::U32), ConstInt::U32(_)) => {}, - (UnsignedInt(ast::UintTy::U64), ConstInt::U64(_)) => {}, - (UnsignedInt(ast::UintTy::U128), ConstInt::U128(_)) => {}, - (UnsignedInt(ast::UintTy::Usize), ConstInt::Usize(_)) => {}, - _ => bug!("disr type mismatch: {:?} vs {:?}", self, val), + fn initial_discriminant<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Discr<'tcx> { + Discr { + val: 0, + ty: self.to_ty(tcx) } } - fn disr_incr<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, val: Option) - -> Option { + fn disr_incr<'a, 'tcx>( + &self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + val: Option>, + ) -> Option> { if let Some(val) = val { - self.assert_ty_matches(val); - (val + typed_literal!(tcx, *self, 1)).ok() + assert_eq!(self.to_ty(tcx), val.ty); + let (new, oflo) = val.checked_add(tcx, 1); + if oflo { + None + } else { + Some(new) + } } else { Some(self.initial_discriminant(tcx)) } @@ -682,15 +713,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { }) } - pub fn const_usize(&self, val: u16) -> ConstInt { - match self.sess.target.usize_ty { - ast::UintTy::U16 => ConstInt::Usize(ConstUsize::Us16(val as u16)), - ast::UintTy::U32 => ConstInt::Usize(ConstUsize::Us32(val as u32)), - ast::UintTy::U64 => ConstInt::Usize(ConstUsize::Us64(val as u64)), - _ => bug!(), - } - } - /// Return whether the node pointed to by def_id is a static item, and its mutability pub fn is_static(&self, def_id: DefId) -> Option { if let Some(node) = self.hir.get_if_local(def_id) { diff --git a/src/librustc_const_math/int.rs b/src/librustc_const_math/int.rs deleted file mode 100644 index 4ec27d7ade560..0000000000000 --- a/src/librustc_const_math/int.rs +++ /dev/null @@ -1,590 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use std::cmp::Ordering; -use syntax::attr::IntType; -use syntax::ast::{IntTy, UintTy}; - -use super::isize::*; -use super::usize::*; -use super::err::*; - -#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, Hash, Eq, PartialEq)] -pub enum ConstInt { - I8(i8), - I16(i16), - I32(i32), - I64(i64), - I128(i128), - Isize(ConstIsize), - U8(u8), - U16(u16), - U32(u32), - U64(u64), - U128(u128), - Usize(ConstUsize), -} -pub use self::ConstInt::*; - - -macro_rules! bounds { - ($ct: ty, $($t:ident $min:ident $max:ident)*) => { - $( - pub const $min: $ct = $t::min_value() as $ct; - pub const $max: $ct = $t::max_value() as $ct; - )* - }; - ($ct: ty: $min_val: expr, $($t:ident $min:ident $max:ident)*) => { - $( - pub const $min: $ct = $min_val; - pub const $max: $ct = $t::max_value() as $ct; - )* - } -} - -mod ubounds { - #![allow(dead_code)] - bounds!{u128: 0, - i8 I8MIN I8MAX i16 I16MIN I16MAX i32 I32MIN I32MAX i64 I64MIN I64MAX i128 I128MIN I128MAX - u8 U8MIN U8MAX u16 U16MIN U16MAX u32 U32MIN U32MAX u64 U64MIN U64MAX u128 U128MIN U128MAX - // do not add constants for isize/usize, because these are guaranteed to be wrong for - // arbitrary host/target combinations - } -} - -mod ibounds { - #![allow(dead_code)] - bounds!(i128, u64 U64MIN U64MAX); - - pub const U128MIN: i128 = 0; - pub const U128MAX: i128 = i128::max_value(); - - bounds!{i128, - i8 I8MIN I8MAX i16 I16MIN I16MAX i32 I32MIN I32MAX i64 I64MIN I64MAX i128 I128MIN I128MAX - u8 U8MIN U8MAX u16 U16MIN U16MAX u32 U32MIN U32MAX - // do not add constants for isize/usize, because these are guaranteed to be wrong for - // arbitrary host/target combinations - } -} - -impl ConstInt { - /// Creates a new unsigned ConstInt with matching type while also checking that overflow does - /// not happen. - pub fn new_unsigned(val: u128, ty: UintTy, usize_ty: UintTy) -> Option { - match ty { - UintTy::U8 if val <= ubounds::U8MAX => Some(U8(val as u8)), - UintTy::U16 if val <= ubounds::U16MAX => Some(U16(val as u16)), - UintTy::U32 if val <= ubounds::U32MAX => Some(U32(val as u32)), - UintTy::U64 if val <= ubounds::U64MAX => Some(U64(val as u64)), - UintTy::Usize if val <= ubounds::U64MAX => ConstUsize::new(val as u64, usize_ty).ok() - .map(Usize), - UintTy::U128 => Some(U128(val)), - _ => None - } - } - - /// Creates a new signed ConstInt with matching type while also checking that overflow does - /// not happen. - pub fn new_signed(val: i128, ty: IntTy, isize_ty: IntTy) -> Option { - match ty { - IntTy::I8 if val <= ibounds::I8MAX => Some(I8(val as i8)), - IntTy::I16 if val <= ibounds::I16MAX => Some(I16(val as i16)), - IntTy::I32 if val <= ibounds::I32MAX => Some(I32(val as i32)), - IntTy::I64 if val <= ibounds::I64MAX => Some(I64(val as i64)), - IntTy::Isize if val <= ibounds::I64MAX => ConstIsize::new(val as i64, isize_ty).ok() - .map(Isize), - IntTy::I128 => Some(I128(val)), - _ => None - } - } - - /// Creates a new unsigned ConstInt with matching type. - pub fn new_unsigned_truncating(val: u128, ty: UintTy, usize_ty: UintTy) -> ConstInt { - match ty { - UintTy::U8 => U8(val as u8), - UintTy::U16 => U16(val as u16), - UintTy::U32 => U32(val as u32), - UintTy::U64 => U64(val as u64), - UintTy::Usize => Usize(ConstUsize::new_truncating(val, usize_ty)), - UintTy::U128 => U128(val) - } - } - - /// Creates a new signed ConstInt with matching type. - pub fn new_signed_truncating(val: i128, ty: IntTy, isize_ty: IntTy) -> ConstInt { - match ty { - IntTy::I8 => I8(val as i8), - IntTy::I16 => I16(val as i16), - IntTy::I32 => I32(val as i32), - IntTy::I64 => I64(val as i64), - IntTy::Isize => Isize(ConstIsize::new_truncating(val, isize_ty)), - IntTy::I128 => I128(val) - } - } - - /// Description of the type, not the value - pub fn description(&self) -> &'static str { - match *self { - I8(_) => "i8", - I16(_) => "i16", - I32(_) => "i32", - I64(_) => "i64", - I128(_) => "i128", - Isize(_) => "isize", - U8(_) => "u8", - U16(_) => "u16", - U32(_) => "u32", - U64(_) => "u64", - U128(_) => "u128", - Usize(_) => "usize", - } - } - - /// Erases the type and returns a u128. - /// This is not the same as `-5i8 as u128` but as `-5i8 as i128 as u128` - pub fn to_u128_unchecked(self) -> u128 { - match self { - I8(i) => i as i128 as u128, - I16(i) => i as i128 as u128, - I32(i) => i as i128 as u128, - I64(i) => i as i128 as u128, - I128(i) => i as i128 as u128, - Isize(Is16(i)) => i as i128 as u128, - Isize(Is32(i)) => i as i128 as u128, - Isize(Is64(i)) => i as i128 as u128, - U8(i) => i as u128, - U16(i) => i as u128, - U32(i) => i as u128, - U64(i) => i as u128, - U128(i) => i as u128, - Usize(Us16(i)) => i as u128, - Usize(Us32(i)) => i as u128, - Usize(Us64(i)) => i as u128, - } - } - - /// Converts the value to a `u32` if it's in the range 0...std::u32::MAX - pub fn to_u32(&self) -> Option { - self.to_u128().and_then(|v| if v <= u32::max_value() as u128 { - Some(v as u32) - } else { - None - }) - } - - /// Converts the value to a `u64` if it's in the range 0...std::u64::MAX - pub fn to_u64(&self) -> Option { - self.to_u128().and_then(|v| if v <= u64::max_value() as u128 { - Some(v as u64) - } else { - None - }) - } - - /// Converts the value to a `u128` if it's in the range 0...std::u128::MAX - pub fn to_u128(&self) -> Option { - match *self { - I8(v) if v >= 0 => Some(v as u128), - I16(v) if v >= 0 => Some(v as u128), - I32(v) if v >= 0 => Some(v as u128), - I64(v) if v >= 0 => Some(v as u128), - I128(v) if v >= 0 => Some(v as u128), - Isize(Is16(v)) if v >= 0 => Some(v as u128), - Isize(Is32(v)) if v >= 0 => Some(v as u128), - Isize(Is64(v)) if v >= 0 => Some(v as u128), - U8(v) => Some(v as u128), - U16(v) => Some(v as u128), - U32(v) => Some(v as u128), - U64(v) => Some(v as u128), - U128(v) => Some(v as u128), - Usize(Us16(v)) => Some(v as u128), - Usize(Us32(v)) => Some(v as u128), - Usize(Us64(v)) => Some(v as u128), - _ => None, - } - } - - pub fn is_negative(&self) -> bool { - match *self { - I8(v) => v < 0, - I16(v) => v < 0, - I32(v) => v < 0, - I64(v) => v < 0, - I128(v) => v < 0, - Isize(Is16(v)) => v < 0, - Isize(Is32(v)) => v < 0, - Isize(Is64(v)) => v < 0, - _ => false, - } - } - - /// Compares the values if they are of the same type - pub fn try_cmp(self, rhs: Self) -> Result<::std::cmp::Ordering, ConstMathErr> { - match (self, rhs) { - (I8(a), I8(b)) => Ok(a.cmp(&b)), - (I16(a), I16(b)) => Ok(a.cmp(&b)), - (I32(a), I32(b)) => Ok(a.cmp(&b)), - (I64(a), I64(b)) => Ok(a.cmp(&b)), - (I128(a), I128(b)) => Ok(a.cmp(&b)), - (Isize(Is16(a)), Isize(Is16(b))) => Ok(a.cmp(&b)), - (Isize(Is32(a)), Isize(Is32(b))) => Ok(a.cmp(&b)), - (Isize(Is64(a)), Isize(Is64(b))) => Ok(a.cmp(&b)), - (U8(a), U8(b)) => Ok(a.cmp(&b)), - (U16(a), U16(b)) => Ok(a.cmp(&b)), - (U32(a), U32(b)) => Ok(a.cmp(&b)), - (U64(a), U64(b)) => Ok(a.cmp(&b)), - (U128(a), U128(b)) => Ok(a.cmp(&b)), - (Usize(Us16(a)), Usize(Us16(b))) => Ok(a.cmp(&b)), - (Usize(Us32(a)), Usize(Us32(b))) => Ok(a.cmp(&b)), - (Usize(Us64(a)), Usize(Us64(b))) => Ok(a.cmp(&b)), - _ => Err(CmpBetweenUnequalTypes), - } - } - - /// Adds 1 to the value and wraps around if the maximum for the type is reached - pub fn wrap_incr(self) -> Self { - macro_rules! add1 { - ($e:expr) => { ($e).wrapping_add(1) } - } - match self { - ConstInt::I8(i) => ConstInt::I8(add1!(i)), - ConstInt::I16(i) => ConstInt::I16(add1!(i)), - ConstInt::I32(i) => ConstInt::I32(add1!(i)), - ConstInt::I64(i) => ConstInt::I64(add1!(i)), - ConstInt::I128(i) => ConstInt::I128(add1!(i)), - ConstInt::Isize(ConstIsize::Is16(i)) => ConstInt::Isize(ConstIsize::Is16(add1!(i))), - ConstInt::Isize(ConstIsize::Is32(i)) => ConstInt::Isize(ConstIsize::Is32(add1!(i))), - ConstInt::Isize(ConstIsize::Is64(i)) => ConstInt::Isize(ConstIsize::Is64(add1!(i))), - ConstInt::U8(i) => ConstInt::U8(add1!(i)), - ConstInt::U16(i) => ConstInt::U16(add1!(i)), - ConstInt::U32(i) => ConstInt::U32(add1!(i)), - ConstInt::U64(i) => ConstInt::U64(add1!(i)), - ConstInt::U128(i) => ConstInt::U128(add1!(i)), - ConstInt::Usize(ConstUsize::Us16(i)) => ConstInt::Usize(ConstUsize::Us16(add1!(i))), - ConstInt::Usize(ConstUsize::Us32(i)) => ConstInt::Usize(ConstUsize::Us32(add1!(i))), - ConstInt::Usize(ConstUsize::Us64(i)) => ConstInt::Usize(ConstUsize::Us64(add1!(i))), - } - } - - pub fn int_type(self) -> IntType { - match self { - ConstInt::I8(_) => IntType::SignedInt(IntTy::I8), - ConstInt::I16(_) => IntType::SignedInt(IntTy::I16), - ConstInt::I32(_) => IntType::SignedInt(IntTy::I32), - ConstInt::I64(_) => IntType::SignedInt(IntTy::I64), - ConstInt::I128(_) => IntType::SignedInt(IntTy::I128), - ConstInt::Isize(_) => IntType::SignedInt(IntTy::Isize), - ConstInt::U8(_) => IntType::UnsignedInt(UintTy::U8), - ConstInt::U16(_) => IntType::UnsignedInt(UintTy::U16), - ConstInt::U32(_) => IntType::UnsignedInt(UintTy::U32), - ConstInt::U64(_) => IntType::UnsignedInt(UintTy::U64), - ConstInt::U128(_) => IntType::UnsignedInt(UintTy::U128), - ConstInt::Usize(_) => IntType::UnsignedInt(UintTy::Usize), - } - } -} - -impl ::std::cmp::PartialOrd for ConstInt { - fn partial_cmp(&self, other: &Self) -> Option { - self.try_cmp(*other).ok() - } -} - -impl ::std::cmp::Ord for ConstInt { - fn cmp(&self, other: &Self) -> Ordering { - self.try_cmp(*other).unwrap() - } -} - -impl ::std::fmt::Display for ConstInt { - fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { - match *self { - I8(i) => write!(fmt, "{}i8", i), - I16(i) => write!(fmt, "{}i16", i), - I32(i) => write!(fmt, "{}i32", i), - I64(i) => write!(fmt, "{}i64", i), - I128(i) => write!(fmt, "{}i128", i), - Isize(i) => write!(fmt, "{}isize", i), - U8(i) => write!(fmt, "{}u8", i), - U16(i) => write!(fmt, "{}u16", i), - U32(i) => write!(fmt, "{}u32", i), - U64(i) => write!(fmt, "{}u64", i), - U128(i) => write!(fmt, "{}u128", i), - Usize(i) => write!(fmt, "{}usize", i), - } - } -} - -macro_rules! overflowing { - ($e:expr, $err:expr) => {{ - if $e.1 { - return Err(Overflow($err)); - } else { - $e.0 - } - }} -} - -macro_rules! impl_binop { - ($op:ident, $func:ident, $checked_func:ident) => { - impl ::std::ops::$op for ConstInt { - type Output = Result; - fn $func(self, rhs: Self) -> Result { - match (self, rhs) { - (I8(a), I8(b)) => a.$checked_func(b).map(I8), - (I16(a), I16(b)) => a.$checked_func(b).map(I16), - (I32(a), I32(b)) => a.$checked_func(b).map(I32), - (I64(a), I64(b)) => a.$checked_func(b).map(I64), - (I128(a), I128(b)) => a.$checked_func(b).map(I128), - (Isize(Is16(a)), Isize(Is16(b))) => a.$checked_func(b).map(Is16).map(Isize), - (Isize(Is32(a)), Isize(Is32(b))) => a.$checked_func(b).map(Is32).map(Isize), - (Isize(Is64(a)), Isize(Is64(b))) => a.$checked_func(b).map(Is64).map(Isize), - (U8(a), U8(b)) => a.$checked_func(b).map(U8), - (U16(a), U16(b)) => a.$checked_func(b).map(U16), - (U32(a), U32(b)) => a.$checked_func(b).map(U32), - (U64(a), U64(b)) => a.$checked_func(b).map(U64), - (U128(a), U128(b)) => a.$checked_func(b).map(U128), - (Usize(Us16(a)), Usize(Us16(b))) => a.$checked_func(b).map(Us16).map(Usize), - (Usize(Us32(a)), Usize(Us32(b))) => a.$checked_func(b).map(Us32).map(Usize), - (Usize(Us64(a)), Usize(Us64(b))) => a.$checked_func(b).map(Us64).map(Usize), - _ => return Err(UnequalTypes(Op::$op)), - }.ok_or(Overflow(Op::$op)) - } - } - } -} - -macro_rules! derive_binop { - ($op:ident, $func:ident) => { - impl ::std::ops::$op for ConstInt { - type Output = Result; - fn $func(self, rhs: Self) -> Result { - match (self, rhs) { - (I8(a), I8(b)) => Ok(I8(a.$func(b))), - (I16(a), I16(b)) => Ok(I16(a.$func(b))), - (I32(a), I32(b)) => Ok(I32(a.$func(b))), - (I64(a), I64(b)) => Ok(I64(a.$func(b))), - (I128(a), I128(b)) => Ok(I128(a.$func(b))), - (Isize(Is16(a)), Isize(Is16(b))) => Ok(Isize(Is16(a.$func(b)))), - (Isize(Is32(a)), Isize(Is32(b))) => Ok(Isize(Is32(a.$func(b)))), - (Isize(Is64(a)), Isize(Is64(b))) => Ok(Isize(Is64(a.$func(b)))), - (U8(a), U8(b)) => Ok(U8(a.$func(b))), - (U16(a), U16(b)) => Ok(U16(a.$func(b))), - (U32(a), U32(b)) => Ok(U32(a.$func(b))), - (U64(a), U64(b)) => Ok(U64(a.$func(b))), - (U128(a), U128(b)) => Ok(U128(a.$func(b))), - (Usize(Us16(a)), Usize(Us16(b))) => Ok(Usize(Us16(a.$func(b)))), - (Usize(Us32(a)), Usize(Us32(b))) => Ok(Usize(Us32(a.$func(b)))), - (Usize(Us64(a)), Usize(Us64(b))) => Ok(Usize(Us64(a.$func(b)))), - _ => Err(UnequalTypes(Op::$op)), - } - } - } - } -} - -impl_binop!(Add, add, checked_add); -impl_binop!(Sub, sub, checked_sub); -impl_binop!(Mul, mul, checked_mul); -derive_binop!(BitAnd, bitand); -derive_binop!(BitOr, bitor); -derive_binop!(BitXor, bitxor); - -const I128_MIN: i128 = ::std::i128::MIN; - -fn check_division( - lhs: ConstInt, - rhs: ConstInt, - op: Op, - zerr: ConstMathErr, -) -> Result<(), ConstMathErr> { - match (lhs, rhs) { - (I8(_), I8(0)) => Err(zerr), - (I16(_), I16(0)) => Err(zerr), - (I32(_), I32(0)) => Err(zerr), - (I64(_), I64(0)) => Err(zerr), - (I128(_), I128(0)) => Err(zerr), - (Isize(_), Isize(Is16(0))) => Err(zerr), - (Isize(_), Isize(Is32(0))) => Err(zerr), - (Isize(_), Isize(Is64(0))) => Err(zerr), - - (U8(_), U8(0)) => Err(zerr), - (U16(_), U16(0)) => Err(zerr), - (U32(_), U32(0)) => Err(zerr), - (U64(_), U64(0)) => Err(zerr), - (U128(_), U128(0)) => Err(zerr), - (Usize(_), Usize(Us16(0))) => Err(zerr), - (Usize(_), Usize(Us32(0))) => Err(zerr), - (Usize(_), Usize(Us64(0))) => Err(zerr), - - (I8(::std::i8::MIN), I8(-1)) => Err(Overflow(op)), - (I16(::std::i16::MIN), I16(-1)) => Err(Overflow(op)), - (I32(::std::i32::MIN), I32(-1)) => Err(Overflow(op)), - (I64(::std::i64::MIN), I64(-1)) => Err(Overflow(op)), - (I128(I128_MIN), I128(-1)) => Err(Overflow(op)), - (Isize(Is16(::std::i16::MIN)), Isize(Is16(-1))) => Err(Overflow(op)), - (Isize(Is32(::std::i32::MIN)), Isize(Is32(-1))) => Err(Overflow(op)), - (Isize(Is64(::std::i64::MIN)), Isize(Is64(-1))) => Err(Overflow(op)), - - _ => Ok(()), - } -} - -impl ::std::ops::Div for ConstInt { - type Output = Result; - fn div(self, rhs: Self) -> Result { - let (lhs, rhs) = (self, rhs); - check_division(lhs, rhs, Op::Div, DivisionByZero)?; - match (lhs, rhs) { - (I8(a), I8(b)) => Ok(I8(a/b)), - (I16(a), I16(b)) => Ok(I16(a/b)), - (I32(a), I32(b)) => Ok(I32(a/b)), - (I64(a), I64(b)) => Ok(I64(a/b)), - (I128(a), I128(b)) => Ok(I128(a/b)), - (Isize(Is16(a)), Isize(Is16(b))) => Ok(Isize(Is16(a/b))), - (Isize(Is32(a)), Isize(Is32(b))) => Ok(Isize(Is32(a/b))), - (Isize(Is64(a)), Isize(Is64(b))) => Ok(Isize(Is64(a/b))), - - (U8(a), U8(b)) => Ok(U8(a/b)), - (U16(a), U16(b)) => Ok(U16(a/b)), - (U32(a), U32(b)) => Ok(U32(a/b)), - (U64(a), U64(b)) => Ok(U64(a/b)), - (U128(a), U128(b)) => Ok(U128(a/b)), - (Usize(Us16(a)), Usize(Us16(b))) => Ok(Usize(Us16(a/b))), - (Usize(Us32(a)), Usize(Us32(b))) => Ok(Usize(Us32(a/b))), - (Usize(Us64(a)), Usize(Us64(b))) => Ok(Usize(Us64(a/b))), - - _ => Err(UnequalTypes(Op::Div)), - } - } -} - -impl ::std::ops::Rem for ConstInt { - type Output = Result; - fn rem(self, rhs: Self) -> Result { - let (lhs, rhs) = (self, rhs); - // should INT_MIN%-1 be zero or an error? - check_division(lhs, rhs, Op::Rem, RemainderByZero)?; - match (lhs, rhs) { - (I8(a), I8(b)) => Ok(I8(a%b)), - (I16(a), I16(b)) => Ok(I16(a%b)), - (I32(a), I32(b)) => Ok(I32(a%b)), - (I64(a), I64(b)) => Ok(I64(a%b)), - (I128(a), I128(b)) => Ok(I128(a%b)), - (Isize(Is16(a)), Isize(Is16(b))) => Ok(Isize(Is16(a%b))), - (Isize(Is32(a)), Isize(Is32(b))) => Ok(Isize(Is32(a%b))), - (Isize(Is64(a)), Isize(Is64(b))) => Ok(Isize(Is64(a%b))), - - (U8(a), U8(b)) => Ok(U8(a%b)), - (U16(a), U16(b)) => Ok(U16(a%b)), - (U32(a), U32(b)) => Ok(U32(a%b)), - (U64(a), U64(b)) => Ok(U64(a%b)), - (U128(a), U128(b)) => Ok(U128(a%b)), - (Usize(Us16(a)), Usize(Us16(b))) => Ok(Usize(Us16(a%b))), - (Usize(Us32(a)), Usize(Us32(b))) => Ok(Usize(Us32(a%b))), - (Usize(Us64(a)), Usize(Us64(b))) => Ok(Usize(Us64(a%b))), - - _ => Err(UnequalTypes(Op::Rem)), - } - } -} - -impl ::std::ops::Shl for ConstInt { - type Output = Result; - fn shl(self, rhs: Self) -> Result { - let b = rhs.to_u32().ok_or(ShiftNegative)?; - match self { - I8(a) => Ok(I8(overflowing!(a.overflowing_shl(b), Op::Shl))), - I16(a) => Ok(I16(overflowing!(a.overflowing_shl(b), Op::Shl))), - I32(a) => Ok(I32(overflowing!(a.overflowing_shl(b), Op::Shl))), - I64(a) => Ok(I64(overflowing!(a.overflowing_shl(b), Op::Shl))), - I128(a) => Ok(I128(overflowing!(a.overflowing_shl(b), Op::Shl))), - Isize(Is16(a)) => Ok(Isize(Is16(overflowing!(a.overflowing_shl(b), Op::Shl)))), - Isize(Is32(a)) => Ok(Isize(Is32(overflowing!(a.overflowing_shl(b), Op::Shl)))), - Isize(Is64(a)) => Ok(Isize(Is64(overflowing!(a.overflowing_shl(b), Op::Shl)))), - U8(a) => Ok(U8(overflowing!(a.overflowing_shl(b), Op::Shl))), - U16(a) => Ok(U16(overflowing!(a.overflowing_shl(b), Op::Shl))), - U32(a) => Ok(U32(overflowing!(a.overflowing_shl(b), Op::Shl))), - U64(a) => Ok(U64(overflowing!(a.overflowing_shl(b), Op::Shl))), - U128(a) => Ok(U128(overflowing!(a.overflowing_shl(b), Op::Shl))), - Usize(Us16(a)) => Ok(Usize(Us16(overflowing!(a.overflowing_shl(b), Op::Shl)))), - Usize(Us32(a)) => Ok(Usize(Us32(overflowing!(a.overflowing_shl(b), Op::Shl)))), - Usize(Us64(a)) => Ok(Usize(Us64(overflowing!(a.overflowing_shl(b), Op::Shl)))), - } - } -} - -impl ::std::ops::Shr for ConstInt { - type Output = Result; - fn shr(self, rhs: Self) -> Result { - let b = rhs.to_u32().ok_or(ShiftNegative)?; - match self { - I8(a) => Ok(I8(overflowing!(a.overflowing_shr(b), Op::Shr))), - I16(a) => Ok(I16(overflowing!(a.overflowing_shr(b), Op::Shr))), - I32(a) => Ok(I32(overflowing!(a.overflowing_shr(b), Op::Shr))), - I64(a) => Ok(I64(overflowing!(a.overflowing_shr(b), Op::Shr))), - I128(a) => Ok(I128(overflowing!(a.overflowing_shr(b), Op::Shr))), - Isize(Is16(a)) => Ok(Isize(Is16(overflowing!(a.overflowing_shr(b), Op::Shr)))), - Isize(Is32(a)) => Ok(Isize(Is32(overflowing!(a.overflowing_shr(b), Op::Shr)))), - Isize(Is64(a)) => Ok(Isize(Is64(overflowing!(a.overflowing_shr(b), Op::Shr)))), - U8(a) => Ok(U8(overflowing!(a.overflowing_shr(b), Op::Shr))), - U16(a) => Ok(U16(overflowing!(a.overflowing_shr(b), Op::Shr))), - U32(a) => Ok(U32(overflowing!(a.overflowing_shr(b), Op::Shr))), - U64(a) => Ok(U64(overflowing!(a.overflowing_shr(b), Op::Shr))), - U128(a) => Ok(U128(overflowing!(a.overflowing_shr(b), Op::Shr))), - Usize(Us16(a)) => Ok(Usize(Us16(overflowing!(a.overflowing_shr(b), Op::Shr)))), - Usize(Us32(a)) => Ok(Usize(Us32(overflowing!(a.overflowing_shr(b), Op::Shr)))), - Usize(Us64(a)) => Ok(Usize(Us64(overflowing!(a.overflowing_shr(b), Op::Shr)))), - } - } -} - -impl ::std::ops::Neg for ConstInt { - type Output = Result; - fn neg(self) -> Result { - match self { - I8(a) => Ok(I8(overflowing!(a.overflowing_neg(), Op::Neg))), - I16(a) => Ok(I16(overflowing!(a.overflowing_neg(), Op::Neg))), - I32(a) => Ok(I32(overflowing!(a.overflowing_neg(), Op::Neg))), - I64(a) => Ok(I64(overflowing!(a.overflowing_neg(), Op::Neg))), - I128(a) => Ok(I128(overflowing!(a.overflowing_neg(), Op::Neg))), - Isize(Is16(a)) => Ok(Isize(Is16(overflowing!(a.overflowing_neg(), Op::Neg)))), - Isize(Is32(a)) => Ok(Isize(Is32(overflowing!(a.overflowing_neg(), Op::Neg)))), - Isize(Is64(a)) => Ok(Isize(Is64(overflowing!(a.overflowing_neg(), Op::Neg)))), - a@U8(0) | a@U16(0) | a@U32(0) | a@U64(0) | a@U128(0) | - a@Usize(Us16(0)) | a@Usize(Us32(0)) | a@Usize(Us64(0)) => Ok(a), - U8(_) | U16(_) | U32(_) | U64(_) | U128(_) | Usize(_) => Err(UnsignedNegation), - } - } -} - -impl ::std::ops::Not for ConstInt { - type Output = Result; - fn not(self) -> Result { - match self { - I8(a) => Ok(I8(!a)), - I16(a) => Ok(I16(!a)), - I32(a) => Ok(I32(!a)), - I64(a) => Ok(I64(!a)), - I128(a) => Ok(I128(!a)), - Isize(Is16(a)) => Ok(Isize(Is16(!a))), - Isize(Is32(a)) => Ok(Isize(Is32(!a))), - Isize(Is64(a)) => Ok(Isize(Is64(!a))), - U8(a) => Ok(U8(!a)), - U16(a) => Ok(U16(!a)), - U32(a) => Ok(U32(!a)), - U64(a) => Ok(U64(!a)), - U128(a) => Ok(U128(!a)), - Usize(Us16(a)) => Ok(Usize(Us16(!a))), - Usize(Us32(a)) => Ok(Usize(Us32(!a))), - Usize(Us64(a)) => Ok(Usize(Us64(!a))), - } - } -} diff --git a/src/librustc_const_math/isize.rs b/src/librustc_const_math/isize.rs deleted file mode 100644 index 18acc782775d8..0000000000000 --- a/src/librustc_const_math/isize.rs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use syntax::ast; -use super::err::*; - -/// Depending on the target only one variant is ever used in a compilation. -/// Anything else is an error. This invariant is checked at several locations -#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, Hash, Eq, PartialEq)] -pub enum ConstIsize { - Is16(i16), - Is32(i32), - Is64(i64), -} -pub use self::ConstIsize::*; - -impl ::std::fmt::Display for ConstIsize { - fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { - write!(fmt, "{}", self.as_i64()) - } -} - -impl ConstIsize { - pub fn as_i64(self) -> i64 { - match self { - Is16(i) => i as i64, - Is32(i) => i as i64, - Is64(i) => i, - } - } - pub fn new(i: i64, isize_ty: ast::IntTy) -> Result { - match isize_ty { - ast::IntTy::I16 if i as i16 as i64 == i => Ok(Is16(i as i16)), - ast::IntTy::I16 => Err(LitOutOfRange(ast::IntTy::Isize)), - ast::IntTy::I32 if i as i32 as i64 == i => Ok(Is32(i as i32)), - ast::IntTy::I32 => Err(LitOutOfRange(ast::IntTy::Isize)), - ast::IntTy::I64 => Ok(Is64(i)), - _ => unreachable!(), - } - } - pub fn new_truncating(i: i128, isize_ty: ast::IntTy) -> Self { - match isize_ty { - ast::IntTy::I16 => Is16(i as i16), - ast::IntTy::I32 => Is32(i as i32), - ast::IntTy::I64 => Is64(i as i64), - _ => unreachable!(), - } - } -} diff --git a/src/librustc_const_math/lib.rs b/src/librustc_const_math/lib.rs index 2d98bc48d2816..5555e727a9552 100644 --- a/src/librustc_const_math/lib.rs +++ b/src/librustc_const_math/lib.rs @@ -29,13 +29,7 @@ extern crate syntax; extern crate serialize as rustc_serialize; // used by deriving mod float; -mod int; -mod usize; -mod isize; mod err; pub use float::*; -pub use int::*; -pub use usize::*; -pub use isize::*; pub use err::{ConstMathErr, Op}; diff --git a/src/librustc_const_math/usize.rs b/src/librustc_const_math/usize.rs deleted file mode 100644 index 56995f08f05b8..0000000000000 --- a/src/librustc_const_math/usize.rs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use syntax::ast; -use super::err::*; - -/// Depending on the target only one variant is ever used in a compilation. -/// Anything else is an error. This invariant is checked at several locations -#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, Hash, Eq, PartialEq)] -pub enum ConstUsize { - Us16(u16), - Us32(u32), - Us64(u64), -} -pub use self::ConstUsize::*; - -impl ::std::fmt::Display for ConstUsize { - fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { - write!(fmt, "{}", self.as_u64()) - } -} - -impl ConstUsize { - pub fn as_u64(self) -> u64 { - match self { - Us16(i) => i as u64, - Us32(i) => i as u64, - Us64(i) => i, - } - } - pub fn new(i: u64, usize_ty: ast::UintTy) -> Result { - match usize_ty { - ast::UintTy::U16 if i as u16 as u64 == i => Ok(Us16(i as u16)), - ast::UintTy::U16 => Err(ULitOutOfRange(ast::UintTy::Usize)), - ast::UintTy::U32 if i as u32 as u64 == i => Ok(Us32(i as u32)), - ast::UintTy::U32 => Err(ULitOutOfRange(ast::UintTy::Usize)), - ast::UintTy::U64 => Ok(Us64(i)), - _ => unreachable!(), - } - } - pub fn new_truncating(i: u128, usize_ty: ast::UintTy) -> Self { - match usize_ty { - ast::UintTy::U16 => Us16(i as u16), - ast::UintTy::U32 => Us32(i as u32), - ast::UintTy::U64 => Us64(i as u64), - _ => unreachable!(), - } - } -} diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 35aa5adf76d7c..059fd9111b689 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -1316,7 +1316,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { self.check_aggregate_rvalue(mir, rvalue, ak, ops, location) } - Rvalue::Repeat(operand, const_usize) => if const_usize.as_u64() > 1 { + Rvalue::Repeat(operand, len) => if *len > 1 { let operand_ty = operand.ty(mir, tcx); let trait_ref = ty::TraitRef { diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index ca6e7c2c41580..e2cc58b1fb050 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -10,8 +10,6 @@ //! See docs in build/expr/mod.rs -use std; - use rustc_const_math::{ConstMathErr, Op}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::indexed_vec::Idx; @@ -19,7 +17,6 @@ use rustc_data_structures::indexed_vec::Idx; use build::{BlockAnd, BlockAndExtension, Builder}; use build::expr::category::{Category, RvalueFunc}; use hair::*; -use rustc_const_math::{ConstInt, ConstIsize}; use rustc::middle::const_val::ConstVal; use rustc::middle::region; use rustc::ty::{self, Ty}; @@ -385,31 +382,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // Helper to get a `-1` value of the appropriate type fn neg_1_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> { - let literal = match ty.sty { - ty::TyInt(ity) => { - let val = match ity { - ast::IntTy::I8 => ConstInt::I8(-1), - ast::IntTy::I16 => ConstInt::I16(-1), - ast::IntTy::I32 => ConstInt::I32(-1), - ast::IntTy::I64 => ConstInt::I64(-1), - ast::IntTy::I128 => ConstInt::I128(-1), - ast::IntTy::Isize => { - let int_ty = self.hir.tcx().sess.target.isize_ty; - let val = ConstIsize::new(-1, int_ty).unwrap(); - ConstInt::Isize(val) - } - }; - - Literal::Value { - value: self.hir.tcx().mk_const(ty::Const { - val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(val.to_u128_unchecked()))), - ty - }) - } - } - _ => { - span_bug!(span, "Invalid type for neg_1_literal: `{:?}`", ty) - } + let literal = Literal::Value { + value: self.hir.tcx().mk_const(ty::Const { + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(-1i128 as u128))), + ty + }) }; self.literal_operand(span, ty, literal) @@ -419,30 +396,22 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { fn minval_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> { let literal = match ty.sty { ty::TyInt(ity) => { + let ity = match ity { + ast::IntTy::Isize => self.hir.tcx().sess.target.isize_ty, + other => other, + }; let val = match ity { - ast::IntTy::I8 => ConstInt::I8(i8::min_value()), - ast::IntTy::I16 => ConstInt::I16(i16::min_value()), - ast::IntTy::I32 => ConstInt::I32(i32::min_value()), - ast::IntTy::I64 => ConstInt::I64(i64::min_value()), - ast::IntTy::I128 => ConstInt::I128(i128::min_value()), - ast::IntTy::Isize => { - let int_ty = self.hir.tcx().sess.target.isize_ty; - let min = match int_ty { - ast::IntTy::I16 => std::i16::MIN as i64, - ast::IntTy::I32 => std::i32::MIN as i64, - ast::IntTy::I64 => std::i64::MIN, - _ => unreachable!() - }; - let val = ConstIsize::new(min, int_ty).unwrap(); - ConstInt::Isize(val) - } + ast::IntTy::I8 => i8::min_value() as i128, + ast::IntTy::I16 => i16::min_value() as i128, + ast::IntTy::I32 => i32::min_value() as i128, + ast::IntTy::I64 => i64::min_value() as i128, + ast::IntTy::I128 => i128::min_value() as i128, + ast::IntTy::Isize => unreachable!(), }; Literal::Value { value: self.hir.tcx().mk_const(ty::Const { - val: ConstVal::Value(Value::ByVal(PrimVal::Bytes( - val.to_u128_unchecked() - ))), + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(val as u128))), ty }) } diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index 4a5d9e228815e..09579eaecb2de 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -197,7 +197,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let tcx = self.hir.tcx(); for (idx, discr) in adt_def.discriminants(tcx).enumerate() { target_blocks.place_back() <- if variants.contains(idx) { - values.push(discr.to_u128_unchecked()); + values.push(discr.val); *(targets.place_back() <- self.cfg.start_new_block()) } else { if otherwise_block.is_none() { diff --git a/src/librustc_mir/const_eval/pattern.rs b/src/librustc_mir/const_eval/pattern.rs index c1618d8e8a959..0b8a96332b0f4 100644 --- a/src/librustc_mir/const_eval/pattern.rs +++ b/src/librustc_mir/const_eval/pattern.rs @@ -799,7 +799,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { ).unwrap(); let variant_index = adt_def .discriminants(self.tcx) - .position(|var| var.to_u128_unchecked() == discr) + .position(|var| var.val == discr) .unwrap(); PatternKind::Variant { adt_def, diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index db44d870acb01..6b5a5c5984216 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -523,10 +523,10 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, promoted: None }; let count = match cx.tcx.at(c.span).const_eval(cx.param_env.and(global_id)) { - Ok(cv) => cv.val.unwrap_usize(cx.tcx), + Ok(cv) => cv.val.unwrap_u64(), Err(e) => { e.report(cx.tcx, cx.tcx.def_span(def_id), "array length"); - ConstUsize::new(0, cx.tcx.sess.target.usize_ty).unwrap() + 0 }, }; diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index 24bf9445d45c0..0b59ce9e117b8 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -29,7 +29,7 @@ use syntax::ast::{self, LitKind}; use syntax::attr; use syntax::symbol::Symbol; use rustc::hir; -use rustc_const_math::{ConstInt, ConstUsize}; +use rustc_const_math::ConstFloat; use rustc_data_structures::sync::Lrc; use rustc::mir::interpret::{Value, PrimVal}; @@ -115,16 +115,11 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { } pub fn usize_literal(&mut self, value: u64) -> Literal<'tcx> { - match ConstUsize::new(value, self.tcx.sess.target.usize_ty) { - Ok(val) => { - Literal::Value { - value: self.tcx.mk_const(ty::Const { - val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(val.as_u64() as u128))), - ty: self.tcx.types.usize - }) - } - } - Err(_) => bug!("usize literal out of range for target"), + Literal::Value { + value: self.tcx.mk_const(ty::Const { + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(value as u128))), + ty: self.tcx.types.usize + }) } } diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs index 329365c4415e8..a7cfa94f6961d 100644 --- a/src/librustc_mir/hair/mod.rs +++ b/src/librustc_mir/hair/mod.rs @@ -14,7 +14,6 @@ //! unit-tested and separated from the Rust source and compiler data //! structures. -use rustc_const_math::ConstUsize; use rustc::mir::{BinOp, BorrowKind, Field, Literal, UnOp}; use rustc::hir::def_id::DefId; use rustc::middle::region; @@ -246,7 +245,7 @@ pub enum ExprKind<'tcx> { }, Repeat { value: ExprRef<'tcx>, - count: ConstUsize, + count: u64, }, Array { fields: Vec>, diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index eaa1bcb96a903..08152019003f1 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -760,7 +760,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M if let ty::TyAdt(adt_def, _) = ty.sty { trace!("Read discriminant {}, valid discriminants {:?}", discr_val, adt_def.discriminants(self.tcx).collect::>()); if adt_def.discriminants(self.tcx).all(|v| { - discr_val != v.to_u128_unchecked() + discr_val != v.val }) { return err!(InvalidDiscriminant); @@ -916,7 +916,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M layout::Variants::Tagged { .. } => { let discr_val = dest_ty.ty_adt_def().unwrap() .discriminant_for_variant(self.tcx, variant_index) - .to_u128_unchecked(); + .val; let (discr_dest, discr) = self.place_field(dest, mir::Field::new(0), layout)?; self.write_primval(discr_dest, PrimVal::Bytes(discr_val), discr.ty)?; diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index ae790971ec863..9aff7fa2a2c71 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -16,7 +16,6 @@ use rustc::mir::*; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::subst::{Kind, Subst, Substs}; use rustc::ty::maps::Providers; -use rustc_const_math::ConstUsize; use rustc::mir::interpret::{Value, PrimVal}; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; @@ -508,13 +507,7 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { ty: self.tcx.types.usize, literal: Literal::Value { value: self.tcx.mk_const(ty::Const { - val: { - let value = ConstUsize::new( - value, - self.tcx.sess.target.usize_ty, - ).unwrap().as_u64(); - ConstVal::Value(Value::ByVal(PrimVal::Bytes(value.into()))) - }, + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(value.into()))), ty: self.tcx.types.usize, }) } diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs index fbdeb3334abd9..458dd488409e4 100644 --- a/src/librustc_mir/util/elaborate_drops.rs +++ b/src/librustc_mir/util/elaborate_drops.rs @@ -426,7 +426,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> variant_path, &adt.variants[variant_index], substs); - values.push(discr.to_u128().unwrap()); + values.push(discr.val); if let Unwind::To(unwind) = unwind { // We can't use the half-ladder from the original // drop ladder, because this breaks the diff --git a/src/librustc_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs index e4705674e2292..4a4ce63cc1d4e 100644 --- a/src/librustc_passes/mir_stats.rs +++ b/src/librustc_passes/mir_stats.rs @@ -12,7 +12,6 @@ // pieces of MIR. The resulting numbers are good approximations but not // completely accurate (some things might be counted twice, others missed). -use rustc_const_math::{ConstUsize}; use rustc::mir::{AggregateKind, AssertMessage, BasicBlock, BasicBlockData}; use rustc::mir::{Constant, Literal, Location, Local, LocalDecl}; use rustc::mir::{Place, PlaceElem, PlaceProjection}; @@ -265,13 +264,6 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> { self.super_const(constant); } - fn visit_const_usize(&mut self, - const_usize: &ConstUsize, - _: Location) { - self.record("ConstUsize", const_usize); - self.super_const_usize(const_usize); - } - fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) { diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 314b8c59df5ab..bae9fe8bd1b0d 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -334,14 +334,6 @@ pub fn cast_shift_expr_rhs( cast_shift_rhs(op, lhs, rhs, |a, b| cx.trunc(a, b), |a, b| cx.zext(a, b)) } -pub fn cast_shift_const_rhs(op: hir::BinOp_, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - cast_shift_rhs(op, - lhs, - rhs, - |a, b| unsafe { llvm::LLVMConstTrunc(a, b.to_ref()) }, - |a, b| unsafe { llvm::LLVMConstZExt(a, b.to_ref()) }) -} - fn cast_shift_rhs(op: hir::BinOp_, lhs: ValueRef, rhs: ValueRef, diff --git a/src/librustc_trans/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs index 0fe425fb7ea1e..d20b51ca0fdf4 100644 --- a/src/librustc_trans/debuginfo/metadata.rs +++ b/src/librustc_trans/debuginfo/metadata.rs @@ -1378,7 +1378,7 @@ fn prepare_enum_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, DIB(cx), name.as_ptr(), // FIXME: what if enumeration has i128 discriminant? - discr.to_u128_unchecked() as u64) + discr.val as u64) } }) .collect(); diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 1d04e30eb3e83..954abf4c5d703 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -9,7 +9,6 @@ // except according to those terms. use llvm::{self, ValueRef}; -use rustc_const_math::{ConstInt, ConstMathErr}; use rustc::middle::const_val::{ConstVal, ConstEvalErr}; use rustc_mir::interpret::{read_target_uint, const_val_field}; use rustc::hir::def_id::DefId; @@ -17,13 +16,11 @@ use rustc::traits; use rustc::mir; use rustc_data_structures::indexed_vec::Idx; use rustc::mir::interpret::{Allocation, GlobalId, MemoryPointer, PrimVal, Value as MiriValue}; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::{self, Ty}; use rustc::ty::layout::{self, HasDataLayout, LayoutOf, Scalar}; -use base; use builder::Builder; use common::{CodegenCx}; use common::{C_bytes, C_struct, C_uint_big, C_undef, C_usize}; -use common::const_to_opt_u128; use consts; use type_of::LayoutLlvmExt; use type_::Type; @@ -31,110 +28,6 @@ use type_::Type; use super::super::callee; use super::FunctionCx; -fn to_const_int(value: ValueRef, t: Ty, tcx: TyCtxt) -> Option { - match t.sty { - ty::TyInt(int_type) => const_to_opt_u128(value, true) - .and_then(|input| ConstInt::new_signed(input as i128, int_type, - tcx.sess.target.isize_ty)), - ty::TyUint(uint_type) => const_to_opt_u128(value, false) - .and_then(|input| ConstInt::new_unsigned(input, uint_type, - tcx.sess.target.usize_ty)), - _ => None - - } -} - -pub fn const_scalar_binop(op: mir::BinOp, - lhs: ValueRef, - rhs: ValueRef, - input_ty: Ty) -> ValueRef { - assert!(!input_ty.is_simd()); - let is_float = input_ty.is_fp(); - let signed = input_ty.is_signed(); - - unsafe { - match op { - mir::BinOp::Add if is_float => llvm::LLVMConstFAdd(lhs, rhs), - mir::BinOp::Add => llvm::LLVMConstAdd(lhs, rhs), - - mir::BinOp::Sub if is_float => llvm::LLVMConstFSub(lhs, rhs), - mir::BinOp::Sub => llvm::LLVMConstSub(lhs, rhs), - - mir::BinOp::Mul if is_float => llvm::LLVMConstFMul(lhs, rhs), - mir::BinOp::Mul => llvm::LLVMConstMul(lhs, rhs), - - mir::BinOp::Div if is_float => llvm::LLVMConstFDiv(lhs, rhs), - mir::BinOp::Div if signed => llvm::LLVMConstSDiv(lhs, rhs), - mir::BinOp::Div => llvm::LLVMConstUDiv(lhs, rhs), - - mir::BinOp::Rem if is_float => llvm::LLVMConstFRem(lhs, rhs), - mir::BinOp::Rem if signed => llvm::LLVMConstSRem(lhs, rhs), - mir::BinOp::Rem => llvm::LLVMConstURem(lhs, rhs), - - mir::BinOp::BitXor => llvm::LLVMConstXor(lhs, rhs), - mir::BinOp::BitAnd => llvm::LLVMConstAnd(lhs, rhs), - mir::BinOp::BitOr => llvm::LLVMConstOr(lhs, rhs), - mir::BinOp::Shl => { - let rhs = base::cast_shift_const_rhs(op.to_hir_binop(), lhs, rhs); - llvm::LLVMConstShl(lhs, rhs) - } - mir::BinOp::Shr => { - let rhs = base::cast_shift_const_rhs(op.to_hir_binop(), lhs, rhs); - if signed { llvm::LLVMConstAShr(lhs, rhs) } - else { llvm::LLVMConstLShr(lhs, rhs) } - } - mir::BinOp::Eq | mir::BinOp::Ne | - mir::BinOp::Lt | mir::BinOp::Le | - mir::BinOp::Gt | mir::BinOp::Ge => { - if is_float { - let cmp = base::bin_op_to_fcmp_predicate(op.to_hir_binop()); - llvm::LLVMConstFCmp(cmp, lhs, rhs) - } else { - let cmp = base::bin_op_to_icmp_predicate(op.to_hir_binop(), - signed); - llvm::LLVMConstICmp(cmp, lhs, rhs) - } - } - mir::BinOp::Offset => unreachable!("BinOp::Offset in const-eval!") - } - } -} - -pub fn const_scalar_checked_binop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - op: mir::BinOp, - lllhs: ValueRef, - llrhs: ValueRef, - input_ty: Ty<'tcx>) - -> Option<(ValueRef, bool)> { - if let (Some(lhs), Some(rhs)) = (to_const_int(lllhs, input_ty, tcx), - to_const_int(llrhs, input_ty, tcx)) { - let result = match op { - mir::BinOp::Add => lhs + rhs, - mir::BinOp::Sub => lhs - rhs, - mir::BinOp::Mul => lhs * rhs, - mir::BinOp::Shl => lhs << rhs, - mir::BinOp::Shr => lhs >> rhs, - _ => { - bug!("Operator `{:?}` is not a checkable operator", op) - } - }; - - let of = match result { - Ok(_) => false, - Err(ConstMathErr::Overflow(_)) | - Err(ConstMathErr::ShiftNegative) => true, - Err(err) => { - bug!("Operator `{:?}` on `{:?}` and `{:?}` errored: {}", - op, lhs, rhs, err.description()); - } - }; - - Some((const_scalar_binop(op, lllhs, llrhs, input_ty), of)) - } else { - None - } -} - pub fn primval_to_llvm(cx: &CodegenCx, cv: PrimVal, scalar: &Scalar, diff --git a/src/librustc_trans/mir/place.rs b/src/librustc_trans/mir/place.rs index 99770476e12f9..b340d91b02708 100644 --- a/src/librustc_trans/mir/place.rs +++ b/src/librustc_trans/mir/place.rs @@ -328,7 +328,7 @@ impl<'a, 'tcx> PlaceRef<'tcx> { let ptr = self.project_field(bx, 0); let to = self.layout.ty.ty_adt_def().unwrap() .discriminant_for_variant(bx.tcx(), variant_index) - .to_u128_unchecked() as u64; + .val as u64; bx.store(C_int(ptr.layout.llvm_type(bx.cx), to as i64), ptr.llval, ptr.align); } diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index b0cb7de824eb5..fa0514952d213 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -30,7 +30,6 @@ use type_of::LayoutLlvmExt; use value::Value; use super::{FunctionCx, LocalRef}; -use super::constant::const_scalar_checked_binop; use super::operand::{OperandRef, OperandValue}; use super::place::PlaceRef; @@ -122,7 +121,6 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { } } - let count = count.as_u64(); let count = C_usize(bx.cx, count); let end = dest.project_index(&bx, count).llval; @@ -645,14 +643,6 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { return OperandValue::Pair(val, C_bool(bx.cx, false)); } - // First try performing the operation on constants, which - // will only succeed if both operands are constant. - // This is necessary to determine when an overflow Assert - // will always panic at runtime, and produce a warning. - if let Some((val, of)) = const_scalar_checked_binop(bx.tcx(), op, lhs, rhs, input_ty) { - return OperandValue::Pair(val, C_bool(bx.cx, of)); - } - let (val, of) = match op { // These are checked using intrinsics mir::BinOp::Add | mir::BinOp::Sub | mir::BinOp::Mul => { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 56f2b30190f1a..4e1fd987d90da 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -100,7 +100,8 @@ use rustc::ty::{self, Ty, TyCtxt, Visibility, ToPredicate}; use rustc::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc::ty::fold::TypeFoldable; use rustc::ty::maps::Providers; -use rustc::ty::util::{Representability, IntTypeExt}; +use rustc::ty::util::{Representability, IntTypeExt, Discr}; +use rustc::ty::layout::LayoutOf; use errors::{DiagnosticBuilder, DiagnosticId}; use require_c_abi_if_variadic; @@ -133,7 +134,6 @@ use rustc::hir::itemlikevisit::ItemLikeVisitor; use rustc::hir::map::Node; use rustc::hir::{self, PatKind}; use rustc::middle::lang_items; -use rustc_const_math::ConstInt; mod autoderef; pub mod dropck; @@ -1632,10 +1632,10 @@ pub fn check_enum<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } - let mut disr_vals: Vec = Vec::new(); + let mut disr_vals: Vec> = Vec::new(); for (discr, v) in def.discriminants(tcx).zip(vs) { // Check for duplicate discriminant values - if let Some(i) = disr_vals.iter().position(|&x| x == discr) { + if let Some(i) = disr_vals.iter().position(|&x| x.val == discr.val) { let variant_i_node_id = tcx.hir.as_local_node_id(def.variants[i].did).unwrap(); let variant_i = tcx.hir.expect_variant(variant_i_node_id); let i_span = match variant_i.node.disr_expr { diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index bb41acc541001..b040bd014e30f 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -39,8 +39,7 @@ use rustc::ty::maps::Providers; use rustc::ty::util::IntTypeExt; use rustc::util::nodemap::{FxHashSet, FxHashMap}; use rustc::mir::interpret::{GlobalId, Value, PrimVal}; - -use rustc_const_math::ConstInt; +use rustc::ty::util::Discr; use syntax::{abi, ast}; use syntax::ast::MetaItemKind; @@ -516,11 +515,11 @@ fn convert_enum_variant_types<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let def = tcx.adt_def(def_id); let repr_type = def.repr.discr_type(); let initial = repr_type.initial_discriminant(tcx); - let mut prev_discr = None::; + let mut prev_discr = None::>; // fill the discriminant values and field types for variant in variants { - let wrapped_discr = prev_discr.map_or(initial, |d| d.wrap_incr()); + let wrapped_discr = prev_discr.map_or(initial, |d| d.wrap_incr(tcx)); prev_discr = Some(if let Some(e) = variant.node.disr_expr { let expr_did = tcx.hir.local_def_id(e.node_id); let substs = Substs::identity_for_item(tcx, expr_did); @@ -535,19 +534,17 @@ fn convert_enum_variant_types<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // so we need to report the real error if let Err(ref err) = result { err.report(tcx, variant.span, "enum discriminant"); -} + } match result { + // FIXME: just use `to_raw_bits` here? Ok(&ty::Const { val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))), .. }) => { - use syntax::attr::IntType; - Some(match repr_type { - IntType::SignedInt(int_type) => ConstInt::new_signed( - b as i128, int_type, tcx.sess.target.isize_ty).unwrap(), - IntType::UnsignedInt(uint_type) => ConstInt::new_unsigned( - b, uint_type, tcx.sess.target.usize_ty).unwrap(), + Some(Discr { + val: b, + ty: initial.ty, }) } _ => None diff --git a/src/test/compile-fail/issue-27592.rs b/src/test/compile-fail/issue-27592.rs index 731d4fb2bf693..bc29594114746 100644 --- a/src/test/compile-fail/issue-27592.rs +++ b/src/test/compile-fail/issue-27592.rs @@ -26,4 +26,5 @@ fn main() { write(|| format_args!("{}", String::from("Hello world"))); //~^ ERROR borrowed value does not live long enough //~| ERROR borrowed value does not live long enough + //~| ERROR borrowed value does not live long enough } diff --git a/src/test/ui/issue-15524.rs b/src/test/ui/issue-15524.rs index 85214bd86336c..7e696c97595aa 100644 --- a/src/test/ui/issue-15524.rs +++ b/src/test/ui/issue-15524.rs @@ -13,13 +13,13 @@ const N: isize = 1; enum Foo { A = 1, B = 1, - //~^ ERROR discriminant value `1isize` already exists + //~^ ERROR discriminant value `1` already exists C = 0, D, - //~^ ERROR discriminant value `1isize` already exists + //~^ ERROR discriminant value `1` already exists E = N, - //~^ ERROR discriminant value `1isize` already exists + //~^ ERROR discriminant value `1` already exists } From bdda1196f2ee661657f3a215307e9ed7f0df13f6 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 26 Jan 2018 13:37:46 +0100 Subject: [PATCH 030/110] Use layout::Integer over manual *size resolving --- src/librustc/ty/util.rs | 53 ++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index e65a3b620c388..dd33d68de2e4a 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -22,6 +22,7 @@ use ty::fold::TypeVisitor; use ty::subst::{Subst, UnpackedKind}; use ty::maps::TyCtxtAt; use ty::TypeVariants::*; +use ty::layout::Integer; use util::common::ErrorReported; use middle::lang_items; use mir::interpret::{Value, PrimVal}; @@ -32,7 +33,7 @@ use rustc_data_structures::fx::FxHashMap; use std::{cmp, fmt}; use std::hash::Hash; use std::intrinsics; -use syntax::ast::{self, Name, UintTy, IntTy}; +use syntax::ast::{self, Name}; use syntax::attr::{self, SignedInt, UnsignedInt}; use syntax_pos::{Span, DUMMY_SP}; @@ -58,31 +59,21 @@ impl<'tcx> Discr<'tcx> { self.checked_add(tcx, 1).0 } pub fn checked_add<'a, 'gcx>(self, tcx: TyCtxt<'a, 'gcx, 'tcx>, n: u128) -> (Self, bool) { - let ty = match self.ty.sty { - TyInt(IntTy::Isize) => tcx.mk_mach_int(tcx.sess.target.isize_ty), - TyUint(UintTy::Usize) => tcx.mk_mach_uint(tcx.sess.target.usize_ty), - _ => self.ty, + let (int, signed) = match self.ty.sty { + TyInt(ity) => (Integer::from_attr(tcx, SignedInt(ity)), true), + TyUint(uty) => (Integer::from_attr(tcx, UnsignedInt(uty)), false), + _ => bug!("non integer discriminant"), }; - let (min, max) = match ty.sty { - TyInt(IntTy::I8) => (i8::min_value() as i128 as u128, i8::max_value() as u128), - TyInt(IntTy::I16) => (i16::min_value() as i128 as u128, i16::max_value() as u128), - TyInt(IntTy::I32) => (i32::min_value() as i128 as u128, i32::max_value() as u128), - TyInt(IntTy::I64) => (i64::min_value() as i128 as u128, i64::max_value() as u128), - TyInt(IntTy::I128) => (i128::min_value() as i128 as u128, i128::max_value() as u128), - TyInt(IntTy::Isize) => unreachable!(), - TyUint(UintTy::U8) => (u8::min_value() as u128, u8::max_value() as u128), - TyUint(UintTy::U16) => (u16::min_value() as u128, u16::max_value() as u128), - TyUint(UintTy::U32) => (u32::min_value() as u128, u32::max_value() as u128), - TyUint(UintTy::U64) => (u64::min_value() as u128, u64::max_value() as u128), - TyUint(UintTy::U128) => (u128::min_value() as u128, u128::max_value()), - TyUint(UintTy::Usize) => unreachable!(), - _ => bug!("not a valid discriminant type: {}", ty) - }; - if ty.is_signed() { + if signed { + let (min, max) = match int { + Integer::I8 => (i8::min_value() as i128, i8::max_value() as i128), + Integer::I16 => (i16::min_value() as i128, i16::max_value() as i128), + Integer::I32 => (i32::min_value() as i128, i32::max_value() as i128), + Integer::I64 => (i64::min_value() as i128, i64::max_value() as i128), + Integer::I128 => (i128::min_value(), i128::max_value()), + }; let val = self.val as i128; let n = n as i128; - let max = max as i128; - let min = min as i128; let oflo = val > max - n; let val = if oflo { min + (n - (max - val)) @@ -94,14 +85,22 @@ impl<'tcx> Discr<'tcx> { ty: self.ty, }, oflo) } else { - let oflo = self.val > max - n; + let (min, max) = match int { + Integer::I8 => (u8::min_value() as u128, u8::max_value() as u128), + Integer::I16 => (u16::min_value() as u128, u16::max_value() as u128), + Integer::I32 => (u32::min_value() as u128, u32::max_value() as u128), + Integer::I64 => (u64::min_value() as u128, u64::max_value() as u128), + Integer::I128 => (u128::min_value(), u128::max_value()), + }; + let val = self.val; + let oflo = val > max - n; let val = if oflo { - min + (n - (max - self.val)) + min + (n - (max - val)) } else { - self.val + n + val + n }; (Self { - val, + val: val, ty: self.ty, }, oflo) } From 03ef614f8f9026d15d64249e576e4ea06c12f686 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 26 Jan 2018 13:45:41 +0100 Subject: [PATCH 031/110] Rename simd shuffle function and adjust comment --- src/librustc_trans/mir/block.rs | 2 +- src/librustc_trans/mir/constant.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index cc344fdf063a2..9d70f8669b2c9 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -530,7 +530,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { span_bug!(span, "shuffle indices must be constant"); } mir::Operand::Constant(ref constant) => { - let (llval, ty) = self.remove_me_shuffle_indices( + let (llval, ty) = self.simd_shuffle_indices( &bx, constant, ); diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 954abf4c5d703..1b470665cd9c5 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -184,12 +184,12 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { }.and_then(|c| self.const_to_miri_value(bx, c)) } - // Old version of trans_constant now used just for SIMD shuffle - pub fn remove_me_shuffle_indices(&mut self, - bx: &Builder<'a, 'tcx>, - constant: &mir::Constant<'tcx>) - -> (ValueRef, Ty<'tcx>) - { + /// process constant containing SIMD shuffle indices + pub fn simd_shuffle_indices( + &mut self, + bx: &Builder<'a, 'tcx>, + constant: &mir::Constant<'tcx>, + ) -> (ValueRef, Ty<'tcx>) { let layout = bx.cx.layout_of(constant.ty); self.mir_constant_to_miri_value(bx, constant) .and_then(|c| { From 8c2db0ba8cc178a35ff3bda92c637958f6d143e7 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 26 Jan 2018 13:50:42 +0100 Subject: [PATCH 032/110] Stop emitting `miri failed` in error messages --- src/librustc/middle/const_val.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index 06d4540a455e8..5b18b7d5fd918 100644 --- a/src/librustc/middle/const_val.rs +++ b/src/librustc/middle/const_val.rs @@ -156,7 +156,7 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { TypeckError => simple!("type-checking failed"), CheckMatchError => simple!("match-checking failed"), // FIXME: report a full backtrace - Miri(ref err) => simple!("miri failed: {}", err), + Miri(ref err) => simple!("{}", err), } } From c5d2e178e75517d584808c4fbbac08b8df2287c0 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 26 Jan 2018 13:54:51 +0100 Subject: [PATCH 033/110] Prefer enum instead of magic numbers --- src/librustc/ich/impls_ty.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 74441d4694567..4f691c0d39485 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -368,6 +368,17 @@ impl_stable_hash_for!(struct mir::interpret::MemoryPointer { offset }); +enum AllocDiscriminant { + Static, + Constant, + Function, +} +impl_stable_hash_for!(enum self::AllocDiscriminant { + Static, + Constant, + Function +}); + impl<'a> HashStable> for mir::interpret::AllocId { fn hash_stable( &self, @@ -377,15 +388,15 @@ impl<'a> HashStable> for mir::interpret::AllocId { ty::tls::with_opt(|tcx| { let tcx = tcx.expect("can't hash AllocIds during hir lowering"); if let Some(def_id) = tcx.interpret_interner.get_corresponding_static_def_id(*self) { - 0.hash_stable(hcx, hasher); + AllocDiscriminant::Static.hash_stable(hcx, hasher); // statics are unique via their DefId def_id.hash_stable(hcx, hasher); } else if let Some(alloc) = tcx.interpret_interner.get_alloc(*self) { // not a static, can't be recursive, hash the allocation - 1.hash_stable(hcx, hasher); + AllocDiscriminant::Constant.hash_stable(hcx, hasher); alloc.hash_stable(hcx, hasher); } else if let Some(inst) = tcx.interpret_interner.get_fn(*self) { - 2.hash_stable(hcx, hasher); + AllocDiscriminant::Function.hash_stable(hcx, hasher); inst.hash_stable(hcx, hasher); } else { bug!("no allocation for {}", self); From 8a93972ba9c29bf5c735108a8f7c316aa60fa0da Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 26 Jan 2018 14:28:58 +0100 Subject: [PATCH 034/110] Use Mutability enum instead of bool --- src/librustc/ich/impls_ty.rs | 7 ++++++- src/librustc/mir/interpret/mod.rs | 9 ++++++--- src/librustc_mir/interpret/memory.rs | 4 ++-- src/librustc_trans/mir/constant.rs | 3 ++- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 4f691c0d39485..2dfb2d0bffc6c 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -417,10 +417,15 @@ impl<'a> HashStable> for mir::interpret::Allocation { } self.undef_mask.hash_stable(hcx, hasher); self.align.hash_stable(hcx, hasher); - self.mutable.hash_stable(hcx, hasher); + self.runtime_mutability.hash_stable(hcx, hasher); } } +impl_stable_hash_for!(enum ::syntax::ast::Mutability { + Immutable, + Mutable +}); + impl_stable_hash_for!(struct mir::interpret::Pointer{primval}); impl_stable_hash_for!(enum mir::interpret::PrimVal { diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs index c395be549a397..b621f3f3731a6 100644 --- a/src/librustc/mir/interpret/mod.rs +++ b/src/librustc/mir/interpret/mod.rs @@ -19,6 +19,7 @@ use ty; use ty::layout::{self, Align, HasDataLayout}; use middle::region; use std::iter; +use syntax::ast::Mutability; #[derive(Clone, Debug, PartialEq)] pub enum Lock { @@ -169,8 +170,10 @@ pub struct Allocation { pub undef_mask: UndefMask, /// The alignment of the allocation to detect unaligned reads. pub align: Align, - /// Whether the allocation should be put into mutable memory when translating via llvm - pub mutable: bool, + /// Whether the allocation (of a static) should be put into mutable memory when translating + /// + /// Only happens for `static mut` or `static` with interior mutability + pub runtime_mutability: Mutability, } impl Allocation { @@ -182,7 +185,7 @@ impl Allocation { relocations: BTreeMap::new(), undef_mask, align: Align::from_bytes(1, 1).unwrap(), - mutable: false, + runtime_mutability: Mutability::Immutable, } } } diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index a22158c73345c..e67c36779111b 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -105,7 +105,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { relocations: BTreeMap::new(), undef_mask: UndefMask::new(size), align, - mutable: false, + runtime_mutability: Mutability::Immutable, }; let id = self.tcx.interpret_interner.reserve(); M::add_lock(self, id); @@ -544,7 +544,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { let uninit = self.uninitialized_statics.remove(&alloc_id); if let Some(mut alloc) = alloc.or(uninit) { // ensure llvm knows not to put this into immutable memroy - alloc.mutable = mutability == Mutability::Mutable; + alloc.runtime_mutability = mutability; let alloc = self.tcx.intern_const_alloc(alloc); self.tcx.interpret_interner.intern_at_reserved(alloc_id, alloc); // recurse into inner allocations diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 1b470665cd9c5..c7a0724c1e72b 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -24,6 +24,7 @@ use common::{C_bytes, C_struct, C_uint_big, C_undef, C_usize}; use consts; use type_of::LayoutLlvmExt; use type_::Type; +use syntax::ast::Mutability; use super::super::callee; use super::FunctionCx; @@ -57,7 +58,7 @@ pub fn primval_to_llvm(cx: &CodegenCx, } else if let Some(alloc) = cx.tcx.interpret_interner .get_alloc(ptr.alloc_id) { let init = global_initializer(cx, alloc); - if alloc.mutable { + if alloc.runtime_mutability == Mutability::Mutable { consts::addr_of_mut(cx, init, alloc.align, "byte_str") } else { consts::addr_of(cx, init, alloc.align, "byte_str") From b75a828e2ba5a3fc7b433bc0898cba3596758fe6 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 26 Jan 2018 15:19:01 +0100 Subject: [PATCH 035/110] Reduce noise in error reporting --- src/librustc/mir/interpret/error.rs | 2 +- src/librustc/ty/util.rs | 4 ++-- src/librustc_mir/interpret/const_eval.rs | 2 +- src/test/compile-fail/issue-27592.rs | 1 - src/test/run-pass/ctfe/promotion.rs | 17 +++++++++++++++++ src/test/ui/discrim-overflow.rs | 10 +++++----- src/test/ui/error-codes/E0081.rs | 2 +- 7 files changed, 27 insertions(+), 11 deletions(-) create mode 100644 src/test/run-pass/ctfe/promotion.rs diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs index 702fd85794fff..3e8aeaff57e62 100644 --- a/src/librustc/mir/interpret/error.rs +++ b/src/librustc/mir/interpret/error.rs @@ -306,7 +306,7 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { PathNotFound(ref path) => write!(f, "Cannot find path {:?}", path), MachineError(ref inner) => - write!(f, "machine error: {}", inner), + write!(f, "{}", inner), IncorrectAllocationInformation(size, size2, align, align2) => write!(f, "incorrect alloc info: expected size {} and align {}, got size {} and align {}", size, align, size2, align2), _ => write!(f, "{}", self.description()), diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index dd33d68de2e4a..cbd9a1b8d4f93 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -76,7 +76,7 @@ impl<'tcx> Discr<'tcx> { let n = n as i128; let oflo = val > max - n; let val = if oflo { - min + (n - (max - val)) + min + (n - (max - val) - 1) } else { val + n }; @@ -95,7 +95,7 @@ impl<'tcx> Discr<'tcx> { let val = self.val; let oflo = val > max - n; let val = if oflo { - min + (n - (max - val)) + min + (n - (max - val) - 1) } else { val + n }; diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index 611633163fad5..6c1d762c912cf 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -183,7 +183,7 @@ impl fmt::Display for ConstEvalError { msg ) } - NotConst(ref msg) => write!(f, "Cannot evaluate within constants: \"{}\"", msg), + NotConst(ref msg) => write!(f, "{}", msg), } } } diff --git a/src/test/compile-fail/issue-27592.rs b/src/test/compile-fail/issue-27592.rs index bc29594114746..731d4fb2bf693 100644 --- a/src/test/compile-fail/issue-27592.rs +++ b/src/test/compile-fail/issue-27592.rs @@ -26,5 +26,4 @@ fn main() { write(|| format_args!("{}", String::from("Hello world"))); //~^ ERROR borrowed value does not live long enough //~| ERROR borrowed value does not live long enough - //~| ERROR borrowed value does not live long enough } diff --git a/src/test/run-pass/ctfe/promotion.rs b/src/test/run-pass/ctfe/promotion.rs new file mode 100644 index 0000000000000..2d228408254aa --- /dev/null +++ b/src/test/run-pass/ctfe/promotion.rs @@ -0,0 +1,17 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn foo(_: &'static [&'static str]) {} +fn bar(_: &'static [&'static str; 3]) {} + +fn main() { + foo(&["a", "b", "c"]); + bar(&["d", "e", "f"]); +} diff --git a/src/test/ui/discrim-overflow.rs b/src/test/ui/discrim-overflow.rs index 0b31d9f97f174..16b417c61a517 100644 --- a/src/test/ui/discrim-overflow.rs +++ b/src/test/ui/discrim-overflow.rs @@ -56,7 +56,7 @@ fn f_u16() { Ok = u16::MAX - 1, Ok2, OhNo, //~ ERROR enum discriminant overflowed [E0370] - //~| overflowed on value after 65535u16 + //~| overflowed on value after 65535 } let x = A::Ok; @@ -68,7 +68,7 @@ fn f_i32() { Ok = i32::MAX - 1, Ok2, OhNo, //~ ERROR enum discriminant overflowed [E0370] - //~| overflowed on value after 2147483647i32 + //~| overflowed on value after 2147483647 } let x = A::Ok; @@ -80,7 +80,7 @@ fn f_u32() { Ok = u32::MAX - 1, Ok2, OhNo, //~ ERROR enum discriminant overflowed [E0370] - //~| overflowed on value after 4294967295u32 + //~| overflowed on value after 4294967295 } let x = A::Ok; @@ -92,7 +92,7 @@ fn f_i64() { Ok = i64::MAX - 1, Ok2, OhNo, //~ ERROR enum discriminant overflowed [E0370] - //~| overflowed on value after 9223372036854775807i64 + //~| overflowed on value after 9223372036854775807 } let x = A::Ok; @@ -104,7 +104,7 @@ fn f_u64() { Ok = u64::MAX - 1, Ok2, OhNo, //~ ERROR enum discriminant overflowed [E0370] - //~| overflowed on value after 18446744073709551615u64 + //~| overflowed on value after 18446744073709551615 } let x = A::Ok; diff --git a/src/test/ui/error-codes/E0081.rs b/src/test/ui/error-codes/E0081.rs index 3b571667336ac..c0cdad258687d 100644 --- a/src/test/ui/error-codes/E0081.rs +++ b/src/test/ui/error-codes/E0081.rs @@ -11,7 +11,7 @@ enum Enum { P = 3, X = 3, - //~^ ERROR discriminant value `3isize` already exists + //~^ ERROR discriminant value `3` already exists Y = 5 } From 0e2da01b91b991da398e4a7a4e4906fc2b31a704 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 26 Jan 2018 15:27:38 +0100 Subject: [PATCH 036/110] Move compare_const_vals out of `eval` --- src/librustc_mir/const_eval/_match.rs | 3 +-- src/librustc_mir/const_eval/eval.rs | 31 -------------------------- src/librustc_mir/const_eval/pattern.rs | 31 +++++++++++++++++++++++++- 3 files changed, 31 insertions(+), 34 deletions(-) diff --git a/src/librustc_mir/const_eval/_match.rs b/src/librustc_mir/const_eval/_match.rs index 3b9bbc0ba8b44..57feffbaf0052 100644 --- a/src/librustc_mir/const_eval/_match.rs +++ b/src/librustc_mir/const_eval/_match.rs @@ -13,13 +13,12 @@ use self::Usefulness::*; use self::WitnessPreference::*; use rustc::middle::const_val::ConstVal; -use const_eval::eval::{compare_const_vals}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::indexed_vec::Idx; use const_eval::pattern::{FieldPattern, Pattern, PatternKind}; -use const_eval::pattern::{PatternFoldable, PatternFolder}; +use const_eval::pattern::{PatternFoldable, PatternFolder, compare_const_vals}; use rustc::hir::def_id::DefId; use rustc::hir::RangeEnd; diff --git a/src/librustc_mir/const_eval/eval.rs b/src/librustc_mir/const_eval/eval.rs index 25a9e52236773..32f834daa4c78 100644 --- a/src/librustc_mir/const_eval/eval.rs +++ b/src/librustc_mir/const_eval/eval.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc::middle::const_val::ConstVal::*; use rustc::middle::const_val::ConstVal; use rustc::hir::def_id::DefId; @@ -17,8 +16,6 @@ use rustc::ty::subst::Substs; use syntax::ast; -use std::cmp::Ordering; - use rustc_const_math::*; /// * `DefId` is the id of the constant. @@ -128,31 +125,3 @@ fn parse_float<'tcx>(num: &str, fty: ast::FloatTy) -> Result { ConstFloat::from_str(num, fty).map_err(|_| ()) } - -pub fn compare_const_vals(a: &ConstVal, b: &ConstVal, ty: Ty) -> Option { - trace!("compare_const_vals: {:?}, {:?}", a, b); - use rustc::mir::interpret::{Value, PrimVal}; - match (a, b) { - (&Value(Value::ByVal(PrimVal::Bytes(a))), - &Value(Value::ByVal(PrimVal::Bytes(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, - } -} diff --git a/src/librustc_mir/const_eval/pattern.rs b/src/librustc_mir/const_eval/pattern.rs index 0b8a96332b0f4..27649146eba61 100644 --- a/src/librustc_mir/const_eval/pattern.rs +++ b/src/librustc_mir/const_eval/pattern.rs @@ -18,10 +18,10 @@ use rustc::ty::subst::{Substs, Kind}; use rustc::hir::{self, PatKind, RangeEnd}; use rustc::hir::def::{Def, CtorKind}; use rustc::hir::pat_util::EnumerateAndAdjustIterator; -use const_eval::eval::compare_const_vals; use rustc_data_structures::indexed_vec::Idx; +use std::cmp::Ordering; use std::fmt; use syntax::ast; use syntax::ptr::P; @@ -1060,3 +1060,32 @@ impl<'tcx> PatternFoldable<'tcx> for PatternKind<'tcx> { } } } + +pub fn compare_const_vals(a: &ConstVal, b: &ConstVal, ty: Ty) -> Option { + use rustc_const_math::ConstFloat; + trace!("compare_const_vals: {:?}, {:?}", a, b); + use rustc::mir::interpret::{Value, PrimVal}; + match (a, b) { + (&ConstVal::Value(Value::ByVal(PrimVal::Bytes(a))), + &ConstVal::Value(Value::ByVal(PrimVal::Bytes(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, + } +} From b38e8af5eb038d3afd4aebc0dd0f0b64b646aee0 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 26 Jan 2018 15:28:45 +0100 Subject: [PATCH 037/110] Remove unused function --- src/librustc_mir/const_eval/eval.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/librustc_mir/const_eval/eval.rs b/src/librustc_mir/const_eval/eval.rs index 32f834daa4c78..29c0cd499b3aa 100644 --- a/src/librustc_mir/const_eval/eval.rs +++ b/src/librustc_mir/const_eval/eval.rs @@ -18,19 +18,6 @@ use syntax::ast; use rustc_const_math::*; -/// * `DefId` is the id of the constant. -/// * `Substs` is the monomorphized substitutions for the expression. -pub fn lookup_const_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - key: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>) - -> Option<(DefId, &'tcx Substs<'tcx>)> { - ty::Instance::resolve( - tcx, - key.param_env, - key.value.0, - key.value.1, - ).map(|instance| (instance.def_id(), instance.substs)) -} - pub fn lit_to_const<'a, 'tcx>(lit: &'tcx ast::LitKind, tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>, From 57f9e0e4684224d471a08d17c2db207e43d3c2c8 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 26 Jan 2018 15:31:39 +0100 Subject: [PATCH 038/110] Move lit_to_const to `pattern` and remove the now-empty `eval` module --- src/librustc_mir/const_eval/eval.rs | 114 ------------------------- src/librustc_mir/const_eval/mod.rs | 3 - src/librustc_mir/const_eval/pattern.rs | 100 +++++++++++++++++++++- 3 files changed, 98 insertions(+), 119 deletions(-) delete mode 100644 src/librustc_mir/const_eval/eval.rs diff --git a/src/librustc_mir/const_eval/eval.rs b/src/librustc_mir/const_eval/eval.rs deleted file mode 100644 index 29c0cd499b3aa..0000000000000 --- a/src/librustc_mir/const_eval/eval.rs +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2012-2016 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use rustc::middle::const_val::ConstVal; - -use rustc::hir::def_id::DefId; -use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::subst::Substs; - -use syntax::ast; - -use rustc_const_math::*; - -pub fn lit_to_const<'a, 'tcx>(lit: &'tcx ast::LitKind, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - ty: Ty<'tcx>, - neg: bool) - -> Result, ()> { - use syntax::ast::*; - - 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 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)), - }; - Ok(ConstVal::Value(lit)) -} - -fn parse_float<'tcx>(num: &str, fty: ast::FloatTy) - -> Result { - ConstFloat::from_str(num, fty).map_err(|_| ()) -} diff --git a/src/librustc_mir/const_eval/mod.rs b/src/librustc_mir/const_eval/mod.rs index f47dc61c27c4d..fff1122387f25 100644 --- a/src/librustc_mir/const_eval/mod.rs +++ b/src/librustc_mir/const_eval/mod.rs @@ -10,10 +10,7 @@ //! constant evaluation on the HIR and code to validate patterns/matches -mod eval; mod _match; pub mod check_match; pub mod pattern; pub mod check; - -pub use self::eval::*; diff --git a/src/librustc_mir/const_eval/pattern.rs b/src/librustc_mir/const_eval/pattern.rs index 27649146eba61..2678984092a52 100644 --- a/src/librustc_mir/const_eval/pattern.rs +++ b/src/librustc_mir/const_eval/pattern.rs @@ -20,6 +20,7 @@ use rustc::hir::def::{Def, CtorKind}; use rustc::hir::pat_util::EnumerateAndAdjustIterator; use rustc_data_structures::indexed_vec::Idx; +use rustc_const_math::ConstFloat; use std::cmp::Ordering; use std::fmt; @@ -716,7 +717,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { 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) { + match 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"), @@ -738,7 +739,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { 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) { + match lit_to_const(&lit.node, self.tcx, ty, true) { Ok(val) => { let instance = ty::Instance::new( self.tables.local_id_root.expect("literal outside any scope"), @@ -1089,3 +1090,98 @@ pub fn compare_const_vals(a: &ConstVal, b: &ConstVal, ty: Ty) -> Option None, } } + +fn lit_to_const<'a, 'tcx>(lit: &'tcx ast::LitKind, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + ty: Ty<'tcx>, + neg: bool) + -> Result, ()> { + use syntax::ast::*; + + 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 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)), + }; + Ok(ConstVal::Value(lit)) +} + +fn parse_float<'tcx>(num: &str, fty: ast::FloatTy) + -> Result { + ConstFloat::from_str(num, fty).map_err(|_| ()) +} From 1543367624ac1a42f2dd4ad55fb54edaf4e0127a Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 26 Jan 2018 15:51:33 +0100 Subject: [PATCH 039/110] The `const_eval` module is no more --- src/librustc_driver/driver.rs | 5 ++--- src/librustc_mir/build/mod.rs | 2 +- .../{const_eval/check.rs => check_const_err.rs} | 0 src/librustc_mir/hair/mod.rs | 2 +- src/librustc_mir/lib.rs | 5 +++-- src/librustc_mir/{const_eval => pattern}/_match.rs | 4 ++-- src/librustc_mir/{const_eval => pattern}/check_match.rs | 8 ++++---- src/librustc_mir/{const_eval => pattern}/mod.rs | 7 ++++--- src/librustc_mir/{const_eval => pattern}/pattern.rs | 0 9 files changed, 17 insertions(+), 16 deletions(-) rename src/librustc_mir/{const_eval/check.rs => check_const_err.rs} (100%) rename src/librustc_mir/{const_eval => pattern}/_match.rs (99%) rename src/librustc_mir/{const_eval => pattern}/check_match.rs (99%) rename src/librustc_mir/{const_eval => pattern}/mod.rs (85%) rename src/librustc_mir/{const_eval => pattern}/pattern.rs (100%) diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 943de1211182c..d396e92598a96 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -37,7 +37,6 @@ use rustc_privacy; use rustc_plugin::registry::Registry; use rustc_plugin as plugin; use rustc_passes::{self, ast_validation, loops, consts, hir_stats}; -use rustc_mir::const_eval::check_match; use super::Compilation; use serialize::json; @@ -1049,7 +1048,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(trans: &TransCrate, time(time_passes, "match checking", - || check_match::check_crate(tcx)); + || mir::pattern::check_crate(tcx)); // this must run before MIR dump, because // "not all control paths return a value" is reported here. @@ -1092,7 +1091,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(trans: &TransCrate, time(time_passes, "MIR linting", || for def_id in tcx.body_owners() { - mir::const_eval::check::check(tcx, def_id) + mir::check_const_err::check(tcx, def_id) }); time(time_passes, "lint checking", || lint::check_crate(tcx)); diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 0f4d1e5bb0eca..57508cc43a46f 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -21,7 +21,7 @@ use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::subst::Substs; use rustc::util::nodemap::NodeMap; use rustc_back::PanicStrategy; -use const_eval::pattern::{BindingMode, PatternKind}; +use pattern::pattern::{BindingMode, PatternKind}; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; use shim; use std::mem; diff --git a/src/librustc_mir/const_eval/check.rs b/src/librustc_mir/check_const_err.rs similarity index 100% rename from src/librustc_mir/const_eval/check.rs rename to src/librustc_mir/check_const_err.rs diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs index a7cfa94f6961d..3eeaad8710157 100644 --- a/src/librustc_mir/hair/mod.rs +++ b/src/librustc_mir/hair/mod.rs @@ -26,7 +26,7 @@ use self::cx::Cx; pub mod cx; -pub use const_eval::pattern::{BindingMode, Pattern, PatternKind, FieldPattern}; +pub use pattern::pattern::{BindingMode, Pattern, PatternKind, FieldPattern}; #[derive(Copy, Clone, Debug)] pub enum LintLevel { diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index d25caa588faf1..43ce6fc67df6b 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -71,7 +71,8 @@ pub mod transform; pub mod util; pub mod interpret; pub mod monomorphize; -pub mod const_eval; +pub mod pattern; +pub mod check_const_err; use rustc::ty::maps::Providers; @@ -80,7 +81,7 @@ pub fn provide(providers: &mut Providers) { shim::provide(providers); transform::provide(providers); providers.const_eval = interpret::const_eval_provider; - providers.check_match = const_eval::check_match::check_match; + providers.check_match = pattern::check_match::check_match; } __build_diagnostic_array! { librustc_mir, DIAGNOSTICS } diff --git a/src/librustc_mir/const_eval/_match.rs b/src/librustc_mir/pattern/_match.rs similarity index 99% rename from src/librustc_mir/const_eval/_match.rs rename to src/librustc_mir/pattern/_match.rs index 57feffbaf0052..3b3151926d6cd 100644 --- a/src/librustc_mir/const_eval/_match.rs +++ b/src/librustc_mir/pattern/_match.rs @@ -17,8 +17,8 @@ use rustc::middle::const_val::ConstVal; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::indexed_vec::Idx; -use const_eval::pattern::{FieldPattern, Pattern, PatternKind}; -use const_eval::pattern::{PatternFoldable, PatternFolder, compare_const_vals}; +use super::pattern::{FieldPattern, Pattern, PatternKind}; +use super::pattern::{PatternFoldable, PatternFolder, compare_const_vals}; use rustc::hir::def_id::DefId; use rustc::hir::RangeEnd; diff --git a/src/librustc_mir/const_eval/check_match.rs b/src/librustc_mir/pattern/check_match.rs similarity index 99% rename from src/librustc_mir/const_eval/check_match.rs rename to src/librustc_mir/pattern/check_match.rs index 9fbf55396984a..05f17c19430bb 100644 --- a/src/librustc_mir/const_eval/check_match.rs +++ b/src/librustc_mir/pattern/check_match.rs @@ -8,11 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use const_eval::_match::{MatchCheckCtxt, Matrix, expand_pattern, is_useful}; -use const_eval::_match::Usefulness::*; -use const_eval::_match::WitnessPreference::*; +use super::_match::{MatchCheckCtxt, Matrix, expand_pattern, is_useful}; +use super::_match::Usefulness::*; +use super::_match::WitnessPreference::*; -use const_eval::pattern::{Pattern, PatternContext, PatternError, PatternKind}; +use super::pattern::{Pattern, PatternContext, PatternError, PatternKind}; use rustc::middle::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor}; use rustc::middle::expr_use_visitor::{LoanCause, MutateMode}; diff --git a/src/librustc_mir/const_eval/mod.rs b/src/librustc_mir/pattern/mod.rs similarity index 85% rename from src/librustc_mir/const_eval/mod.rs rename to src/librustc_mir/pattern/mod.rs index fff1122387f25..ff5ddc93a4bae 100644 --- a/src/librustc_mir/const_eval/mod.rs +++ b/src/librustc_mir/pattern/mod.rs @@ -11,6 +11,7 @@ //! constant evaluation on the HIR and code to validate patterns/matches mod _match; -pub mod check_match; -pub mod pattern; -pub mod check; +pub(crate) mod check_match; +pub(crate) mod pattern; + +pub use self::check_match::check_crate; diff --git a/src/librustc_mir/const_eval/pattern.rs b/src/librustc_mir/pattern/pattern.rs similarity index 100% rename from src/librustc_mir/const_eval/pattern.rs rename to src/librustc_mir/pattern/pattern.rs From bb81f9bdec4c4541c68673b3d01771793465e16e Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 26 Jan 2018 15:55:12 +0100 Subject: [PATCH 040/110] only export the two check* functions from check_match --- src/librustc_mir/lib.rs | 2 +- src/librustc_mir/pattern/mod.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 43ce6fc67df6b..f6ceaba29ac68 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -81,7 +81,7 @@ pub fn provide(providers: &mut Providers) { shim::provide(providers); transform::provide(providers); providers.const_eval = interpret::const_eval_provider; - providers.check_match = pattern::check_match::check_match; + providers.check_match = pattern::check_match; } __build_diagnostic_array! { librustc_mir, DIAGNOSTICS } diff --git a/src/librustc_mir/pattern/mod.rs b/src/librustc_mir/pattern/mod.rs index ff5ddc93a4bae..c99dc4a7658d9 100644 --- a/src/librustc_mir/pattern/mod.rs +++ b/src/librustc_mir/pattern/mod.rs @@ -11,7 +11,8 @@ //! constant evaluation on the HIR and code to validate patterns/matches mod _match; -pub(crate) mod check_match; +mod check_match; pub(crate) mod pattern; pub use self::check_match::check_crate; +pub(crate) use self::check_match::check_match; From ef995bde504b388f31dc0a5e1be7a001cbfbe0ed Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 26 Jan 2018 16:12:07 +0100 Subject: [PATCH 041/110] Move the pattern checking code to hair --- src/librustc_driver/driver.rs | 2 +- src/librustc_mir/build/mod.rs | 3 +- src/librustc_mir/hair/mod.rs | 3 +- src/librustc_mir/{ => hair}/pattern/_match.rs | 4 +- .../{ => hair}/pattern/check_match.rs | 2 +- src/librustc_mir/hair/pattern/mod.rs | 1186 +++++++++++++++++ .../{ => hair}/pattern/pattern.rs | 0 src/librustc_mir/lib.rs | 4 +- src/librustc_mir/pattern/mod.rs | 18 - 9 files changed, 1195 insertions(+), 27 deletions(-) rename src/librustc_mir/{ => hair}/pattern/_match.rs (99%) rename src/librustc_mir/{ => hair}/pattern/check_match.rs (99%) create mode 100644 src/librustc_mir/hair/pattern/mod.rs rename src/librustc_mir/{ => hair}/pattern/pattern.rs (100%) delete mode 100644 src/librustc_mir/pattern/mod.rs diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index d396e92598a96..f232d039f6641 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -1048,7 +1048,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(trans: &TransCrate, time(time_passes, "match checking", - || mir::pattern::check_crate(tcx)); + || mir::matchck_crate(tcx)); // this must run before MIR dump, because // "not all control paths return a value" is reported here. diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 57508cc43a46f..23c5499bb6396 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -11,7 +11,7 @@ use build; use hair::cx::Cx; -use hair::LintLevel; +use hair::{LintLevel, BindingMode, PatternKind}; use rustc::hir; use rustc::hir::def_id::{DefId, LocalDefId}; use rustc::middle::region; @@ -21,7 +21,6 @@ use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::subst::Substs; use rustc::util::nodemap::NodeMap; use rustc_back::PanicStrategy; -use pattern::pattern::{BindingMode, PatternKind}; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; use shim; use std::mem; diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs index 3eeaad8710157..5f60a134fb130 100644 --- a/src/librustc_mir/hair/mod.rs +++ b/src/librustc_mir/hair/mod.rs @@ -26,7 +26,8 @@ use self::cx::Cx; pub mod cx; -pub use pattern::pattern::{BindingMode, Pattern, PatternKind, FieldPattern}; +pub mod pattern; +pub use self::pattern::{BindingMode, Pattern, PatternKind, FieldPattern}; #[derive(Copy, Clone, Debug)] pub enum LintLevel { diff --git a/src/librustc_mir/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs similarity index 99% rename from src/librustc_mir/pattern/_match.rs rename to src/librustc_mir/hair/pattern/_match.rs index 3b3151926d6cd..7f443893544f4 100644 --- a/src/librustc_mir/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -17,8 +17,8 @@ use rustc::middle::const_val::ConstVal; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::indexed_vec::Idx; -use super::pattern::{FieldPattern, Pattern, PatternKind}; -use super::pattern::{PatternFoldable, PatternFolder, compare_const_vals}; +use super::{FieldPattern, Pattern, PatternKind}; +use super::{PatternFoldable, PatternFolder, compare_const_vals}; use rustc::hir::def_id::DefId; use rustc::hir::RangeEnd; diff --git a/src/librustc_mir/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs similarity index 99% rename from src/librustc_mir/pattern/check_match.rs rename to src/librustc_mir/hair/pattern/check_match.rs index 05f17c19430bb..69ed4e6064fcf 100644 --- a/src/librustc_mir/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -12,7 +12,7 @@ use super::_match::{MatchCheckCtxt, Matrix, expand_pattern, is_useful}; use super::_match::Usefulness::*; use super::_match::WitnessPreference::*; -use super::pattern::{Pattern, PatternContext, PatternError, PatternKind}; +use super::{Pattern, PatternContext, PatternError, PatternKind}; use rustc::middle::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor}; use rustc::middle::expr_use_visitor::{LoanCause, MutateMode}; diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs new file mode 100644 index 0000000000000..1141425c554a9 --- /dev/null +++ b/src/librustc_mir/hair/pattern/mod.rs @@ -0,0 +1,1186 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Code to validate patterns/matches + +mod _match; +mod check_match; + +pub use self::check_match::check_crate; +pub(crate) use self::check_match::check_match; + +use interpret::{const_val_field, const_discr}; + +use rustc::middle::const_val::ConstVal; +use rustc::mir::{Field, BorrowKind, Mutability}; +use rustc::mir::interpret::{GlobalId, Value, PrimVal}; +use rustc::ty::{self, TyCtxt, AdtDef, Ty, Region}; +use rustc::ty::subst::{Substs, Kind}; +use rustc::hir::{self, PatKind, RangeEnd}; +use rustc::hir::def::{Def, CtorKind}; +use rustc::hir::pat_util::EnumerateAndAdjustIterator; + +use rustc_data_structures::indexed_vec::Idx; +use rustc_const_math::ConstFloat; + +use std::cmp::Ordering; +use std::fmt; +use syntax::ast; +use syntax::ptr::P; +use syntax_pos::Span; + +#[derive(Clone, Debug)] +pub enum PatternError { + StaticInPattern(Span), + FloatBug, + NonConstPath(Span), +} + +#[derive(Copy, Clone, Debug)] +pub enum BindingMode<'tcx> { + ByValue, + ByRef(Region<'tcx>, BorrowKind), +} + +#[derive(Clone, Debug)] +pub struct FieldPattern<'tcx> { + pub field: Field, + pub pattern: Pattern<'tcx>, +} + +#[derive(Clone, Debug)] +pub struct Pattern<'tcx> { + pub ty: Ty<'tcx>, + pub span: Span, + pub kind: Box>, +} + +#[derive(Clone, Debug)] +pub enum PatternKind<'tcx> { + Wild, + + /// x, ref x, x @ P, etc + Binding { + mutability: Mutability, + name: ast::Name, + mode: BindingMode<'tcx>, + var: ast::NodeId, + ty: Ty<'tcx>, + subpattern: Option>, + }, + + /// Foo(...) or Foo{...} or Foo, where `Foo` is a variant name from an adt with >1 variants + Variant { + adt_def: &'tcx AdtDef, + substs: &'tcx Substs<'tcx>, + variant_index: usize, + subpatterns: Vec>, + }, + + /// (...), Foo(...), Foo{...}, or Foo, where `Foo` is a variant name from an adt with 1 variant + Leaf { + subpatterns: Vec>, + }, + + /// box P, &P, &mut P, etc + Deref { + subpattern: Pattern<'tcx>, + }, + + Constant { + value: &'tcx ty::Const<'tcx>, + }, + + Range { + lo: &'tcx ty::Const<'tcx>, + hi: &'tcx ty::Const<'tcx>, + end: RangeEnd, + }, + + /// matches against a slice, checking the length and extracting elements. + /// irrefutable when there is a slice pattern and both `prefix` and `suffix` are empty. + /// e.g. `&[ref xs..]`. + Slice { + prefix: Vec>, + slice: Option>, + suffix: Vec>, + }, + + /// fixed match against an array, irrefutable + Array { + prefix: Vec>, + slice: Option>, + suffix: Vec>, + }, +} + +fn print_const_val(value: &ty::Const, f: &mut fmt::Formatter) -> fmt::Result { + match value.val { + ConstVal::Value(v) => print_miri_value(v, value.ty, f), + ConstVal::Unevaluated(..) => bug!("{:?} not printable in a pattern", value) + } +} + +fn print_miri_value(value: Value, ty: Ty, f: &mut fmt::Formatter) -> fmt::Result { + use rustc::ty::TypeVariants::*; + match (value, &ty.sty) { + (Value::ByVal(PrimVal::Bytes(0)), &TyBool) => write!(f, "false"), + (Value::ByVal(PrimVal::Bytes(1)), &TyBool) => write!(f, "true"), + (Value::ByVal(PrimVal::Bytes(n)), &TyUint(..)) => write!(f, "{:?}", n), + (Value::ByVal(PrimVal::Bytes(n)), &TyInt(..)) => write!(f, "{:?}", n as i128), + (Value::ByVal(PrimVal::Bytes(n)), &TyChar) => + write!(f, "{:?}", ::std::char::from_u32(n as u32).unwrap()), + _ => bug!("{:?}: {} not printable in a pattern", value, ty), + } +} + +impl<'tcx> fmt::Display for Pattern<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self.kind { + PatternKind::Wild => write!(f, "_"), + PatternKind::Binding { mutability, name, mode, ref subpattern, .. } => { + let is_mut = match mode { + BindingMode::ByValue => mutability == Mutability::Mut, + BindingMode::ByRef(_, bk) => { + write!(f, "ref ")?; + bk == BorrowKind::Mut + } + }; + if is_mut { + write!(f, "mut ")?; + } + write!(f, "{}", name)?; + if let Some(ref subpattern) = *subpattern { + write!(f, " @ {}", subpattern)?; + } + Ok(()) + } + PatternKind::Variant { ref subpatterns, .. } | + PatternKind::Leaf { ref subpatterns } => { + let variant = match *self.kind { + PatternKind::Variant { adt_def, variant_index, .. } => { + Some(&adt_def.variants[variant_index]) + } + _ => if let ty::TyAdt(adt, _) = self.ty.sty { + if !adt.is_enum() { + Some(&adt.variants[0]) + } else { + None + } + } else { + None + } + }; + + let mut first = true; + let mut start_or_continue = || if first { first = false; "" } else { ", " }; + + if let Some(variant) = variant { + write!(f, "{}", variant.name)?; + + // Only for TyAdt we can have `S {...}`, + // which we handle separately here. + if variant.ctor_kind == CtorKind::Fictive { + write!(f, " {{ ")?; + + let mut printed = 0; + for p in subpatterns { + if let PatternKind::Wild = *p.pattern.kind { + continue; + } + let name = variant.fields[p.field.index()].name; + write!(f, "{}{}: {}", start_or_continue(), name, p.pattern)?; + printed += 1; + } + + if printed < variant.fields.len() { + write!(f, "{}..", start_or_continue())?; + } + + return write!(f, " }}"); + } + } + + let num_fields = variant.map_or(subpatterns.len(), |v| v.fields.len()); + if num_fields != 0 || variant.is_none() { + write!(f, "(")?; + for i in 0..num_fields { + write!(f, "{}", start_or_continue())?; + + // Common case: the field is where we expect it. + if let Some(p) = subpatterns.get(i) { + if p.field.index() == i { + write!(f, "{}", p.pattern)?; + continue; + } + } + + // Otherwise, we have to go looking for it. + if let Some(p) = subpatterns.iter().find(|p| p.field.index() == i) { + write!(f, "{}", p.pattern)?; + } else { + write!(f, "_")?; + } + } + write!(f, ")")?; + } + + Ok(()) + } + PatternKind::Deref { ref subpattern } => { + match self.ty.sty { + ty::TyAdt(def, _) if def.is_box() => write!(f, "box ")?, + ty::TyRef(_, mt) => { + write!(f, "&")?; + if mt.mutbl == hir::MutMutable { + write!(f, "mut ")?; + } + } + _ => bug!("{} is a bad Deref pattern type", self.ty) + } + write!(f, "{}", subpattern) + } + PatternKind::Constant { value } => { + print_const_val(value, f) + } + PatternKind::Range { lo, hi, end } => { + print_const_val(lo, f)?; + match end { + RangeEnd::Included => write!(f, "...")?, + RangeEnd::Excluded => write!(f, "..")?, + } + print_const_val(hi, f) + } + PatternKind::Slice { ref prefix, ref slice, ref suffix } | + PatternKind::Array { ref prefix, ref slice, ref suffix } => { + let mut first = true; + let mut start_or_continue = || if first { first = false; "" } else { ", " }; + write!(f, "[")?; + for p in prefix { + write!(f, "{}{}", start_or_continue(), p)?; + } + if let Some(ref slice) = *slice { + write!(f, "{}", start_or_continue())?; + match *slice.kind { + PatternKind::Wild => {} + _ => write!(f, "{}", slice)? + } + write!(f, "..")?; + } + for p in suffix { + write!(f, "{}{}", start_or_continue(), p)?; + } + write!(f, "]") + } + } + } +} + +pub struct PatternContext<'a, 'tcx: 'a> { + pub tcx: TyCtxt<'a, 'tcx, 'tcx>, + pub param_env: ty::ParamEnv<'tcx>, + pub tables: &'a ty::TypeckTables<'tcx>, + pub substs: &'tcx Substs<'tcx>, + pub errors: Vec, +} + +impl<'a, 'tcx> Pattern<'tcx> { + pub fn from_hir(tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env_and_substs: ty::ParamEnvAnd<'tcx, &'tcx Substs<'tcx>>, + tables: &'a ty::TypeckTables<'tcx>, + pat: &'tcx hir::Pat) -> Self { + let mut pcx = PatternContext::new(tcx, param_env_and_substs, tables); + let result = pcx.lower_pattern(pat); + if !pcx.errors.is_empty() { + let msg = format!("encountered errors lowering pattern: {:?}", pcx.errors); + tcx.sess.delay_span_bug(pat.span, &msg); + } + debug!("Pattern::from_hir({:?}) = {:?}", pat, result); + result + } +} + +impl<'a, 'tcx> PatternContext<'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 { + PatternContext { + tcx, + param_env: param_env_and_substs.param_env, + tables, + substs: param_env_and_substs.value, + errors: vec![] + } + } + + pub fn lower_pattern(&mut self, pat: &'tcx hir::Pat) -> Pattern<'tcx> { + // When implicit dereferences have been inserted in this pattern, the unadjusted lowered + // pattern has the type that results *after* dereferencing. For example, in this code: + // + // ``` + // match &&Some(0i32) { + // Some(n) => { ... }, + // _ => { ... }, + // } + // ``` + // + // the type assigned to `Some(n)` in `unadjusted_pat` would be `Option` (this is + // determined in rustc_typeck::check::match). The adjustments would be + // + // `vec![&&Option, &Option]`. + // + // Applying the adjustments, we want to instead output `&&Some(n)` (as a HAIR pattern). So + // we wrap the unadjusted pattern in `PatternKind::Deref` repeatedly, consuming the + // adjustments in *reverse order* (last-in-first-out, so that the last `Deref` inserted + // gets the least-dereferenced type). + let unadjusted_pat = self.lower_pattern_unadjusted(pat); + self.tables + .pat_adjustments() + .get(pat.hir_id) + .unwrap_or(&vec![]) + .iter() + .rev() + .fold(unadjusted_pat, |pat, ref_ty| { + debug!("{:?}: wrapping pattern with type {:?}", pat, ref_ty); + Pattern { + span: pat.span, + ty: ref_ty, + kind: Box::new(PatternKind::Deref { subpattern: pat }), + } + }, + ) + } + + fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat) -> Pattern<'tcx> { + let mut ty = self.tables.node_id_to_type(pat.hir_id); + + let kind = match pat.node { + PatKind::Wild => PatternKind::Wild, + + PatKind::Lit(ref value) => self.lower_lit(value), + + 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 + } + } + + PatKind::Path(ref qpath) => { + return self.lower_path(qpath, pat.hir_id, pat.span); + } + + PatKind::Ref(ref subpattern, _) | + PatKind::Box(ref subpattern) => { + PatternKind::Deref { subpattern: self.lower_pattern(subpattern) } + } + + PatKind::Slice(ref prefix, ref slice, ref suffix) => { + let ty = self.tables.node_id_to_type(pat.hir_id); + match ty.sty { + ty::TyRef(_, mt) => + PatternKind::Deref { + subpattern: Pattern { + ty: mt.ty, + span: pat.span, + kind: Box::new(self.slice_or_array_pattern( + pat.span, mt.ty, prefix, slice, suffix)) + }, + }, + + ty::TySlice(..) | + ty::TyArray(..) => + self.slice_or_array_pattern(pat.span, ty, prefix, slice, suffix), + + ref sty => + span_bug!( + pat.span, + "unexpanded type for vector pattern: {:?}", + sty), + } + } + + PatKind::Tuple(ref subpatterns, ddpos) => { + let ty = self.tables.node_id_to_type(pat.hir_id); + match ty.sty { + ty::TyTuple(ref tys, _) => { + let subpatterns = + subpatterns.iter() + .enumerate_and_adjust(tys.len(), ddpos) + .map(|(i, subpattern)| FieldPattern { + field: Field::new(i), + pattern: self.lower_pattern(subpattern) + }) + .collect(); + + PatternKind::Leaf { subpatterns: subpatterns } + } + + ref sty => span_bug!(pat.span, "unexpected type for tuple pattern: {:?}", sty), + } + } + + PatKind::Binding(_, id, ref ident, ref sub) => { + let var_ty = self.tables.node_id_to_type(pat.hir_id); + let region = match var_ty.sty { + ty::TyRef(r, _) => Some(r), + _ => None, + }; + let bm = *self.tables.pat_binding_modes().get(pat.hir_id) + .expect("missing binding mode"); + let (mutability, mode) = match bm { + ty::BindByValue(hir::MutMutable) => + (Mutability::Mut, BindingMode::ByValue), + ty::BindByValue(hir::MutImmutable) => + (Mutability::Not, BindingMode::ByValue), + ty::BindByReference(hir::MutMutable) => + (Mutability::Not, BindingMode::ByRef( + region.unwrap(), BorrowKind::Mut)), + ty::BindByReference(hir::MutImmutable) => + (Mutability::Not, BindingMode::ByRef( + region.unwrap(), BorrowKind::Shared)), + }; + + // A ref x pattern is the same node used for x, and as such it has + // x's type, which is &T, where we want T (the type being matched). + if let ty::BindByReference(_) = bm { + if let ty::TyRef(_, mt) = ty.sty { + ty = mt.ty; + } else { + bug!("`ref {}` has wrong type {}", ident.node, ty); + } + } + + PatternKind::Binding { + mutability, + mode, + name: ident.node, + var: id, + ty: var_ty, + subpattern: self.lower_opt_pattern(sub), + } + } + + PatKind::TupleStruct(ref qpath, ref subpatterns, ddpos) => { + let def = self.tables.qpath_def(qpath, pat.hir_id); + let adt_def = match ty.sty { + ty::TyAdt(adt_def, _) => adt_def, + _ => span_bug!(pat.span, "tuple struct pattern not applied to an ADT"), + }; + let variant_def = adt_def.variant_of_def(def); + + let subpatterns = + subpatterns.iter() + .enumerate_and_adjust(variant_def.fields.len(), ddpos) + .map(|(i, field)| FieldPattern { + field: Field::new(i), + pattern: self.lower_pattern(field), + }) + .collect(); + self.lower_variant_or_leaf(def, pat.span, ty, subpatterns) + } + + PatKind::Struct(ref qpath, ref fields, _) => { + let def = self.tables.qpath_def(qpath, pat.hir_id); + let adt_def = match ty.sty { + ty::TyAdt(adt_def, _) => adt_def, + _ => { + span_bug!( + pat.span, + "struct pattern not applied to an ADT"); + } + }; + let variant_def = adt_def.variant_of_def(def); + + let subpatterns = + fields.iter() + .map(|field| { + let index = variant_def.index_of_field_named(field.node.name); + let index = index.unwrap_or_else(|| { + span_bug!( + pat.span, + "no field with name {:?}", + field.node.name); + }); + FieldPattern { + field: Field::new(index), + pattern: self.lower_pattern(&field.node.pat), + } + }) + .collect(); + + self.lower_variant_or_leaf(def, pat.span, ty, subpatterns) + } + }; + + Pattern { + span: pat.span, + ty, + kind: Box::new(kind), + } + } + + fn lower_patterns(&mut self, pats: &'tcx [P]) -> Vec> { + pats.iter().map(|p| self.lower_pattern(p)).collect() + } + + fn lower_opt_pattern(&mut self, pat: &'tcx Option>) -> Option> + { + pat.as_ref().map(|p| self.lower_pattern(p)) + } + + fn flatten_nested_slice_patterns( + &mut self, + prefix: Vec>, + slice: Option>, + suffix: Vec>) + -> (Vec>, Option>, Vec>) + { + let orig_slice = match slice { + Some(orig_slice) => orig_slice, + None => return (prefix, slice, suffix) + }; + let orig_prefix = prefix; + let orig_suffix = suffix; + + // dance because of intentional borrow-checker stupidity. + let kind = *orig_slice.kind; + match kind { + PatternKind::Slice { prefix, slice, mut suffix } | + PatternKind::Array { prefix, slice, mut suffix } => { + let mut orig_prefix = orig_prefix; + + orig_prefix.extend(prefix); + suffix.extend(orig_suffix); + + (orig_prefix, slice, suffix) + } + _ => { + (orig_prefix, Some(Pattern { + kind: box kind, ..orig_slice + }), orig_suffix) + } + } + } + + fn slice_or_array_pattern( + &mut self, + span: Span, + ty: Ty<'tcx>, + prefix: &'tcx [P], + slice: &'tcx Option>, + suffix: &'tcx [P]) + -> PatternKind<'tcx> + { + let prefix = self.lower_patterns(prefix); + let slice = self.lower_opt_pattern(slice); + let suffix = self.lower_patterns(suffix); + let (prefix, slice, suffix) = + self.flatten_nested_slice_patterns(prefix, slice, suffix); + + match ty.sty { + ty::TySlice(..) => { + // matching a slice or fixed-length array + PatternKind::Slice { prefix: prefix, slice: slice, suffix: suffix } + } + + ty::TyArray(_, len) => { + // fixed-length array + let len = len.val.unwrap_u64(); + assert!(len >= prefix.len() as u64 + suffix.len() as u64); + PatternKind::Array { prefix: prefix, slice: slice, suffix: suffix } + } + + _ => { + span_bug!(span, "bad slice pattern type {:?}", ty); + } + } + } + + fn lower_variant_or_leaf( + &mut self, + def: Def, + span: Span, + ty: Ty<'tcx>, + subpatterns: Vec>) + -> PatternKind<'tcx> + { + match def { + Def::Variant(variant_id) | Def::VariantCtor(variant_id, ..) => { + let enum_id = self.tcx.parent_def_id(variant_id).unwrap(); + let adt_def = self.tcx.adt_def(enum_id); + if adt_def.is_enum() { + let substs = match ty.sty { + ty::TyAdt(_, substs) | + ty::TyFnDef(_, substs) => substs, + _ => bug!("inappropriate type for def: {:?}", ty.sty), + }; + PatternKind::Variant { + adt_def, + substs, + variant_index: adt_def.variant_index_with_id(variant_id), + subpatterns, + } + } else { + PatternKind::Leaf { subpatterns: subpatterns } + } + } + + Def::Struct(..) | Def::StructCtor(..) | Def::Union(..) | + Def::TyAlias(..) | Def::AssociatedTy(..) | Def::SelfTy(..) => { + PatternKind::Leaf { subpatterns: subpatterns } + } + + _ => { + self.errors.push(PatternError::NonConstPath(span)); + PatternKind::Wild + } + } + } + + fn lower_path(&mut self, + qpath: &hir::QPath, + id: hir::HirId, + span: Span) + -> Pattern<'tcx> { + let ty = self.tables.node_id_to_type(id); + let def = self.tables.qpath_def(qpath, id); + let kind = match def { + Def::Const(def_id) | Def::AssociatedConst(def_id) => { + let substs = self.tables.node_substs(id); + match ty::Instance::resolve( + self.tcx, + self.param_env, + def_id, + substs, + ) { + Some(instance) => { + let cid = GlobalId { + instance, + promoted: None, + }; + match self.tcx.at(span).const_eval(self.param_env.and(cid)) { + Ok(value) => { + return self.const_to_pat(instance, value, id, span) + }, + Err(err) => { + err.report(self.tcx, span, "pattern"); + PatternKind::Wild + }, + } + }, + None => { + self.errors.push(PatternError::StaticInPattern(span)); + PatternKind::Wild + }, + } + } + _ => self.lower_variant_or_leaf(def, span, ty, vec![]), + }; + + Pattern { + span, + ty, + kind: Box::new(kind), + } + } + + fn lower_lit(&mut self, expr: &'tcx hir::Expr) -> PatternKind<'tcx> { + match expr.node { + hir::ExprLit(ref lit) => { + let ty = self.tables.expr_ty(expr); + match 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(()) => { + self.errors.push(PatternError::FloatBug); + 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 lit_to_const(&lit.node, self.tcx, ty, true) { + 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(()) => { + self.errors.push(PatternError::FloatBug); + PatternKind::Wild + }, + } + } + _ => span_bug!(expr.span, "not a literal: {:?}", expr), + } + } + + fn const_to_pat( + &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(_) => { + 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"); + PatternKind::Wild + } + ty::TyAdt(adt_def, _) if !self.tcx.has_attr(adt_def.did, "structural_match") => { + let msg = format!("to use a constant of type `{}` in a pattern, \ + `{}` must be annotated with `#[derive(PartialEq, Eq)]`", + self.tcx.item_path_str(adt_def.did), + self.tcx.item_path_str(adt_def.did)); + self.tcx.sess.span_err(span, &msg); + PatternKind::Wild + }, + ty::TyAdt(adt_def, substs) if adt_def.is_enum() => { + match cv.val { + ConstVal::Value(val) => { + let discr = const_discr( + self.tcx, self.param_env, instance, val, cv.ty + ).unwrap(); + let variant_index = adt_def + .discriminants(self.tcx) + .position(|var| var.val == discr) + .unwrap(); + PatternKind::Variant { + adt_def, + substs, + variant_index, + subpatterns: adt_def + .variants[variant_index] + .fields + .iter() + .enumerate() + .map(|(i, _)| { + 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, + ).unwrap(), + _ => bug!("{:#?} is not a valid tuple", cv), + }; + FieldPattern { + field, + pattern: self.const_to_pat(instance, val, id, span), + } + }).collect(), + } + }, + _ => return Pattern { + span, + ty: cv.ty, + kind: Box::new(PatternKind::Constant { + value: cv, + }), + } + } + }, + ty::TyAdt(adt_def, _) => { + let struct_var = adt_def.non_enum_variant(); + PatternKind::Leaf { + subpatterns: struct_var.fields.iter().enumerate().map(|(i, _)| { + let field = Field::new(i); + let val = match cv.val { + 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, id, span), + } + }).collect() + } + } + ty::TyTuple(fields, _) => { + PatternKind::Leaf { + subpatterns: (0..fields.len()).map(|i| { + let field = Field::new(i); + let val = match cv.val { + 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, id, span), + } + }).collect() + } + } + ty::TyArray(_, n) => { + 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::Value(miri) => const_val_field( + self.tcx, self.param_env, instance, None, field, miri, cv.ty, + ).unwrap(), + _ => bug!("{:#?} is not a valid tuple", cv), + }; + self.const_to_pat(instance, val, id, span) + }).collect(), + slice: None, + suffix: Vec::new(), + } + } + _ => { + PatternKind::Constant { + value: cv, + } + }, + }; + + Pattern { + span, + ty: cv.ty, + kind: Box::new(kind), + } + } +} + +pub trait PatternFoldable<'tcx> : Sized { + fn fold_with>(&self, folder: &mut F) -> Self { + self.super_fold_with(folder) + } + + fn super_fold_with>(&self, folder: &mut F) -> Self; +} + +pub trait PatternFolder<'tcx> : Sized { + fn fold_pattern(&mut self, pattern: &Pattern<'tcx>) -> Pattern<'tcx> { + pattern.super_fold_with(self) + } + + fn fold_pattern_kind(&mut self, kind: &PatternKind<'tcx>) -> PatternKind<'tcx> { + kind.super_fold_with(self) + } +} + + +impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Box { + fn super_fold_with>(&self, folder: &mut F) -> Self { + let content: T = (**self).fold_with(folder); + box content + } +} + +impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Vec { + fn super_fold_with>(&self, folder: &mut F) -> Self { + self.iter().map(|t| t.fold_with(folder)).collect() + } +} + +impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Option { + fn super_fold_with>(&self, folder: &mut F) -> Self{ + self.as_ref().map(|t| t.fold_with(folder)) + } +} + +macro_rules! CloneImpls { + (<$lt_tcx:tt> $($ty:ty),+) => { + $( + impl<$lt_tcx> PatternFoldable<$lt_tcx> for $ty { + fn super_fold_with>(&self, _: &mut F) -> Self { + Clone::clone(self) + } + } + )+ + } +} + +CloneImpls!{ <'tcx> + Span, Field, Mutability, ast::Name, ast::NodeId, usize, &'tcx ty::Const<'tcx>, + Region<'tcx>, Ty<'tcx>, BindingMode<'tcx>, &'tcx AdtDef, + &'tcx Substs<'tcx>, &'tcx Kind<'tcx> +} + +impl<'tcx> PatternFoldable<'tcx> for FieldPattern<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + FieldPattern { + field: self.field.fold_with(folder), + pattern: self.pattern.fold_with(folder) + } + } +} + +impl<'tcx> PatternFoldable<'tcx> for Pattern<'tcx> { + fn fold_with>(&self, folder: &mut F) -> Self { + folder.fold_pattern(self) + } + + fn super_fold_with>(&self, folder: &mut F) -> Self { + Pattern { + ty: self.ty.fold_with(folder), + span: self.span.fold_with(folder), + kind: self.kind.fold_with(folder) + } + } +} + +impl<'tcx> PatternFoldable<'tcx> for PatternKind<'tcx> { + fn fold_with>(&self, folder: &mut F) -> Self { + folder.fold_pattern_kind(self) + } + + fn super_fold_with>(&self, folder: &mut F) -> Self { + match *self { + PatternKind::Wild => PatternKind::Wild, + PatternKind::Binding { + mutability, + name, + mode, + var, + ty, + ref subpattern, + } => PatternKind::Binding { + mutability: mutability.fold_with(folder), + name: name.fold_with(folder), + mode: mode.fold_with(folder), + var: var.fold_with(folder), + ty: ty.fold_with(folder), + subpattern: subpattern.fold_with(folder), + }, + PatternKind::Variant { + adt_def, + substs, + variant_index, + ref subpatterns, + } => PatternKind::Variant { + adt_def: adt_def.fold_with(folder), + substs: substs.fold_with(folder), + variant_index: variant_index.fold_with(folder), + subpatterns: subpatterns.fold_with(folder) + }, + PatternKind::Leaf { + ref subpatterns, + } => PatternKind::Leaf { + subpatterns: subpatterns.fold_with(folder), + }, + PatternKind::Deref { + ref subpattern, + } => PatternKind::Deref { + subpattern: subpattern.fold_with(folder), + }, + PatternKind::Constant { + value + } => PatternKind::Constant { + value: value.fold_with(folder) + }, + PatternKind::Range { + lo, + hi, + end, + } => PatternKind::Range { + lo: lo.fold_with(folder), + hi: hi.fold_with(folder), + end, + }, + PatternKind::Slice { + ref prefix, + ref slice, + ref suffix, + } => PatternKind::Slice { + prefix: prefix.fold_with(folder), + slice: slice.fold_with(folder), + suffix: suffix.fold_with(folder) + }, + PatternKind::Array { + ref prefix, + ref slice, + ref suffix + } => PatternKind::Array { + prefix: prefix.fold_with(folder), + slice: slice.fold_with(folder), + suffix: suffix.fold_with(folder) + }, + } + } +} + +pub fn compare_const_vals(a: &ConstVal, b: &ConstVal, ty: Ty) -> Option { + use rustc_const_math::ConstFloat; + trace!("compare_const_vals: {:?}, {:?}", a, b); + use rustc::mir::interpret::{Value, PrimVal}; + match (a, b) { + (&ConstVal::Value(Value::ByVal(PrimVal::Bytes(a))), + &ConstVal::Value(Value::ByVal(PrimVal::Bytes(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, + } +} + +fn lit_to_const<'a, 'tcx>(lit: &'tcx ast::LitKind, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + ty: Ty<'tcx>, + neg: bool) + -> Result, ()> { + use syntax::ast::*; + + 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 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)), + }; + Ok(ConstVal::Value(lit)) +} + +fn parse_float<'tcx>(num: &str, fty: ast::FloatTy) + -> Result { + ConstFloat::from_str(num, fty).map_err(|_| ()) +} diff --git a/src/librustc_mir/pattern/pattern.rs b/src/librustc_mir/hair/pattern/pattern.rs similarity index 100% rename from src/librustc_mir/pattern/pattern.rs rename to src/librustc_mir/hair/pattern/pattern.rs diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index f6ceaba29ac68..f6b38dcc00150 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -71,9 +71,9 @@ pub mod transform; pub mod util; pub mod interpret; pub mod monomorphize; -pub mod pattern; pub mod check_const_err; +pub use hair::pattern::check_crate as matchck_crate; use rustc::ty::maps::Providers; pub fn provide(providers: &mut Providers) { @@ -81,7 +81,7 @@ pub fn provide(providers: &mut Providers) { shim::provide(providers); transform::provide(providers); providers.const_eval = interpret::const_eval_provider; - providers.check_match = pattern::check_match; + providers.check_match = hair::pattern::check_match; } __build_diagnostic_array! { librustc_mir, DIAGNOSTICS } diff --git a/src/librustc_mir/pattern/mod.rs b/src/librustc_mir/pattern/mod.rs deleted file mode 100644 index c99dc4a7658d9..0000000000000 --- a/src/librustc_mir/pattern/mod.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2016 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! constant evaluation on the HIR and code to validate patterns/matches - -mod _match; -mod check_match; -pub(crate) mod pattern; - -pub use self::check_match::check_crate; -pub(crate) use self::check_match::check_match; From 4f65261c4396ed3d6611ff990c71e8b9e4cf4f11 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 26 Jan 2018 16:22:26 +0100 Subject: [PATCH 042/110] Remove unused error variants and const eval types --- src/librustc/ich/impls_ty.rs | 16 ----------- src/librustc/middle/const_val.rs | 42 ++--------------------------- src/librustc/ty/codec.rs | 20 -------------- src/librustc/ty/structural_impls.rs | 12 --------- 4 files changed, 2 insertions(+), 88 deletions(-) diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 2dfb2d0bffc6c..72bc5af3b7e2d 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -353,10 +353,6 @@ for ::middle::const_val::ConstVal<'gcx> { } } -impl_stable_hash_for!(struct ::middle::const_val::ByteArray<'tcx> { - data -}); - impl_stable_hash_for!(enum mir::interpret::Value { ByVal(v), ByValPair(a, b), @@ -454,16 +450,7 @@ for ::middle::const_val::ErrKind<'gcx> { mem::discriminant(self).hash_stable(hcx, hasher); match *self { - CannotCast | - MissingStructField | NonConstPath | - ExpectedConstTuple | - ExpectedConstStruct | - IndexedNonVec | - IndexNotUsize | - MiscBinaryOp | - MiscCatchAll | - IndexOpFeatureGated | TypeckError | CheckMatchError => { // nothing to do @@ -481,9 +468,6 @@ for ::middle::const_val::ErrKind<'gcx> { LayoutError(ref layout_error) => { layout_error.hash_stable(hcx, hasher); } - ErroneousReferencedConstant(ref const_val) => { - const_val.hash_stable(hcx, hasher); - } Miri(ref err) => err.hash_stable(hcx, hasher), } } diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index 5b18b7d5fd918..c987ed0ea5d3f 100644 --- a/src/librustc/middle/const_val.rs +++ b/src/librustc/middle/const_val.rs @@ -16,7 +16,6 @@ use mir::interpret::{Value, PrimVal}; use errors::DiagnosticBuilder; use graphviz::IntoCow; -use serialize; use syntax_pos::Span; use std::borrow::Cow; @@ -29,13 +28,6 @@ pub enum ConstVal<'tcx> { Value(Value), } -#[derive(Copy, Clone, Debug, Hash, RustcEncodable, Eq, PartialEq)] -pub struct ByteArray<'tcx> { - pub data: &'tcx [u8], -} - -impl<'tcx> serialize::UseSpecializedDecodable for ByteArray<'tcx> {} - impl<'tcx> ConstVal<'tcx> { pub fn to_raw_bits(&self) -> Option { match *self { @@ -64,26 +56,14 @@ pub struct ConstEvalErr<'tcx> { #[derive(Clone, Debug)] pub enum ErrKind<'tcx> { - CannotCast, - MissingStructField, NonConstPath, UnimplementedConstVal(&'static str), - ExpectedConstTuple, - ExpectedConstStruct, - IndexedNonVec, - IndexNotUsize, IndexOutOfBounds { len: u64, index: u64 }, - MiscBinaryOp, - MiscCatchAll, - - IndexOpFeatureGated, Math(ConstMathErr), LayoutError(layout::LayoutError<'tcx>), - ErroneousReferencedConstant(Box>), - TypeckError, CheckMatchError, Miri(::mir::interpret::EvalError<'tcx>), @@ -131,28 +111,17 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { } match self.kind { - CannotCast => simple!("can't cast this type"), - MissingStructField => simple!("nonexistent struct field"), NonConstPath => simple!("non-constant path in constant expression"), UnimplementedConstVal(what) => simple!("unimplemented constant expression: {}", what), - ExpectedConstTuple => simple!("expected constant tuple"), - ExpectedConstStruct => simple!("expected constant struct"), - IndexedNonVec => simple!("indexing is only supported for arrays"), - IndexNotUsize => simple!("indices must be of type `usize`"), IndexOutOfBounds { len, index } => { simple!("index out of bounds: the len is {} but the index is {}", len, index) } - MiscBinaryOp => simple!("bad operands for binary"), - MiscCatchAll => simple!("unsupported constant expr"), - IndexOpFeatureGated => simple!("the index operation on const values is unstable"), Math(ref err) => Simple(err.description().into_cow()), LayoutError(ref err) => Simple(err.to_string().into_cow()), - ErroneousReferencedConstant(_) => simple!("could not evaluate referenced constant"), - TypeckError => simple!("type-checking failed"), CheckMatchError => simple!("match-checking failed"), // FIXME: report a full backtrace @@ -166,15 +135,8 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { primary_kind: &str) -> DiagnosticBuilder<'gcx> { - let mut err = self; - while let &ConstEvalErr { - kind: ErrKind::ErroneousReferencedConstant(box ref i_err), .. - } = err { - err = i_err; - } - - let mut diag = struct_error(tcx, err.span, "constant evaluation error"); - err.note(tcx, primary_span, primary_kind, &mut diag); + let mut diag = struct_error(tcx, self.span, "constant evaluation error"); + self.note(tcx, primary_span, primary_kind, &mut diag); diag } diff --git a/src/librustc/ty/codec.rs b/src/librustc/ty/codec.rs index fbb14f39ade34..f98bc95356098 100644 --- a/src/librustc/ty/codec.rs +++ b/src/librustc/ty/codec.rs @@ -17,7 +17,6 @@ // persisting to incr. comp. caches. use hir::def_id::{DefId, CrateNum}; -use middle::const_val::ByteArray; use rustc_data_structures::fx::FxHashMap; use rustc_serialize::{Decodable, Decoder, Encoder, Encodable, opaque}; use std::hash::Hash; @@ -240,17 +239,6 @@ pub fn decode_existential_predicate_slice<'a, 'tcx, D>(decoder: &mut D) .mk_existential_predicates((0..len).map(|_| Decodable::decode(decoder)))?) } -#[inline] -pub fn decode_byte_array<'a, 'tcx, D>(decoder: &mut D) - -> Result, D::Error> - where D: TyDecoder<'a, 'tcx>, - 'tcx: 'a, -{ - Ok(ByteArray { - data: decoder.tcx().alloc_byte_array(&Vec::decode(decoder)?) - }) -} - #[inline] pub fn decode_const<'a, 'tcx, D>(decoder: &mut D) -> Result<&'tcx ty::Const<'tcx>, D::Error> @@ -278,7 +266,6 @@ macro_rules! implement_ty_decoder { use $crate::ty::codec::*; use $crate::ty::subst::Substs; use $crate::hir::def_id::{CrateNum}; - use $crate::middle::const_val::ByteArray; use rustc_serialize::{Decoder, SpecializedDecoder}; use std::borrow::Cow; @@ -377,13 +364,6 @@ macro_rules! implement_ty_decoder { } } - impl<$($typaram),*> SpecializedDecoder> - for $DecoderName<$($typaram),*> { - fn specialized_decode(&mut self) -> Result, Self::Error> { - decode_byte_array(self) - } - } - impl<$($typaram),*> SpecializedDecoder<&'tcx $crate::ty::Const<'tcx>> for $DecoderName<$($typaram),*> { fn specialized_decode(&mut self) -> Result<&'tcx ty::Const<'tcx>, Self::Error> { diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 9394a853920e5..4e1f3664d7f85 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -703,26 +703,14 @@ impl<'a, 'tcx> Lift<'tcx> for const_val::ErrKind<'a> { use middle::const_val::ErrKind::*; Some(match *self { - CannotCast => CannotCast, - MissingStructField => MissingStructField, NonConstPath => NonConstPath, UnimplementedConstVal(s) => UnimplementedConstVal(s), - ExpectedConstTuple => ExpectedConstTuple, - ExpectedConstStruct => ExpectedConstStruct, - IndexedNonVec => IndexedNonVec, - IndexNotUsize => IndexNotUsize, IndexOutOfBounds { len, index } => IndexOutOfBounds { len, index }, - MiscBinaryOp => MiscBinaryOp, - MiscCatchAll => MiscCatchAll, - IndexOpFeatureGated => IndexOpFeatureGated, Math(ref e) => Math(e.clone()), LayoutError(ref e) => { return tcx.lift(e).map(LayoutError) } - ErroneousReferencedConstant(ref e) => { - return tcx.lift(e).map(ErroneousReferencedConstant) - } TypeckError => TypeckError, CheckMatchError => CheckMatchError, From f8602ac12c08372cb9d6bac01ed151877063ad67 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 26 Jan 2018 16:24:54 +0100 Subject: [PATCH 043/110] Explain the existance of `is_binop_lang_item` --- src/librustc/ty/context.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 8c508c2f17c81..b760649c37d15 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -1270,6 +1270,10 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self.get_lang_items(LOCAL_CRATE) } + /// Due to missing llvm support for lowering 128 bit math to software emulation + /// (on some targets), the lowering can be done in MIR. + /// + /// This function only exists until said support is implemented. pub fn is_binop_lang_item(&self, def_id: DefId) -> Option<(mir::BinOp, bool)> { let items = self.lang_items(); let def_id = Some(def_id); From 4fbf1199ef5c7340c6b7aa0424beeee4ab88d0c7 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 26 Jan 2018 16:29:37 +0100 Subject: [PATCH 044/110] Accidental paste --- src/librustc/ty/layout.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index b02f221bb8132..382e99f26943f 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -952,7 +952,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { enum StructKind { /// A tuple, closure, or univariant which cannot be coerced to unsized. AlwaysSized, - /// A univariant, the last field of which fn compute_uncachedmay be coerced to unsized. + /// A univariant, the last field of which may be coerced to unsized. MaybeUnsized, /// A univariant, but with a prefix of an arbitrary size & alignment (e.g. enum tag). Prefixed(Size, Align), From 5125d58fcf1190c6cdba814f9b49528764e614f5 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 26 Jan 2018 16:44:10 +0100 Subject: [PATCH 045/110] Simplify code around reading/writing ConstVals --- .../borrow_check/nll/type_check/mod.rs | 62 ++----------------- src/librustc_mir/build/misc.rs | 30 +++------ src/librustc_trans/mir/analyze.rs | 19 ++---- 3 files changed, 21 insertions(+), 90 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 059fd9111b689..36e173dd5d640 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -24,9 +24,7 @@ use rustc::traits::{self, FulfillmentContext}; use rustc::ty::error::TypeError; use rustc::ty::fold::TypeFoldable; use rustc::ty::{self, ToPolyTraitRef, Ty, TyCtxt, TypeVariants}; -use rustc::middle::const_val::ConstVal; use rustc::mir::*; -use rustc::mir::interpret::{Value, PrimVal}; use rustc::mir::tcx::PlaceTy; use rustc::mir::visit::{PlaceContext, Visitor}; use std::fmt; @@ -259,22 +257,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { // constraints on `'a` and `'b`. These constraints // would be lost if we just look at the normalized // value. - let did = match value.val { - ConstVal::Value(Value::ByVal(PrimVal::Ptr(p))) => { - self.tcx() - .interpret_interner - .get_fn(p.alloc_id) - .map(|instance| instance.def_id()) - }, - ConstVal::Value(Value::ByVal(PrimVal::Undef)) => { - match value.ty.sty { - ty::TyFnDef(ty_def_id, _) => Some(ty_def_id), - _ => None, - } - }, - _ => None, - }; - if let Some(def_id) = did { + if let ty::TyFnDef(def_id, substs) = value.ty.sty { let tcx = self.tcx(); let type_checker = &mut self.cx; @@ -287,17 +270,6 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { // are transitioning to the miri-based system, we // don't have a handy function for that, so for // now we just ignore `value.val` regions. - let substs = match value.ty.sty { - ty::TyFnDef(ty_def_id, substs) => { - assert_eq!(def_id, ty_def_id); - substs - } - _ => span_bug!( - self.last_span, - "unexpected type for constant function: {:?}", - value.ty - ), - }; let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, substs); @@ -1029,35 +1001,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } fn is_box_free(&self, operand: &Operand<'tcx>) -> bool { - match operand { - &Operand::Constant(box Constant { - literal: - Literal::Value { - value: - &ty::Const { - val, - ty, - }, - .. - }, - .. - }) => match val { - ConstVal::Value(Value::ByVal(PrimVal::Ptr(p))) => { - let inst = self.tcx().interpret_interner.get_fn(p.alloc_id); - inst.map_or(false, |inst| { - Some(inst.def_id()) == self.tcx().lang_items().box_free_fn() - }) - }, - ConstVal::Value(Value::ByVal(PrimVal::Undef)) => { - match ty.sty { - ty::TyFnDef(ty_def_id, _) => { - Some(ty_def_id) == self.tcx().lang_items().box_free_fn() - } - _ => false, - } + match *operand { + Operand::Constant(ref c) => match c.ty.sty { + ty::TyFnDef(ty_def_id, _) => { + Some(ty_def_id) == self.tcx().lang_items().box_free_fn() } _ => false, - } + }, _ => false, } } diff --git a/src/librustc_mir/build/misc.rs b/src/librustc_mir/build/misc.rs index 78c7004ef6034..6e10c2307c8e6 100644 --- a/src/librustc_mir/build/misc.rs +++ b/src/librustc_mir/build/misc.rs @@ -54,30 +54,20 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // Returns a zero literal operand for the appropriate type, works for // bool, char and integers. pub fn zero_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> { - let literal = match ty.sty { - ty::TyBool => { - self.hir.false_literal() - } - ty::TyChar => { - Literal::Value { - value: self.hir.tcx().mk_const(ty::Const { - val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(0))), - ty - }) - } - } + match ty.sty { + ty::TyBool | + ty::TyChar | ty::TyUint(_) | - ty::TyInt(_) => { - Literal::Value { - value: self.hir.tcx().mk_const(ty::Const { - val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(0))), - ty - }) - } - } + ty::TyInt(_) => {} _ => { span_bug!(span, "Invalid type for zero_literal: `{:?}`", ty) } + } + let literal = Literal::Value { + value: self.hir.tcx().mk_const(ty::Const { + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(0))), + ty + }) }; self.literal_operand(span, ty, literal) diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs index cc5af9489b4cf..dd34dc0345886 100644 --- a/src/librustc_trans/mir/analyze.rs +++ b/src/librustc_trans/mir/analyze.rs @@ -13,11 +13,9 @@ use rustc_data_structures::bitvec::BitVector; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; -use rustc::middle::const_val::ConstVal; -use rustc::mir::{self, Location, TerminatorKind, Literal}; +use rustc::mir::{self, Location, TerminatorKind}; use rustc::mir::visit::{Visitor, PlaceContext}; use rustc::mir::traversal; -use rustc::mir::interpret::{Value, PrimVal}; use rustc::ty; use rustc::ty::layout::LayoutOf; use type_of::LayoutLlvmExt; @@ -112,19 +110,12 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> { location: Location) { let check = match *kind { mir::TerminatorKind::Call { - func: mir::Operand::Constant(box mir::Constant { - literal: Literal::Value { - value: &ty::Const { val, ty }, .. - }, .. - }), + func: mir::Operand::Constant(ref c), ref args, .. - } => match val { - ConstVal::Value(Value::ByVal(PrimVal::Undef)) => match ty.sty { - ty::TyFnDef(did, _) => Some((did, args)), - _ => None, - }, + } => match c.ty.sty { + ty::TyFnDef(did, _) => Some((did, args)), _ => None, - } + }, _ => None, }; if let Some((def_id, args)) = check { From df6b40e342879169962295e27f1f878998f22bc3 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Sat, 27 Jan 2018 16:07:45 +0100 Subject: [PATCH 046/110] Add regression tests --- src/test/run-pass/ctfe/union-ice.rs | 45 ++++++++++++++++++++ src/test/ui/const-eval/index_out_of_bound.rs | 13 ++++++ 2 files changed, 58 insertions(+) create mode 100644 src/test/run-pass/ctfe/union-ice.rs create mode 100644 src/test/ui/const-eval/index_out_of_bound.rs diff --git a/src/test/run-pass/ctfe/union-ice.rs b/src/test/run-pass/ctfe/union-ice.rs new file mode 100644 index 0000000000000..f83f49f298b90 --- /dev/null +++ b/src/test/run-pass/ctfe/union-ice.rs @@ -0,0 +1,45 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(const_fn)] + +type Field1 = i32; +type Field2 = f32; +type Field3 = i64; + +union DummyUnion { + field1: Field1, + field2: Field2, + field3: Field3, +} + +const FLOAT1_AS_I32: i32 = 1065353216; +const UNION: DummyUnion = DummyUnion { field1: FLOAT1_AS_I32 }; + +const fn read_field1() -> Field1 { + const FIELD1: Field1 = unsafe { UNION.field1 }; + FIELD1 +} + +const fn read_field2() -> Field2 { + const FIELD2: Field2 = unsafe { UNION.field2 }; + FIELD2 +} + +const fn read_field3() -> Field3 { + const FIELD3: Field3 = unsafe { UNION.field3 }; + FIELD3 +} + +fn main() { + assert_eq!(read_field1(), FLOAT1_AS_I32); + assert_eq!(read_field2(), 1.0); + assert_eq!(read_field3(), unsafe { UNION.field3 }); +} diff --git a/src/test/ui/const-eval/index_out_of_bound.rs b/src/test/ui/const-eval/index_out_of_bound.rs new file mode 100644 index 0000000000000..0109f72aa1d8d --- /dev/null +++ b/src/test/ui/const-eval/index_out_of_bound.rs @@ -0,0 +1,13 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +static FOO: i32 = [][0]; //~ ERROR E0080 + +fn main() {} From 5b247b9bbe7252db2d22d3ec4be172c73f1f94e9 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Sat, 27 Jan 2018 17:15:40 +0100 Subject: [PATCH 047/110] Report errors in statics during collecting instead of translating --- src/librustc/ty/error.rs | 8 +- src/librustc_mir/monomorphize/collector.rs | 157 ++++++++++++------- src/librustc_trans/base.rs | 2 + src/test/compile-fail/huge-array.rs | 2 +- src/test/ui/const-eval/index_out_of_bound.rs | 4 +- src/test/ui/const-fn-error.stderr | 12 +- 6 files changed, 116 insertions(+), 69 deletions(-) diff --git a/src/librustc/ty/error.rs b/src/librustc/ty/error.rs index 5dc78d84da835..b50b9cb43d3a1 100644 --- a/src/librustc/ty/error.rs +++ b/src/librustc/ty/error.rs @@ -9,10 +9,7 @@ // except according to those terms. use hir::def_id::DefId; -use middle::const_val::ConstVal; use ty::{self, BoundRegion, Region, Ty, TyCtxt}; -use mir::interpret::{Value, PrimVal}; - use std::fmt; use syntax::abi; use syntax::ast; @@ -185,9 +182,8 @@ impl<'a, 'gcx, 'lcx, 'tcx> ty::TyS<'tcx> { ty::TyAdt(def, _) => format!("{} `{}`", def.descr(), tcx.item_path_str(def.did)), ty::TyForeign(def_id) => format!("extern type `{}`", tcx.item_path_str(def_id)), ty::TyArray(_, n) => { - match n.val { - ConstVal::Value(Value::ByVal(PrimVal::Bytes(n))) => - format!("array of {} elements", n), + match n.val.to_raw_bits() { + Some(n) => format!("array of {} elements", n), _ => "array".to_string(), } } diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index 3d393b5696a3a..a6df0465789b2 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -201,9 +201,10 @@ use rustc::ty::subst::{Substs, Kind}; use rustc::ty::{self, TypeFoldable, Ty, TyCtxt}; use rustc::ty::adjustment::CustomCoerceUnsized; use rustc::session::config; -use rustc::mir::{self, Location}; +use rustc::mir::{self, Location, Promoted}; use rustc::mir::visit::Visitor as MirVisitor; use rustc::mir::mono::MonoItem; +use rustc::mir::interpret::GlobalId; use monomorphize::{self, Instance}; use rustc::util::nodemap::{FxHashSet, FxHashMap, DefIdMap}; @@ -378,7 +379,19 @@ fn collect_items_rec<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, recursion_depth_reset = None; - collect_neighbours(tcx, instance, true, &mut neighbors); + let cid = GlobalId { + instance, + promoted: None, + }; + let param_env = ty::ParamEnv::empty(traits::Reveal::All); + + match tcx.const_eval(param_env.and(cid)) { + Ok(val) => collect_const(tcx, val, instance.substs, &mut neighbors), + Err(err) => { + let span = tcx.def_span(def_id); + err.report(tcx, span, "static"); + } + } } MonoItem::Fn(instance) => { // Sanity check whether this ended up being collected accidentally @@ -390,7 +403,7 @@ fn collect_items_rec<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, recursion_depths)); check_type_length_limit(tcx, instance); - collect_neighbours(tcx, instance, false, &mut neighbors); + collect_neighbours(tcx, instance, &mut neighbors); } MonoItem::GlobalAsm(..) => { recursion_depth_reset = None; @@ -499,7 +512,6 @@ struct MirNeighborCollector<'a, 'tcx: 'a> { mir: &'a mir::Mir<'tcx>, output: &'a mut Vec>, param_substs: &'tcx Substs<'tcx>, - const_context: bool, } impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { @@ -569,27 +581,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, location: Location) { debug!("visiting const {:?} @ {:?}", *constant, location); - match constant.val { - ConstVal::Unevaluated(def_id, substs) => { - let substs = self.tcx.trans_apply_param_substs(self.param_substs, - &substs); - let instance = ty::Instance::resolve(self.tcx, - ty::ParamEnv::empty(traits::Reveal::All), - def_id, - substs).unwrap(); - collect_neighbours(self.tcx, instance, true, self.output); - }, - ConstVal::Value(Value::ByValPair(PrimVal::Ptr(a), PrimVal::Ptr(b))) => { - collect_miri(self.tcx, a.alloc_id, self.output); - collect_miri(self.tcx, b.alloc_id, self.output); - } - ConstVal::Value(Value::ByValPair(_, PrimVal::Ptr(ptr))) | - ConstVal::Value(Value::ByValPair(PrimVal::Ptr(ptr), _)) | - ConstVal::Value(Value::ByVal(PrimVal::Ptr(ptr))) | - ConstVal::Value(Value::ByRef(Pointer { primval: PrimVal::Ptr(ptr) }, _)) => - collect_miri(self.tcx, ptr.alloc_id, self.output), - _ => {}, - } + collect_const(self.tcx, constant, self.param_substs, self.output); self.super_const(constant); } @@ -605,30 +597,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { mir::TerminatorKind::Call { ref func, .. } => { let callee_ty = func.ty(self.mir, tcx); let callee_ty = tcx.trans_apply_param_substs(self.param_substs, &callee_ty); - - let constness = match (self.const_context, &callee_ty.sty) { - (true, &ty::TyFnDef(def_id, substs)) if self.tcx.is_const_fn(def_id) => { - let instance = - ty::Instance::resolve(self.tcx, - ty::ParamEnv::empty(traits::Reveal::All), - def_id, - substs).unwrap(); - Some(instance) - } - _ => None - }; - - if let Some(const_fn_instance) = constness { - // If this is a const fn, called from a const context, we - // have to visit its body in order to find any fn reifications - // it might contain. - collect_neighbours(self.tcx, - const_fn_instance, - true, - self.output); - } else { - visit_fn_use(self.tcx, callee_ty, true, &mut self.output); - } + visit_fn_use(self.tcx, callee_ty, true, &mut self.output); } mir::TerminatorKind::Drop { ref location, .. } | mir::TerminatorKind::DropAndReplace { ref location, .. } => { @@ -1117,7 +1086,14 @@ fn collect_miri<'a, 'tcx>( alloc_id: AllocId, output: &mut Vec>, ) { - if let Some(alloc) = tcx.interpret_interner.get_alloc(alloc_id) { + if let Some(did) = tcx.interpret_interner.get_corresponding_static_def_id(alloc_id) { + let instance = Instance::mono(tcx, did); + if should_monomorphize_locally(tcx, &instance) { + trace!("collecting static {:?}", did); + let node_id = tcx.hir.as_local_node_id(did).unwrap(); + output.push(MonoItem::Static(node_id)); + } + } else if let Some(alloc) = tcx.interpret_interner.get_alloc(alloc_id) { trace!("collecting {:?} with {:#?}", alloc_id, alloc); for &inner in alloc.relocations.values() { collect_miri(tcx, inner, output); @@ -1135,23 +1111,29 @@ fn collect_miri<'a, 'tcx>( /// Scan the MIR in order to find function calls, closures, and drop-glue fn collect_neighbours<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>, - const_context: bool, output: &mut Vec>) { let mir = tcx.instance_mir(instance.def); - let mut visitor = MirNeighborCollector { + MirNeighborCollector { tcx, mir: &mir, output, param_substs: instance.substs, - const_context, - }; - - visitor.visit_mir(&mir); - for promoted in &mir.promoted { - visitor.mir = promoted; - visitor.visit_mir(promoted); + }.visit_mir(&mir); + let param_env = ty::ParamEnv::empty(traits::Reveal::All); + for (i, promoted) in mir.promoted.iter().enumerate() { + use rustc_data_structures::indexed_vec::Idx; + let cid = GlobalId { + instance, + promoted: Some(Promoted::new(i)), + }; + match tcx.const_eval(param_env.and(cid)) { + Ok(val) => collect_const(tcx, val, instance.substs, output), + Err(err) => { + err.report(tcx, promoted.span, "promoted"); + } + } } } @@ -1163,3 +1145,60 @@ fn def_id_to_string<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, printer.push_def_path(def_id, &mut output); output } + +fn collect_const<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + constant: &ty::Const<'tcx>, + param_substs: &'tcx Substs<'tcx>, + output: &mut Vec>, +) { + debug!("visiting const {:?}", *constant); + + let val = match constant.val { + ConstVal::Unevaluated(def_id, substs) => { + let param_env = ty::ParamEnv::empty(traits::Reveal::All); + let substs = tcx.trans_apply_param_substs(param_substs, + &substs); + let instance = ty::Instance::resolve(tcx, + param_env, + def_id, + substs).unwrap(); + + let cid = GlobalId { + instance, + promoted: None, + }; + match tcx.const_eval(param_env.and(cid)) { + Ok(val) => val.val, + Err(err) => { + let span = tcx.def_span(def_id); + err.report(tcx, span, "constant"); + return; + } + } + }, + _ => constant.val, + }; + match val { + ConstVal::Unevaluated(..) => bug!("const eval yielded unevaluated const"), + ConstVal::Value(Value::ByValPair(PrimVal::Ptr(a), PrimVal::Ptr(b))) => { + collect_miri(tcx, a.alloc_id, output); + collect_miri(tcx, b.alloc_id, output); + } + ConstVal::Value(Value::ByValPair(_, PrimVal::Ptr(ptr))) | + ConstVal::Value(Value::ByValPair(PrimVal::Ptr(ptr), _)) | + ConstVal::Value(Value::ByVal(PrimVal::Ptr(ptr))) => + collect_miri(tcx, ptr.alloc_id, output), + ConstVal::Value(Value::ByRef(Pointer { primval: PrimVal::Ptr(ptr) }, _)) => { + // by ref should only collect the inner allocation, not the value itself + let alloc = tcx + .interpret_interner + .get_alloc(ptr.alloc_id) + .expect("ByRef to extern static is not allowed"); + for &inner in alloc.relocations.values() { + collect_miri(tcx, inner, output); + } + } + _ => {}, + } +} diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index bae9fe8bd1b0d..49a5b7ac8b907 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -972,6 +972,8 @@ fn collect_and_partition_translation_items<'a, 'tcx>( collector::collect_crate_mono_items(tcx, collection_mode) }); + tcx.sess.abort_if_errors(); + ::rustc_mir::monomorphize::assert_symbols_are_distinct(tcx, items.iter()); let strategy = if tcx.sess.opts.incremental.is_some() { diff --git a/src/test/compile-fail/huge-array.rs b/src/test/compile-fail/huge-array.rs index 029e9651cb3cd..7de84802e1d05 100644 --- a/src/test/compile-fail/huge-array.rs +++ b/src/test/compile-fail/huge-array.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:; 1518599999 +// error-pattern:; 1518600000 fn generic(t: T) { let s: [T; 1518600000] = [t; 1518600000]; diff --git a/src/test/ui/const-eval/index_out_of_bound.rs b/src/test/ui/const-eval/index_out_of_bound.rs index 0109f72aa1d8d..632804f2fd366 100644 --- a/src/test/ui/const-eval/index_out_of_bound.rs +++ b/src/test/ui/const-eval/index_out_of_bound.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -static FOO: i32 = [][0]; //~ ERROR E0080 +static FOO: i32 = [][0]; +//~^ ERROR E0080 +//~| ERROR E0080 fn main() {} diff --git a/src/test/ui/const-fn-error.stderr b/src/test/ui/const-fn-error.stderr index 26c238992ab60..f7ce0565c85ca 100644 --- a/src/test/ui/const-fn-error.stderr +++ b/src/test/ui/const-fn-error.stderr @@ -20,15 +20,23 @@ error[E0016]: blocks in constant functions are limited to items and tail express | ^ error[E0015]: calls in constant functions are limited to constant functions, struct and enum constructors - --> $DIR/const-fn-error.rs:17:14 + --> $DIR/const-fn-error.rs:18:14 | +<<<<<<< HEAD LL | for i in 0..x { //~ ERROR calls in constant functions +======= +18 | for i in 0..x { +>>>>>>> Report errors in statics during collecting instead of translating | ^^^^ error[E0019]: constant function contains unimplemented expression type - --> $DIR/const-fn-error.rs:17:14 + --> $DIR/const-fn-error.rs:18:14 | +<<<<<<< HEAD LL | for i in 0..x { //~ ERROR calls in constant functions +======= +18 | for i in 0..x { +>>>>>>> Report errors in statics during collecting instead of translating | ^^^^ error[E0080]: constant evaluation error From edb2af58e4171a5a34fef0cc1b542039e5fc4701 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Sun, 28 Jan 2018 14:11:55 +0100 Subject: [PATCH 048/110] Don't use scary block reorderings --- src/librustc_mir/transform/instcombine.rs | 60 ++--------------------- 1 file changed, 4 insertions(+), 56 deletions(-) diff --git a/src/librustc_mir/transform/instcombine.rs b/src/librustc_mir/transform/instcombine.rs index 640fd7cadab2c..d5fa379dea00e 100644 --- a/src/librustc_mir/transform/instcombine.rs +++ b/src/librustc_mir/transform/instcombine.rs @@ -12,8 +12,8 @@ use rustc::mir::{Constant, Literal, Location, Place, Mir, Operand, ProjectionElem, Rvalue, Local}; use rustc::mir::{NullOp, StatementKind, Statement, BasicBlock, LocalKind}; -use rustc::mir::{SourceInfo, ARGUMENT_VISIBILITY_SCOPE, TerminatorKind}; -use rustc::mir::visit::{MutVisitor, Visitor, TyContext}; +use rustc::mir::TerminatorKind; +use rustc::mir::visit::{MutVisitor, Visitor}; use rustc::middle::const_val::ConstVal; use rustc::ty::{TyCtxt, TypeVariants, self, Instance}; use rustc::mir::interpret::{Value, PrimVal, GlobalId}; @@ -21,10 +21,8 @@ use interpret::{eval_body_with_mir, eval_body, mk_borrowck_eval_cx, unary_op, Va use rustc::util::nodemap::{FxHashMap, FxHashSet}; use rustc_data_structures::indexed_vec::Idx; use std::mem; -use std::collections::VecDeque; use transform::{MirPass, MirSource}; use syntax::codemap::Span; -use rustc_data_structures::control_flow_graph::ControlFlowGraph; use rustc::ty::subst::Substs; pub struct InstCombine; @@ -73,7 +71,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for InstCombineVisitor<'a, 'tcx> { } if let Some(constant) = self.optimizations.arrays_lengths.remove(&location) { - debug!("Replacing `Len([_; N])`: {:?}", rvalue); + debug!("Replacing `Len([_; N])`: {:?} with {:?}", rvalue, constant); *rvalue = Rvalue::Use(Operand::Constant(box constant)); } @@ -101,6 +99,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for InstCombineVisitor<'a, 'tcx> { ) { self.super_constant(constant, location); if let Some(&(val, ty, _)) = self.optimizations.constants.get(constant) { + debug!("Replacing `{:?}` with {:?}:{:?}", constant.literal, val, ty); constant.literal = Literal::Value { value: self.tcx.mk_const(ty::Const { val: ConstVal::Value(val), @@ -464,57 +463,6 @@ impl<'tcx> Visitor<'tcx> for ConstPropVisitor { } impl<'b, 'a, 'tcx> Visitor<'tcx> for OptimizationFinder<'b, 'a, 'tcx> { - // override to visit basic blocks in execution order - fn super_mir(&mut self, mir: &Mir<'tcx>) { - let mut seen = FxHashSet::default(); - seen.insert(mir.start_node()); - let mut sorted = Vec::new(); - let mut next = VecDeque::new(); - sorted.push(mir.start_node()); - next.push_back(mir.start_node()); - while let Some(current) = next.pop_front() { - for successor in mir.successors(current) { - trace!("checking successor of {:?}: {:?}", current, successor); - trace!("{:?}, {:?}", sorted, next); - if seen.contains(&successor) { - for &pending in &next { - // not a back-edge, just a branch merging back into a single execution - if pending == successor { - // move to the back of the queue - let i = sorted.iter().position(|&b| b == successor).unwrap(); - sorted.remove(i); - sorted.push(successor); - break; - } - } - } else { - seen.insert(successor); - sorted.push(successor); - next.push_back(successor); - } - } - } - trace!("checking basic blocks: {:?}", sorted); - for bb in sorted { - self.visit_basic_block_data(bb, &mir[bb]); - } - - for scope in &mir.visibility_scopes { - self.visit_visibility_scope_data(scope); - } - - self.visit_ty(&mir.return_ty(), TyContext::ReturnTy(SourceInfo { - span: mir.span, - scope: ARGUMENT_VISIBILITY_SCOPE, - })); - - for local in mir.local_decls.indices() { - self.visit_local_decl(local, &mir.local_decls[local]); - } - - self.visit_span(&mir.span); - } - fn visit_constant( &mut self, constant: &Constant<'tcx>, From 54615ec9894f4f101bb15d70b6dc9b0c61f1249c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Sun, 28 Jan 2018 14:41:17 +0100 Subject: [PATCH 049/110] Split const prop into its own pass --- src/librustc_mir/transform/const_prop.rs | 513 ++++++++++++++++++++++ src/librustc_mir/transform/instcombine.rs | 444 +------------------ src/librustc_mir/transform/mod.rs | 2 + 3 files changed, 520 insertions(+), 439 deletions(-) create mode 100644 src/librustc_mir/transform/const_prop.rs diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs new file mode 100644 index 0000000000000..652b921e80a74 --- /dev/null +++ b/src/librustc_mir/transform/const_prop.rs @@ -0,0 +1,513 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Propagates constants for early reporting of statically known +//! assertion failures + + + +use rustc::mir::{Constant, Literal, Location, Place, Mir, Operand, Rvalue, Local}; +use rustc::mir::{NullOp, StatementKind, Statement, BasicBlock, LocalKind}; +use rustc::mir::TerminatorKind; +use rustc::mir::visit::{MutVisitor, Visitor}; +use rustc::middle::const_val::ConstVal; +use rustc::ty::{TyCtxt, self, Instance}; +use rustc::mir::interpret::{Value, PrimVal, GlobalId}; +use interpret::{eval_body_with_mir, eval_body, mk_borrowck_eval_cx, unary_op, ValTy}; +use rustc::util::nodemap::FxHashMap; +use transform::{MirPass, MirSource}; +use syntax::codemap::Span; +use rustc::ty::subst::Substs; + +pub struct ConstProp; + +impl MirPass for ConstProp { + fn run_pass<'a, 'tcx>(&self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + source: MirSource, + mir: &mut Mir<'tcx>) { + trace!("ConstProp starting for {:?}", source.def_id); + + // First, find optimization opportunities. This is done in a pre-pass to keep the MIR + // read-only so that we can do global analyses on the MIR in the process (e.g. + // `Place::ty()`). + let optimizations = { + let mut optimization_finder = OptimizationFinder::new(mir, tcx, source); + optimization_finder.visit_mir(mir); + optimization_finder.optimizations + }; + + // Then carry out those optimizations. + MutVisitor::visit_mir(&mut ConstPropVisitor { optimizations, tcx }, mir); + trace!("ConstProp done for {:?}", source.def_id); + } +} + +type Const<'tcx> = (Value, ty::Ty<'tcx>, Span); + +pub struct ConstPropVisitor<'a, 'tcx: 'a> { + optimizations: OptimizationList<'tcx>, + tcx: TyCtxt<'a, 'tcx, 'tcx>, +} + +impl<'a, 'tcx> MutVisitor<'tcx> for ConstPropVisitor<'a, 'tcx> { + fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) { + if let Some((value, ty, span)) = self.optimizations.const_prop.remove(&location) { + let value = self.tcx.mk_const(ty::Const { + val: ConstVal::Value(value), + ty, + }); + debug!("Replacing `{:?}` with {:?}", rvalue, value); + let constant = Constant { + ty, + literal: Literal::Value { value }, + span, + }; + *rvalue = Rvalue::Use(Operand::Constant(box constant)); + } + + self.super_rvalue(rvalue, location) + } + + fn visit_constant( + &mut self, + constant: &mut Constant<'tcx>, + location: Location, + ) { + self.super_constant(constant, location); + if let Some(&(val, ty, _)) = self.optimizations.constants.get(constant) { + debug!("Replacing `{:?}` with {:?}:{:?}", constant.literal, val, ty); + constant.literal = Literal::Value { + value: self.tcx.mk_const(ty::Const { + val: ConstVal::Value(val), + ty, + }), + }; + } + } + + fn visit_operand( + &mut self, + operand: &mut Operand<'tcx>, + location: Location, + ) { + self.super_operand(operand, location); + let new = match operand { + Operand::Move(Place::Local(local)) | + Operand::Copy(Place::Local(local)) => { + trace!("trying to read {:?}", local); + self.optimizations.places.get(&local).cloned() + }, + _ => return, + }; + if let Some((value, ty, span)) = new { + let value = self.tcx.mk_const(ty::Const { + val: ConstVal::Value(value), + ty, + }); + debug!("Replacing `{:?}` with {:?}", operand, value); + let constant = Constant { + ty, + literal: Literal::Value { value }, + span, + }; + *operand = Operand::Constant(box constant); + } + } + + fn visit_terminator_kind( + &mut self, + block: BasicBlock, + kind: &mut TerminatorKind<'tcx>, + location: Location, + ) { + match kind { + TerminatorKind::SwitchInt { discr: value, .. } | + TerminatorKind::Yield { value, .. } | + TerminatorKind::Assert { cond: value, .. } => { + if let Some((new, ty, span)) = self.optimizations.terminators.remove(&block) { + let new = self.tcx.mk_const(ty::Const { + val: ConstVal::Value(new), + ty, + }); + debug!("Replacing `{:?}` with {:?}", value, new); + let constant = Constant { + ty, + literal: Literal::Value { value: new }, + span, + }; + *value = Operand::Constant(box constant); + } + } + // FIXME: do this optimization for function calls + _ => {}, + } + self.super_terminator_kind(block, kind, location) + } +} + +/// Finds optimization opportunities on the MIR. +struct OptimizationFinder<'b, 'a, 'tcx:'a+'b> { + mir: &'b Mir<'tcx>, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + source: MirSource, + optimizations: OptimizationList<'tcx>, +} + +impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { + fn new( + mir: &'b Mir<'tcx>, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + source: MirSource, + ) -> OptimizationFinder<'b, 'a, 'tcx> { + OptimizationFinder { + mir, + tcx, + source, + optimizations: OptimizationList::default(), + } + } + + fn eval_constant(&mut self, c: &Constant<'tcx>) -> Option> { + if let Some(&val) = self.optimizations.constants.get(c) { + return Some(val); + } + match c.literal { + Literal::Value { value } => match value.val { + ConstVal::Value(v) => Some((v, value.ty, c.span)), + ConstVal::Unevaluated(did, substs) => { + let param_env = self.tcx.param_env(self.source.def_id); + let instance = Instance::resolve( + self.tcx, + param_env, + did, + substs, + )?; + let cid = GlobalId { + instance, + promoted: None, + }; + let (value, _, ty) = eval_body(self.tcx, cid, param_env)?; + let val = (value, ty, c.span); + trace!("evaluated {:?} to {:?}", c, val); + self.optimizations.constants.insert(c.clone(), val); + Some(val) + }, + }, + // evaluate the promoted and replace the constant with the evaluated result + Literal::Promoted { index } => { + let generics = self.tcx.generics_of(self.source.def_id); + if generics.parent_types as usize + generics.types.len() > 0 { + // FIXME: can't handle code with generics + return None; + } + let substs = Substs::identity_for_item(self.tcx, self.source.def_id); + let instance = Instance::new(self.source.def_id, substs); + let cid = GlobalId { + instance, + promoted: Some(index), + }; + let param_env = self.tcx.param_env(self.source.def_id); + let (value, _, ty) = eval_body_with_mir(self.tcx, cid, self.mir, param_env)?; + let val = (value, ty, c.span); + trace!("evaluated {:?} to {:?}", c, val); + self.optimizations.constants.insert(c.clone(), val); + Some(val) + } + } + } + + fn eval_operand(&mut self, op: &Operand<'tcx>) -> Option> { + match *op { + Operand::Constant(ref c) => self.eval_constant(c), + Operand::Move(ref place) | Operand::Copy(ref place) => match *place { + Place::Local(loc) => self.optimizations.places.get(&loc).cloned(), + // FIXME(oli-obk): field and index projections + Place::Projection(_) => None, + _ => None, + }, + } + } + + fn simplify_operand(&mut self, op: &Operand<'tcx>) -> Option> { + match *op { + Operand::Constant(ref c) => match c.literal { + Literal::Value { .. } => None, + _ => self.eval_operand(op), + }, + _ => self.eval_operand(op), + } + } + + fn const_prop( + &mut self, + rvalue: &Rvalue<'tcx>, + place_ty: ty::Ty<'tcx>, + span: Span, + ) -> Option> { + match *rvalue { + // No need to overwrite an already evaluated constant + Rvalue::Use(Operand::Constant(box Constant { + literal: Literal::Value { + value: &ty::Const { + val: ConstVal::Value(_), + .. + }, + }, + .. + })) => None, + // This branch exists for the sanity type check + Rvalue::Use(Operand::Constant(ref c)) => { + assert_eq!(c.ty, place_ty); + self.eval_constant(c) + }, + Rvalue::Use(ref op) => { + self.eval_operand(op) + }, + Rvalue::Repeat(..) | + Rvalue::Ref(..) | + Rvalue::Cast(..) | + Rvalue::Aggregate(..) | + Rvalue::NullaryOp(NullOp::Box, _) | + Rvalue::Discriminant(..) => None, + // FIXME(oli-obk): evaluate static/constant slice lengths + Rvalue::Len(_) => None, + Rvalue::NullaryOp(NullOp::SizeOf, ty) => { + let param_env = self.tcx.param_env(self.source.def_id); + type_size_of(self.tcx, param_env, ty).map(|n| ( + Value::ByVal(PrimVal::Bytes(n as u128)), + self.tcx.types.usize, + span, + )) + } + Rvalue::UnaryOp(op, ref arg) => { + let def_id = if self.tcx.is_closure(self.source.def_id) { + self.tcx.closure_base_def_id(self.source.def_id) + } else { + self.source.def_id + }; + let generics = self.tcx.generics_of(def_id); + if generics.parent_types as usize + generics.types.len() > 0 { + // FIXME: can't handle code with generics + return None; + } + let substs = Substs::identity_for_item(self.tcx, self.source.def_id); + let instance = Instance::new(self.source.def_id, substs); + let ecx = mk_borrowck_eval_cx(self.tcx, instance, self.mir, span).unwrap(); + + let val = self.eval_operand(arg)?; + let prim = ecx.value_to_primval(ValTy { value: val.0, ty: val.1 }).ok()?; + let kind = ecx.ty_to_primval_kind(val.1).ok()?; + match unary_op(op, prim, kind) { + Ok(val) => Some((Value::ByVal(val), place_ty, span)), + Err(mut err) => { + ecx.report(&mut err, false, Some(span)); + None + }, + } + } + Rvalue::CheckedBinaryOp(op, ref left, ref right) | + Rvalue::BinaryOp(op, ref left, ref right) => { + trace!("rvalue binop {:?} for {:?} and {:?}", op, left, right); + let left = self.eval_operand(left)?; + let right = self.eval_operand(right)?; + let def_id = if self.tcx.is_closure(self.source.def_id) { + self.tcx.closure_base_def_id(self.source.def_id) + } else { + self.source.def_id + }; + let generics = self.tcx.generics_of(def_id); + let has_generics = generics.parent_types as usize + generics.types.len() > 0; + if has_generics { + // FIXME: can't handle code with generics + return None; + } + let substs = Substs::identity_for_item(self.tcx, self.source.def_id); + let instance = Instance::new(self.source.def_id, substs); + let ecx = mk_borrowck_eval_cx(self.tcx, instance, self.mir, span).unwrap(); + + let l = ecx.value_to_primval(ValTy { value: left.0, ty: left.1 }).ok()?; + let r = ecx.value_to_primval(ValTy { value: right.0, ty: right.1 }).ok()?; + trace!("const evaluating {:?} for {:?} and {:?}", op, left, right); + match ecx.binary_op(op, l, left.1, r, right.1) { + Ok((val, overflow)) => { + let val = if let Rvalue::CheckedBinaryOp(..) = *rvalue { + Value::ByValPair( + val, + PrimVal::from_bool(overflow), + ) + } else { + if overflow { + use rustc::mir::interpret::EvalErrorKind; + let mut err = EvalErrorKind::OverflowingMath.into(); + ecx.report(&mut err, false, Some(span)); + return None; + } + Value::ByVal(val) + }; + Some((val, place_ty, span)) + }, + Err(mut err) => { + ecx.report(&mut err, false, Some(span)); + None + }, + } + }, + } + } +} + +fn type_size_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty: ty::Ty<'tcx>) -> Option { + use rustc::ty::layout::LayoutOf; + (tcx, param_env).layout_of(ty).ok().map(|layout| layout.size.bytes()) +} + +struct CanConstProp { + local: Local, + can_const_prop: bool, + // false at the beginning, once set, there are not allowed to be any more assignments + found_assignment: bool, +} + +impl CanConstProp { + /// returns true if `local` can be propagated + fn check<'tcx>(local: Local, mir: &Mir<'tcx>) -> bool { + let mut cpv = CanConstProp { + local, + can_const_prop: true, + found_assignment: false, + }; + cpv.visit_mir(mir); + cpv.can_const_prop + } + + fn is_our_local(&mut self, mut place: &Place) -> bool { + while let Place::Projection(ref proj) = place { + place = &proj.base; + } + if let Place::Local(local) = *place { + local == self.local + } else { + false + } + } +} + +impl<'tcx> Visitor<'tcx> for CanConstProp { + fn visit_statement( + &mut self, + block: BasicBlock, + statement: &Statement<'tcx>, + location: Location, + ) { + self.super_statement(block, statement, location); + match statement.kind { + StatementKind::SetDiscriminant { ref place, .. } | + StatementKind::Assign(ref place, _) => { + if self.is_our_local(place) { + if self.found_assignment { + self.can_const_prop = false; + } else { + self.found_assignment = true + } + } + }, + StatementKind::InlineAsm { ref outputs, .. } => { + for place in outputs { + if self.is_our_local(place) { + if self.found_assignment { + self.can_const_prop = false; + } else { + self.found_assignment = true + } + return; + } + } + } + _ => {} + } + } + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { + self.super_rvalue(rvalue, location); + if let Rvalue::Ref(_, _, ref place) = *rvalue { + if self.is_our_local(place) { + self.can_const_prop = false; + } + } + } +} + +impl<'b, 'a, 'tcx> Visitor<'tcx> for OptimizationFinder<'b, 'a, 'tcx> { + fn visit_constant( + &mut self, + constant: &Constant<'tcx>, + location: Location, + ) { + trace!("visit_constant: {:?}", constant); + self.super_constant(constant, location); + self.eval_constant(constant); + } + + fn visit_statement( + &mut self, + block: BasicBlock, + statement: &Statement<'tcx>, + location: Location, + ) { + trace!("visit_statement: {:?}", statement); + if let StatementKind::Assign(ref place, ref rval) = statement.kind { + let place_ty = place + .ty(&self.mir.local_decls, self.tcx) + .to_ty(self.tcx); + let span = statement.source_info.span; + if let Some(value) = self.const_prop(rval, place_ty, span) { + self.optimizations.const_prop.insert(location, value); + if let Place::Local(local) = *place { + if self.mir.local_kind(local) == LocalKind::Temp + && CanConstProp::check(local, self.mir) { + trace!("storing {:?} to {:?}", value, local); + assert!(self.optimizations.places.insert(local, value).is_none()); + } + } + } + } + self.super_statement(block, statement, location); + } + + fn visit_terminator_kind( + &mut self, + block: BasicBlock, + kind: &TerminatorKind<'tcx>, + _location: Location, + ) { + match kind { + TerminatorKind::SwitchInt { discr: value, .. } | + TerminatorKind::Yield { value, .. } | + TerminatorKind::Assert { cond: value, .. } => { + if let Some(value) = self.simplify_operand(value) { + self.optimizations.terminators.insert(block, value); + } + } + // FIXME: do this optimization for function calls + _ => {}, + } + } +} + +#[derive(Default)] +struct OptimizationList<'tcx> { + const_prop: FxHashMap>, + /// Terminators that get their Operand(s) turned into constants. + terminators: FxHashMap>, + places: FxHashMap>, + constants: FxHashMap, Const<'tcx>>, +} diff --git a/src/librustc_mir/transform/instcombine.rs b/src/librustc_mir/transform/instcombine.rs index d5fa379dea00e..d1c2c7a61d495 100644 --- a/src/librustc_mir/transform/instcombine.rs +++ b/src/librustc_mir/transform/instcombine.rs @@ -11,19 +11,12 @@ //! Performs various peephole optimizations. use rustc::mir::{Constant, Literal, Location, Place, Mir, Operand, ProjectionElem, Rvalue, Local}; -use rustc::mir::{NullOp, StatementKind, Statement, BasicBlock, LocalKind}; -use rustc::mir::TerminatorKind; use rustc::mir::visit::{MutVisitor, Visitor}; -use rustc::middle::const_val::ConstVal; -use rustc::ty::{TyCtxt, TypeVariants, self, Instance}; -use rustc::mir::interpret::{Value, PrimVal, GlobalId}; -use interpret::{eval_body_with_mir, eval_body, mk_borrowck_eval_cx, unary_op, ValTy}; +use rustc::ty::{TyCtxt, TypeVariants}; use rustc::util::nodemap::{FxHashMap, FxHashSet}; use rustc_data_structures::indexed_vec::Idx; use std::mem; use transform::{MirPass, MirSource}; -use syntax::codemap::Span; -use rustc::ty::subst::Substs; pub struct InstCombine; @@ -38,25 +31,22 @@ impl MirPass for InstCombine { // read-only so that we can do global analyses on the MIR in the process (e.g. // `Place::ty()`). let optimizations = { - let mut optimization_finder = OptimizationFinder::new(mir, tcx, source); + let mut optimization_finder = OptimizationFinder::new(mir, tcx); optimization_finder.visit_mir(mir); optimization_finder.optimizations }; // Then carry out those optimizations. - MutVisitor::visit_mir(&mut InstCombineVisitor { optimizations, tcx }, mir); + MutVisitor::visit_mir(&mut InstCombineVisitor { optimizations }, mir); trace!("InstCombine done for {:?}", source.def_id); } } -type Const<'tcx> = (Value, ty::Ty<'tcx>, Span); - -pub struct InstCombineVisitor<'a, 'tcx: 'a> { +pub struct InstCombineVisitor<'tcx> { optimizations: OptimizationList<'tcx>, - tcx: TyCtxt<'a, 'tcx, 'tcx>, } -impl<'a, 'tcx> MutVisitor<'tcx> for InstCombineVisitor<'a, 'tcx> { +impl<'tcx> MutVisitor<'tcx> for InstCombineVisitor<'tcx> { fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) { if self.optimizations.and_stars.remove(&location) { debug!("Replacing `&*`: {:?}", rvalue); @@ -75,105 +65,14 @@ impl<'a, 'tcx> MutVisitor<'tcx> for InstCombineVisitor<'a, 'tcx> { *rvalue = Rvalue::Use(Operand::Constant(box constant)); } - if let Some((value, ty, span)) = self.optimizations.const_prop.remove(&location) { - let value = self.tcx.mk_const(ty::Const { - val: ConstVal::Value(value), - ty, - }); - debug!("Replacing `{:?}` with {:?}", rvalue, value); - let constant = Constant { - ty, - literal: Literal::Value { value }, - span, - }; - *rvalue = Rvalue::Use(Operand::Constant(box constant)); - } - self.super_rvalue(rvalue, location) } - - fn visit_constant( - &mut self, - constant: &mut Constant<'tcx>, - location: Location, - ) { - self.super_constant(constant, location); - if let Some(&(val, ty, _)) = self.optimizations.constants.get(constant) { - debug!("Replacing `{:?}` with {:?}:{:?}", constant.literal, val, ty); - constant.literal = Literal::Value { - value: self.tcx.mk_const(ty::Const { - val: ConstVal::Value(val), - ty, - }), - }; - } - } - - fn visit_operand( - &mut self, - operand: &mut Operand<'tcx>, - location: Location, - ) { - self.super_operand(operand, location); - let new = match operand { - Operand::Move(Place::Local(local)) | - Operand::Copy(Place::Local(local)) => { - trace!("trying to read {:?}", local); - self.optimizations.places.get(&local).cloned() - }, - _ => return, - }; - if let Some((value, ty, span)) = new { - let value = self.tcx.mk_const(ty::Const { - val: ConstVal::Value(value), - ty, - }); - debug!("Replacing `{:?}` with {:?}", operand, value); - let constant = Constant { - ty, - literal: Literal::Value { value }, - span, - }; - *operand = Operand::Constant(box constant); - } - } - - fn visit_terminator_kind( - &mut self, - block: BasicBlock, - kind: &mut TerminatorKind<'tcx>, - location: Location, - ) { - match kind { - TerminatorKind::SwitchInt { discr: value, .. } | - TerminatorKind::Yield { value, .. } | - TerminatorKind::Assert { cond: value, .. } => { - if let Some((new, ty, span)) = self.optimizations.terminators.remove(&block) { - let new = self.tcx.mk_const(ty::Const { - val: ConstVal::Value(new), - ty, - }); - debug!("Replacing `{:?}` with {:?}", value, new); - let constant = Constant { - ty, - literal: Literal::Value { value: new }, - span, - }; - *value = Operand::Constant(box constant); - } - } - // FIXME: do this optimization for function calls - _ => {}, - } - self.super_terminator_kind(block, kind, location) - } } /// Finds optimization opportunities on the MIR. struct OptimizationFinder<'b, 'a, 'tcx:'a+'b> { mir: &'b Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>, - source: MirSource, optimizations: OptimizationList<'tcx>, } @@ -181,325 +80,16 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { fn new( mir: &'b Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>, - source: MirSource, ) -> OptimizationFinder<'b, 'a, 'tcx> { OptimizationFinder { mir, tcx, - source, optimizations: OptimizationList::default(), } } - - fn eval_constant(&mut self, c: &Constant<'tcx>) -> Option> { - if let Some(&val) = self.optimizations.constants.get(c) { - return Some(val); - } - match c.literal { - Literal::Value { value } => match value.val { - ConstVal::Value(v) => Some((v, value.ty, c.span)), - ConstVal::Unevaluated(did, substs) => { - let param_env = self.tcx.param_env(self.source.def_id); - let instance = Instance::resolve( - self.tcx, - param_env, - did, - substs, - )?; - let cid = GlobalId { - instance, - promoted: None, - }; - let (value, _, ty) = eval_body(self.tcx, cid, param_env)?; - let val = (value, ty, c.span); - trace!("evaluated {:?} to {:?}", c, val); - self.optimizations.constants.insert(c.clone(), val); - Some(val) - }, - }, - // evaluate the promoted and replace the constant with the evaluated result - Literal::Promoted { index } => { - let generics = self.tcx.generics_of(self.source.def_id); - if generics.parent_types as usize + generics.types.len() > 0 { - // FIXME: can't handle code with generics - return None; - } - let substs = Substs::identity_for_item(self.tcx, self.source.def_id); - let instance = Instance::new(self.source.def_id, substs); - let cid = GlobalId { - instance, - promoted: Some(index), - }; - let param_env = self.tcx.param_env(self.source.def_id); - let (value, _, ty) = eval_body_with_mir(self.tcx, cid, self.mir, param_env)?; - let val = (value, ty, c.span); - trace!("evaluated {:?} to {:?}", c, val); - self.optimizations.constants.insert(c.clone(), val); - Some(val) - } - } - } - - fn eval_operand(&mut self, op: &Operand<'tcx>) -> Option> { - match *op { - Operand::Constant(ref c) => self.eval_constant(c), - Operand::Move(ref place) | Operand::Copy(ref place) => match *place { - Place::Local(loc) => self.optimizations.places.get(&loc).cloned(), - // FIXME(oli-obk): field and index projections - Place::Projection(_) => None, - _ => None, - }, - } - } - - fn simplify_operand(&mut self, op: &Operand<'tcx>) -> Option> { - match *op { - Operand::Constant(ref c) => match c.literal { - Literal::Value { .. } => None, - _ => self.eval_operand(op), - }, - _ => self.eval_operand(op), - } - } - - fn const_prop( - &mut self, - rvalue: &Rvalue<'tcx>, - place_ty: ty::Ty<'tcx>, - span: Span, - ) -> Option> { - match *rvalue { - // No need to overwrite an already evaluated constant - Rvalue::Use(Operand::Constant(box Constant { - literal: Literal::Value { - value: &ty::Const { - val: ConstVal::Value(_), - .. - }, - }, - .. - })) => None, - // This branch exists for the sanity type check - Rvalue::Use(Operand::Constant(ref c)) => { - assert_eq!(c.ty, place_ty); - self.eval_constant(c) - }, - Rvalue::Use(ref op) => { - self.eval_operand(op) - }, - Rvalue::Repeat(..) | - Rvalue::Ref(..) | - Rvalue::Cast(..) | - Rvalue::Aggregate(..) | - Rvalue::NullaryOp(NullOp::Box, _) | - Rvalue::Discriminant(..) => None, - // FIXME(oli-obk): evaluate static/constant slice lengths - Rvalue::Len(_) => None, - Rvalue::NullaryOp(NullOp::SizeOf, ty) => { - let param_env = self.tcx.param_env(self.source.def_id); - type_size_of(self.tcx, param_env, ty).map(|n| ( - Value::ByVal(PrimVal::Bytes(n as u128)), - self.tcx.types.usize, - span, - )) - } - Rvalue::UnaryOp(op, ref arg) => { - let def_id = if self.tcx.is_closure(self.source.def_id) { - self.tcx.closure_base_def_id(self.source.def_id) - } else { - self.source.def_id - }; - let generics = self.tcx.generics_of(def_id); - if generics.parent_types as usize + generics.types.len() > 0 { - // FIXME: can't handle code with generics - return None; - } - let substs = Substs::identity_for_item(self.tcx, self.source.def_id); - let instance = Instance::new(self.source.def_id, substs); - let ecx = mk_borrowck_eval_cx(self.tcx, instance, self.mir, span).unwrap(); - - let val = self.eval_operand(arg)?; - let prim = ecx.value_to_primval(ValTy { value: val.0, ty: val.1 }).ok()?; - let kind = ecx.ty_to_primval_kind(val.1).ok()?; - match unary_op(op, prim, kind) { - Ok(val) => Some((Value::ByVal(val), place_ty, span)), - Err(mut err) => { - ecx.report(&mut err, false, Some(span)); - None - }, - } - } - Rvalue::CheckedBinaryOp(op, ref left, ref right) | - Rvalue::BinaryOp(op, ref left, ref right) => { - trace!("rvalue binop {:?} for {:?} and {:?}", op, left, right); - let left = self.eval_operand(left)?; - let right = self.eval_operand(right)?; - let def_id = if self.tcx.is_closure(self.source.def_id) { - self.tcx.closure_base_def_id(self.source.def_id) - } else { - self.source.def_id - }; - let generics = self.tcx.generics_of(def_id); - let has_generics = generics.parent_types as usize + generics.types.len() > 0; - if has_generics { - // FIXME: can't handle code with generics - return None; - } - let substs = Substs::identity_for_item(self.tcx, self.source.def_id); - let instance = Instance::new(self.source.def_id, substs); - let ecx = mk_borrowck_eval_cx(self.tcx, instance, self.mir, span).unwrap(); - - let l = ecx.value_to_primval(ValTy { value: left.0, ty: left.1 }).ok()?; - let r = ecx.value_to_primval(ValTy { value: right.0, ty: right.1 }).ok()?; - trace!("const evaluating {:?} for {:?} and {:?}", op, left, right); - match ecx.binary_op(op, l, left.1, r, right.1) { - Ok((val, overflow)) => { - let val = if let Rvalue::CheckedBinaryOp(..) = *rvalue { - Value::ByValPair( - val, - PrimVal::from_bool(overflow), - ) - } else { - if overflow { - use rustc::mir::interpret::EvalErrorKind; - let mut err = EvalErrorKind::OverflowingMath.into(); - ecx.report(&mut err, false, Some(span)); - return None; - } - Value::ByVal(val) - }; - Some((val, place_ty, span)) - }, - Err(mut err) => { - ecx.report(&mut err, false, Some(span)); - None - }, - } - }, - } - } -} - -fn type_size_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - ty: ty::Ty<'tcx>) -> Option { - use rustc::ty::layout::LayoutOf; - (tcx, param_env).layout_of(ty).ok().map(|layout| layout.size.bytes()) -} - -struct ConstPropVisitor { - local: Local, - can_const_prop: bool, - // false at the beginning, once set, there are not allowed to be any more assignments - found_assignment: bool, -} - -impl ConstPropVisitor { - /// returns true if `local` can be propagated - fn check<'tcx>(local: Local, mir: &Mir<'tcx>) -> bool { - let mut cpv = ConstPropVisitor { - local, - can_const_prop: true, - found_assignment: false, - }; - cpv.visit_mir(mir); - cpv.can_const_prop - } - - fn is_our_local(&mut self, mut place: &Place) -> bool { - while let Place::Projection(ref proj) = place { - place = &proj.base; - } - if let Place::Local(local) = *place { - local == self.local - } else { - false - } - } -} - -impl<'tcx> Visitor<'tcx> for ConstPropVisitor { - fn visit_statement( - &mut self, - block: BasicBlock, - statement: &Statement<'tcx>, - location: Location, - ) { - self.super_statement(block, statement, location); - match statement.kind { - StatementKind::SetDiscriminant { ref place, .. } | - StatementKind::Assign(ref place, _) => { - if self.is_our_local(place) { - if self.found_assignment { - self.can_const_prop = false; - } else { - self.found_assignment = true - } - } - }, - StatementKind::InlineAsm { ref outputs, .. } => { - for place in outputs { - if self.is_our_local(place) { - if self.found_assignment { - self.can_const_prop = false; - } else { - self.found_assignment = true - } - return; - } - } - } - _ => {} - } - } - fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { - self.super_rvalue(rvalue, location); - if let Rvalue::Ref(_, _, ref place) = *rvalue { - if self.is_our_local(place) { - self.can_const_prop = false; - } - } - } } impl<'b, 'a, 'tcx> Visitor<'tcx> for OptimizationFinder<'b, 'a, 'tcx> { - fn visit_constant( - &mut self, - constant: &Constant<'tcx>, - location: Location, - ) { - trace!("visit_constant: {:?}", constant); - self.super_constant(constant, location); - self.eval_constant(constant); - } - - fn visit_statement( - &mut self, - block: BasicBlock, - statement: &Statement<'tcx>, - location: Location, - ) { - trace!("visit_statement: {:?}", statement); - if let StatementKind::Assign(ref place, ref rval) = statement.kind { - let place_ty = place - .ty(&self.mir.local_decls, self.tcx) - .to_ty(self.tcx); - let span = statement.source_info.span; - if let Some(value) = self.const_prop(rval, place_ty, span) { - self.optimizations.const_prop.insert(location, value); - if let Place::Local(local) = *place { - if self.mir.local_kind(local) == LocalKind::Temp - && ConstPropVisitor::check(local, self.mir) { - trace!("storing {:?} to {:?}", value, local); - assert!(self.optimizations.places.insert(local, value).is_none()); - } - } - return; - } - } - self.super_statement(block, statement, location); - } - fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { if let Rvalue::Ref(_, _, Place::Projection(ref projection)) = *rvalue { if let ProjectionElem::Deref = projection.elem { @@ -522,34 +112,10 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for OptimizationFinder<'b, 'a, 'tcx> { self.super_rvalue(rvalue, location) } - - fn visit_terminator_kind( - &mut self, - block: BasicBlock, - kind: &TerminatorKind<'tcx>, - _location: Location, - ) { - match kind { - TerminatorKind::SwitchInt { discr: value, .. } | - TerminatorKind::Yield { value, .. } | - TerminatorKind::Assert { cond: value, .. } => { - if let Some(value) = self.simplify_operand(value) { - self.optimizations.terminators.insert(block, value); - } - } - // FIXME: do this optimization for function calls - _ => {}, - } - } } #[derive(Default)] struct OptimizationList<'tcx> { and_stars: FxHashSet, arrays_lengths: FxHashMap>, - const_prop: FxHashMap>, - /// Terminators that get their Operand(s) turned into constants. - terminators: FxHashMap>, - places: FxHashMap>, - constants: FxHashMap, Const<'tcx>>, } diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 652069ab7c17e..81b740c917b5e 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -41,6 +41,7 @@ pub mod dump_mir; pub mod deaggregator; pub mod instcombine; pub mod copy_prop; +pub mod const_prop; pub mod generator; pub mod inline; pub mod lower_128bit; @@ -265,6 +266,7 @@ fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx generator::StateTransform, instcombine::InstCombine, + const_prop::ConstProp, simplify_branches::SimplifyBranches::new("after-const-prop"), deaggregator::Deaggregator, copy_prop::CopyPropagation, From 00c95b29bce5a6aa0acb10f78418760d5249394a Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 29 Jan 2018 08:50:47 +0100 Subject: [PATCH 050/110] Revert all changes to the instcombine pass --- src/librustc/ty/error.rs | 2 +- src/librustc_mir/transform/const_prop.rs | 83 +++++++++++++++++++---- src/librustc_mir/transform/instcombine.rs | 15 ++-- 3 files changed, 77 insertions(+), 23 deletions(-) diff --git a/src/librustc/ty/error.rs b/src/librustc/ty/error.rs index b50b9cb43d3a1..dcb70a8f86a8a 100644 --- a/src/librustc/ty/error.rs +++ b/src/librustc/ty/error.rs @@ -184,7 +184,7 @@ impl<'a, 'gcx, 'lcx, 'tcx> ty::TyS<'tcx> { ty::TyArray(_, n) => { match n.val.to_raw_bits() { Some(n) => format!("array of {} elements", n), - _ => "array".to_string(), + None => "array".to_string(), } } ty::TySlice(_) => "slice".to_string(), diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 652b921e80a74..8cbb2afae397a 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -44,8 +44,11 @@ impl MirPass for ConstProp { optimization_finder.optimizations }; - // Then carry out those optimizations. - MutVisitor::visit_mir(&mut ConstPropVisitor { optimizations, tcx }, mir); + // We only actually run when optimizing MIR (at any level). + if tcx.sess.opts.debugging_opts.mir_opt_level != 0 { + // Then carry out those optimizations. + MutVisitor::visit_mir(&mut ConstPropVisitor { optimizations, tcx }, mir); + } trace!("ConstProp done for {:?}", source.def_id); } } @@ -236,16 +239,6 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { } } - fn simplify_operand(&mut self, op: &Operand<'tcx>) -> Option> { - match *op { - Operand::Constant(ref c) => match c.literal { - Literal::Value { .. } => None, - _ => self.eval_operand(op), - }, - _ => self.eval_operand(op), - } - } - fn const_prop( &mut self, rvalue: &Rvalue<'tcx>, @@ -487,14 +480,76 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for OptimizationFinder<'b, 'a, 'tcx> { &mut self, block: BasicBlock, kind: &TerminatorKind<'tcx>, - _location: Location, + location: Location, ) { match kind { TerminatorKind::SwitchInt { discr: value, .. } | TerminatorKind::Yield { value, .. } | TerminatorKind::Assert { cond: value, .. } => { - if let Some(value) = self.simplify_operand(value) { + match value { + Operand::Constant(box Constant { + literal: Literal::Value { + value: &ty::Const { + val: ConstVal::Value(_), + .. + }, + }, + .. + }) => return, + _ => {}, + } + if let Some(value) = self.eval_operand(value) { self.optimizations.terminators.insert(block, value); + if let TerminatorKind::Assert { expected, msg, .. } = kind { + if Value::ByVal(PrimVal::from_bool(*expected)) != value.0 { + let span = self.mir[block] + .statements[location.statement_index] + .source_info + .span; + let node_id = self + .tcx + .hir + .as_local_node_id(self.source.def_id) + .expect("some part of a failing const eval must be local"); + let mut lint = self.tcx.struct_span_lint_node( + ::rustc::lint::builtin::CONST_ERR, + node_id, + span, + "constant evaluation error", + ); + use rustc::mir::AssertMessage::*; + match msg { + GeneratorResumedAfterReturn => + lint.span_label(span, "generator resumed after completion"), + GeneratorResumedAfterPanic => + lint.span_label(span, "generator resumed after panicking"), + Math(ref err) => lint.span_label(span, err.description()), + BoundsCheck { ref len, ref index } => { + let len = self.eval_operand(len).expect("len must be const"); + let len = match len.0 { + Value::ByVal(PrimVal::Bytes(n)) => n, + _ => bug!("const len not primitive: {:?}", len), + }; + let index = self + .eval_operand(index) + .expect("index must be const"); + let index = match index.0 { + Value::ByVal(PrimVal::Bytes(n)) => n, + _ => bug!("const index not primitive: {:?}", index), + }; + lint.span_label( + span, + format!( + "index out of bounds: \ + the len is {} but the index is {}", + len, + index, + ), + ) + }, + }.emit(); + } + } } } // FIXME: do this optimization for function calls diff --git a/src/librustc_mir/transform/instcombine.rs b/src/librustc_mir/transform/instcombine.rs index d1c2c7a61d495..8856d263864cd 100644 --- a/src/librustc_mir/transform/instcombine.rs +++ b/src/librustc_mir/transform/instcombine.rs @@ -23,9 +23,12 @@ pub struct InstCombine; impl MirPass for InstCombine { fn run_pass<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, - source: MirSource, + _: MirSource, mir: &mut Mir<'tcx>) { - trace!("InstCombine starting for {:?}", source.def_id); + // We only run when optimizing MIR (at any level). + if tcx.sess.opts.debugging_opts.mir_opt_level == 0 { + return + } // First, find optimization opportunities. This is done in a pre-pass to keep the MIR // read-only so that we can do global analyses on the MIR in the process (e.g. @@ -38,7 +41,6 @@ impl MirPass for InstCombine { // Then carry out those optimizations. MutVisitor::visit_mir(&mut InstCombineVisitor { optimizations }, mir); - trace!("InstCombine done for {:?}", source.def_id); } } @@ -61,7 +63,7 @@ impl<'tcx> MutVisitor<'tcx> for InstCombineVisitor<'tcx> { } if let Some(constant) = self.optimizations.arrays_lengths.remove(&location) { - debug!("Replacing `Len([_; N])`: {:?} with {:?}", rvalue, constant); + debug!("Replacing `Len([_; N])`: {:?}", rvalue); *rvalue = Rvalue::Use(Operand::Constant(box constant)); } @@ -77,10 +79,7 @@ struct OptimizationFinder<'b, 'a, 'tcx:'a+'b> { } impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { - fn new( - mir: &'b Mir<'tcx>, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - ) -> OptimizationFinder<'b, 'a, 'tcx> { + fn new(mir: &'b Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> OptimizationFinder<'b, 'a, 'tcx> { OptimizationFinder { mir, tcx, From e549f0b3d61c5bd56a469325e5324a829e5bd2d2 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 29 Jan 2018 09:58:28 +0100 Subject: [PATCH 051/110] Simplify const SIMD shuffle in trans --- src/librustc_trans/mir/constant.rs | 91 ++++++++++-------------------- 1 file changed, 30 insertions(+), 61 deletions(-) diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index c7a0724c1e72b..6aa8b7e5449fd 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -191,70 +191,39 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { bx: &Builder<'a, 'tcx>, constant: &mir::Constant<'tcx>, ) -> (ValueRef, Ty<'tcx>) { - let layout = bx.cx.layout_of(constant.ty); self.mir_constant_to_miri_value(bx, constant) .and_then(|c| { - let llval = match c { - MiriValue::ByVal(val) => { - let scalar = match layout.abi { - layout::Abi::Scalar(ref x) => x, - _ => bug!("from_const: invalid ByVal layout: {:#?}", layout) - }; - primval_to_llvm(bx.cx, val, scalar, layout.immediate_llvm_type(bx.cx)) - }, - MiriValue::ByValPair(a_val, b_val) => { - let (a_scalar, b_scalar) = match layout.abi { - layout::Abi::ScalarPair(ref a, ref b) => (a, b), - _ => bug!("from_const: invalid ByValPair layout: {:#?}", layout) - }; - let a_llval = primval_to_llvm( - bx.cx, - a_val, - a_scalar, - layout.scalar_pair_element_llvm_type(bx.cx, 0), - ); - let b_llval = primval_to_llvm( - bx.cx, - b_val, - b_scalar, - layout.scalar_pair_element_llvm_type(bx.cx, 1), - ); - C_struct(bx.cx, &[a_llval, b_llval], false) - }, - MiriValue::ByRef(..) => { - let field_ty = constant.ty.builtin_index().unwrap(); - let fields = match constant.ty.sty { - ty::TyArray(_, n) => n.val.unwrap_u64(), - ref other => bug!("invalid simd shuffle type: {}", other), - }; - let values: Result, _> = (0..fields).map(|field| { - let field = const_val_field( - bx.tcx(), - ty::ParamEnv::empty(traits::Reveal::All), - self.instance, - None, - mir::Field::new(field as usize), - c, - constant.ty, - )?; - match field.val { - ConstVal::Value(MiriValue::ByVal(prim)) => { - let layout = bx.cx.layout_of(field_ty); - let scalar = match layout.abi { - layout::Abi::Scalar(ref x) => x, - _ => bug!("from_const: invalid ByVal layout: {:#?}", layout) - }; - Ok(primval_to_llvm( - bx.cx, prim, scalar, - layout.immediate_llvm_type(bx.cx), - )) - }, - other => bug!("simd shuffle field {:?}, {}", other, constant.ty), - } - }).collect(); - C_struct(bx.cx, &values?, false) - }, + let field_ty = constant.ty.builtin_index().unwrap(); + let fields = match constant.ty.sty { + ty::TyArray(_, n) => n.val.unwrap_u64(), + ref other => bug!("invalid simd shuffle type: {}", other), }; + let values: Result, _> = (0..fields).map(|field| { + let field = const_val_field( + bx.tcx(), + ty::ParamEnv::empty(traits::Reveal::All), + self.instance, + None, + mir::Field::new(field as usize), + c, + constant.ty, + )?; + match field.val { + ConstVal::Value(MiriValue::ByVal(prim)) => { + let layout = bx.cx.layout_of(field_ty); + let scalar = match layout.abi { + layout::Abi::Scalar(ref x) => x, + _ => bug!("from_const: invalid ByVal layout: {:#?}", layout) + }; + Ok(primval_to_llvm( + bx.cx, prim, scalar, + layout.immediate_llvm_type(bx.cx), + )) + }, + other => bug!("simd shuffle field {:?}, {}", other, constant.ty), + } + }).collect(); + let llval = C_struct(bx.cx, &values?, false); Ok((llval, constant.ty)) }) .unwrap_or_else(|e| { From 21554b96aff27d2d4ac2742bc4b00b52982933ca Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 29 Jan 2018 10:18:49 +0100 Subject: [PATCH 052/110] Fix ICE in const prop --- src/librustc_mir/transform/const_prop.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 8cbb2afae397a..3b9b758baf214 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -480,7 +480,7 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for OptimizationFinder<'b, 'a, 'tcx> { &mut self, block: BasicBlock, kind: &TerminatorKind<'tcx>, - location: Location, + _location: Location, ) { match kind { TerminatorKind::SwitchInt { discr: value, .. } | @@ -503,7 +503,9 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for OptimizationFinder<'b, 'a, 'tcx> { if let TerminatorKind::Assert { expected, msg, .. } = kind { if Value::ByVal(PrimVal::from_bool(*expected)) != value.0 { let span = self.mir[block] - .statements[location.statement_index] + .terminator + .as_ref() + .unwrap() .source_info .span; let node_id = self From d57a109203526c2aa1c4bf88984726a82dd4bec8 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 29 Jan 2018 10:29:10 +0100 Subject: [PATCH 053/110] Adjust tests to more aggressive const err linting --- src/test/compile-fail/const-err-early.rs | 1 + src/test/compile-fail/const-err-multi.rs | 1 + src/test/compile-fail/eval-enum.rs | 2 ++ src/test/compile-fail/issue-8460-const.rs | 20 ++++++++++++++++++++ src/test/ui/error-codes/E0080.rs | 2 ++ 5 files changed, 26 insertions(+) diff --git a/src/test/compile-fail/const-err-early.rs b/src/test/compile-fail/const-err-early.rs index 8c2e6233abfe2..2e19de0a27960 100644 --- a/src/test/compile-fail/const-err-early.rs +++ b/src/test/compile-fail/const-err-early.rs @@ -14,6 +14,7 @@ pub const A: i8 = -std::i8::MIN; //~ ERROR E0080 //~^ ERROR attempt to negate with overflow //~| ERROR const_err +//~| ERROR const_err pub const B: u8 = 200u8 + 200u8; //~ ERROR E0080 pub const C: u8 = 200u8 * 4; //~ ERROR E0080 pub const D: u8 = 42u8 - (42u8 + 1); //~ ERROR E0080 diff --git a/src/test/compile-fail/const-err-multi.rs b/src/test/compile-fail/const-err-multi.rs index 668f95f2c8d73..5fa31e05322b3 100644 --- a/src/test/compile-fail/const-err-multi.rs +++ b/src/test/compile-fail/const-err-multi.rs @@ -13,6 +13,7 @@ pub const A: i8 = -std::i8::MIN; //~ ERROR attempt to negate with overflow //~^ ERROR E0080 //~| ERROR const_err +//~| ERROR const_err pub const B: i8 = A; //~^ ERROR E0080 pub const C: u8 = A as u8; diff --git a/src/test/compile-fail/eval-enum.rs b/src/test/compile-fail/eval-enum.rs index 49f76c532df54..469618407d8f9 100644 --- a/src/test/compile-fail/eval-enum.rs +++ b/src/test/compile-fail/eval-enum.rs @@ -13,10 +13,12 @@ enum Test { //~^ attempt to divide by zero //~| ERROR constant evaluation error //~| WARN constant evaluation error + //~| WARN constant evaluation error RemZero = 1%0, //~^ attempt to calculate the remainder with a divisor of zero //~| ERROR constant evaluation error //~| WARN constant evaluation error + //~| WARN constant evaluation error } fn main() {} diff --git a/src/test/compile-fail/issue-8460-const.rs b/src/test/compile-fail/issue-8460-const.rs index 1d59e75a0f0f0..e2cf88d5774f7 100644 --- a/src/test/compile-fail/issue-8460-const.rs +++ b/src/test/compile-fail/issue-8460-const.rs @@ -17,61 +17,81 @@ fn main() { assert!(thread::spawn(move|| { isize::MIN / -1; }).join().is_err()); //~^ ERROR attempt to divide with overflow //~| ERROR constant evaluation error + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i8::MIN / -1; }).join().is_err()); //~^ ERROR attempt to divide with overflow //~| ERROR constant evaluation error + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i16::MIN / -1; }).join().is_err()); //~^ ERROR attempt to divide with overflow //~| ERROR constant evaluation error + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i32::MIN / -1; }).join().is_err()); //~^ ERROR attempt to divide with overflow //~| ERROR constant evaluation error + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i64::MIN / -1; }).join().is_err()); //~^ ERROR attempt to divide with overflow //~| ERROR constant evaluation error + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1isize / 0; }).join().is_err()); //~^ ERROR attempt to divide by zero //~| ERROR constant evaluation error + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i8 / 0; }).join().is_err()); //~^ ERROR attempt to divide by zero //~| ERROR constant evaluation error + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i16 / 0; }).join().is_err()); //~^ ERROR attempt to divide by zero //~| ERROR constant evaluation error + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i32 / 0; }).join().is_err()); //~^ ERROR attempt to divide by zero //~| ERROR constant evaluation error + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i64 / 0; }).join().is_err()); //~^ ERROR attempt to divide by zero //~| ERROR constant evaluation error + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with overflow //~| ERROR constant evaluation error + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with overflow //~| ERROR constant evaluation error + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with overflow //~| ERROR constant evaluation error + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with overflow //~| ERROR constant evaluation error + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with overflow //~| ERROR constant evaluation error + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1isize % 0; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with a divisor of zero //~| ERROR constant evaluation error + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i8 % 0; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with a divisor of zero //~| ERROR constant evaluation error + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i16 % 0; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with a divisor of zero //~| ERROR constant evaluation error + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i32 % 0; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with a divisor of zero //~| ERROR constant evaluation error + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i64 % 0; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with a divisor of zero //~| ERROR constant evaluation error + //~| ERROR constant evaluation error } diff --git a/src/test/ui/error-codes/E0080.rs b/src/test/ui/error-codes/E0080.rs index 6e525d4ea9ec7..c8e425711284e 100644 --- a/src/test/ui/error-codes/E0080.rs +++ b/src/test/ui/error-codes/E0080.rs @@ -13,6 +13,8 @@ enum Enum { //~| shift left with overflow Y = (1 / 0) //~ ERROR E0080 //~| const_err + //~| const_err + //~| const_err //~| divide by zero } From ec857e1e099c135f48c8706cdea65497309de2b3 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 29 Jan 2018 10:32:11 +0100 Subject: [PATCH 054/110] Deduplicate code in rustdoc --- src/librustdoc/clean/mod.rs | 62 ++++++++++++++----------------------- 1 file changed, 24 insertions(+), 38 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 2372153963a1c..5d4addce2c439 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2511,25 +2511,7 @@ impl Clean for hir::Ty { ty: cx.tcx.types.usize }) }); - let n = match n.val { - ConstVal::Unevaluated(def_id, _) => { - if let Some(node_id) = cx.tcx.hir.as_local_node_id(def_id) { - print_const_expr(cx, cx.tcx.hir.body_owned_by(node_id)) - } else { - inline::print_inlined_const(cx, def_id) - } - }, - ConstVal::Value(val) => { - let mut s = String::new(); - ::rustc::mir::print_miri_value(val, n.ty, &mut s).unwrap(); - // array lengths are obviously usize - if s.ends_with("usize") { - let n = s.len() - "usize".len(); - s.truncate(n); - } - s - }, - }; + let n = print_const(cx, n); Array(box ty.clean(cx), n) }, TyTup(ref tys) => Tuple(tys.clean(cx)), @@ -2656,25 +2638,7 @@ impl<'tcx> Clean for Ty<'tcx> { n = new_n; } }; - let n = match n.val { - ConstVal::Unevaluated(def_id, _) => { - if let Some(node_id) = cx.tcx.hir.as_local_node_id(def_id) { - print_const_expr(cx, cx.tcx.hir.body_owned_by(node_id)) - } else { - inline::print_inlined_const(cx, def_id) - } - }, - ConstVal::Value(val) => { - let mut s = String::new(); - ::rustc::mir::print_miri_value(val, n.ty, &mut s).unwrap(); - // array lengths are obviously usize - if s.ends_with("usize") { - let n = s.len() - "usize".len(); - s.truncate(n); - } - s - }, - }; + let n = print_const(cx, n); Array(box ty.clean(cx), n) } ty::TyRawPtr(mt) => RawPointer(mt.mutbl.clean(cx), box mt.ty.clean(cx)), @@ -3658,6 +3622,28 @@ fn name_from_pat(p: &hir::Pat) -> String { } } +fn print_const(cx: &DocContext, n: &ty::Const) -> String { + match n.val { + ConstVal::Unevaluated(def_id, _) => { + if let Some(node_id) = cx.tcx.hir.as_local_node_id(def_id) { + print_const_expr(cx, cx.tcx.hir.body_owned_by(node_id)) + } else { + inline::print_inlined_const(cx, def_id) + } + }, + ConstVal::Value(val) => { + let mut s = String::new(); + ::rustc::mir::print_miri_value(val, n.ty, &mut s).unwrap(); + // array lengths are obviously usize + if s.ends_with("usize") { + let n = s.len() - "usize".len(); + s.truncate(n); + } + s + }, + } +} + fn print_const_expr(cx: &DocContext, body: hir::BodyId) -> String { cx.tcx.hir.node_to_pretty_string(body.node_id) } From 4667346042b526300f4172518d80dfb2da98ee27 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 29 Jan 2018 10:38:05 +0100 Subject: [PATCH 055/110] Remove the fragile const propagator and just do linting --- src/librustc_mir/transform/const_prop.rs | 126 +---------------------- 1 file changed, 3 insertions(+), 123 deletions(-) diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 3b9b758baf214..3b52152946a97 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -16,7 +16,7 @@ use rustc::mir::{Constant, Literal, Location, Place, Mir, Operand, Rvalue, Local}; use rustc::mir::{NullOp, StatementKind, Statement, BasicBlock, LocalKind}; use rustc::mir::TerminatorKind; -use rustc::mir::visit::{MutVisitor, Visitor}; +use rustc::mir::visit::Visitor; use rustc::middle::const_val::ConstVal; use rustc::ty::{TyCtxt, self, Instance}; use rustc::mir::interpret::{Value, PrimVal, GlobalId}; @@ -38,124 +38,15 @@ impl MirPass for ConstProp { // First, find optimization opportunities. This is done in a pre-pass to keep the MIR // read-only so that we can do global analyses on the MIR in the process (e.g. // `Place::ty()`). - let optimizations = { - let mut optimization_finder = OptimizationFinder::new(mir, tcx, source); - optimization_finder.visit_mir(mir); - optimization_finder.optimizations - }; + let mut optimization_finder = OptimizationFinder::new(mir, tcx, source); + optimization_finder.visit_mir(mir); - // We only actually run when optimizing MIR (at any level). - if tcx.sess.opts.debugging_opts.mir_opt_level != 0 { - // Then carry out those optimizations. - MutVisitor::visit_mir(&mut ConstPropVisitor { optimizations, tcx }, mir); - } trace!("ConstProp done for {:?}", source.def_id); } } type Const<'tcx> = (Value, ty::Ty<'tcx>, Span); -pub struct ConstPropVisitor<'a, 'tcx: 'a> { - optimizations: OptimizationList<'tcx>, - tcx: TyCtxt<'a, 'tcx, 'tcx>, -} - -impl<'a, 'tcx> MutVisitor<'tcx> for ConstPropVisitor<'a, 'tcx> { - fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) { - if let Some((value, ty, span)) = self.optimizations.const_prop.remove(&location) { - let value = self.tcx.mk_const(ty::Const { - val: ConstVal::Value(value), - ty, - }); - debug!("Replacing `{:?}` with {:?}", rvalue, value); - let constant = Constant { - ty, - literal: Literal::Value { value }, - span, - }; - *rvalue = Rvalue::Use(Operand::Constant(box constant)); - } - - self.super_rvalue(rvalue, location) - } - - fn visit_constant( - &mut self, - constant: &mut Constant<'tcx>, - location: Location, - ) { - self.super_constant(constant, location); - if let Some(&(val, ty, _)) = self.optimizations.constants.get(constant) { - debug!("Replacing `{:?}` with {:?}:{:?}", constant.literal, val, ty); - constant.literal = Literal::Value { - value: self.tcx.mk_const(ty::Const { - val: ConstVal::Value(val), - ty, - }), - }; - } - } - - fn visit_operand( - &mut self, - operand: &mut Operand<'tcx>, - location: Location, - ) { - self.super_operand(operand, location); - let new = match operand { - Operand::Move(Place::Local(local)) | - Operand::Copy(Place::Local(local)) => { - trace!("trying to read {:?}", local); - self.optimizations.places.get(&local).cloned() - }, - _ => return, - }; - if let Some((value, ty, span)) = new { - let value = self.tcx.mk_const(ty::Const { - val: ConstVal::Value(value), - ty, - }); - debug!("Replacing `{:?}` with {:?}", operand, value); - let constant = Constant { - ty, - literal: Literal::Value { value }, - span, - }; - *operand = Operand::Constant(box constant); - } - } - - fn visit_terminator_kind( - &mut self, - block: BasicBlock, - kind: &mut TerminatorKind<'tcx>, - location: Location, - ) { - match kind { - TerminatorKind::SwitchInt { discr: value, .. } | - TerminatorKind::Yield { value, .. } | - TerminatorKind::Assert { cond: value, .. } => { - if let Some((new, ty, span)) = self.optimizations.terminators.remove(&block) { - let new = self.tcx.mk_const(ty::Const { - val: ConstVal::Value(new), - ty, - }); - debug!("Replacing `{:?}` with {:?}", value, new); - let constant = Constant { - ty, - literal: Literal::Value { value: new }, - span, - }; - *value = Operand::Constant(box constant); - } - } - // FIXME: do this optimization for function calls - _ => {}, - } - self.super_terminator_kind(block, kind, location) - } -} - /// Finds optimization opportunities on the MIR. struct OptimizationFinder<'b, 'a, 'tcx:'a+'b> { mir: &'b Mir<'tcx>, @@ -179,9 +70,6 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { } fn eval_constant(&mut self, c: &Constant<'tcx>) -> Option> { - if let Some(&val) = self.optimizations.constants.get(c) { - return Some(val); - } match c.literal { Literal::Value { value } => match value.val { ConstVal::Value(v) => Some((v, value.ty, c.span)), @@ -200,7 +88,6 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { let (value, _, ty) = eval_body(self.tcx, cid, param_env)?; let val = (value, ty, c.span); trace!("evaluated {:?} to {:?}", c, val); - self.optimizations.constants.insert(c.clone(), val); Some(val) }, }, @@ -221,7 +108,6 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { let (value, _, ty) = eval_body_with_mir(self.tcx, cid, self.mir, param_env)?; let val = (value, ty, c.span); trace!("evaluated {:?} to {:?}", c, val); - self.optimizations.constants.insert(c.clone(), val); Some(val) } } @@ -463,7 +349,6 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for OptimizationFinder<'b, 'a, 'tcx> { .to_ty(self.tcx); let span = statement.source_info.span; if let Some(value) = self.const_prop(rval, place_ty, span) { - self.optimizations.const_prop.insert(location, value); if let Place::Local(local) = *place { if self.mir.local_kind(local) == LocalKind::Temp && CanConstProp::check(local, self.mir) { @@ -499,7 +384,6 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for OptimizationFinder<'b, 'a, 'tcx> { _ => {}, } if let Some(value) = self.eval_operand(value) { - self.optimizations.terminators.insert(block, value); if let TerminatorKind::Assert { expected, msg, .. } = kind { if Value::ByVal(PrimVal::from_bool(*expected)) != value.0 { let span = self.mir[block] @@ -562,9 +446,5 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for OptimizationFinder<'b, 'a, 'tcx> { #[derive(Default)] struct OptimizationList<'tcx> { - const_prop: FxHashMap>, - /// Terminators that get their Operand(s) turned into constants. - terminators: FxHashMap>, places: FxHashMap>, - constants: FxHashMap, Const<'tcx>>, } From e093ab0df4c2c2113ff6bd9bdc83aadb1f6c1f6c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 29 Jan 2018 11:24:51 +0100 Subject: [PATCH 056/110] Adjust tests to changed const err lints --- src/test/compile-fail/const-err-early.rs | 1 - src/test/compile-fail/const-err-multi.rs | 2 +- src/test/compile-fail/issue-8460-const.rs | 61 +++++++++++++------ .../compile-fail/lint-exceeding-bitshifts2.rs | 10 +-- 4 files changed, 47 insertions(+), 27 deletions(-) diff --git a/src/test/compile-fail/const-err-early.rs b/src/test/compile-fail/const-err-early.rs index 2e19de0a27960..4a146d8de3cde 100644 --- a/src/test/compile-fail/const-err-early.rs +++ b/src/test/compile-fail/const-err-early.rs @@ -12,7 +12,6 @@ #![deny(const_err)] pub const A: i8 = -std::i8::MIN; //~ ERROR E0080 -//~^ ERROR attempt to negate with overflow //~| ERROR const_err //~| ERROR const_err pub const B: u8 = 200u8 + 200u8; //~ ERROR E0080 diff --git a/src/test/compile-fail/const-err-multi.rs b/src/test/compile-fail/const-err-multi.rs index 5fa31e05322b3..eb24a698419eb 100644 --- a/src/test/compile-fail/const-err-multi.rs +++ b/src/test/compile-fail/const-err-multi.rs @@ -10,7 +10,7 @@ #![deny(const_err)] -pub const A: i8 = -std::i8::MIN; //~ ERROR attempt to negate with overflow +pub const A: i8 = -std::i8::MIN; //~^ ERROR E0080 //~| ERROR const_err //~| ERROR const_err diff --git a/src/test/compile-fail/issue-8460-const.rs b/src/test/compile-fail/issue-8460-const.rs index e2cf88d5774f7..024aca2e66d36 100644 --- a/src/test/compile-fail/issue-8460-const.rs +++ b/src/test/compile-fail/issue-8460-const.rs @@ -9,89 +9,110 @@ // except according to those terms. #![deny(const_err)] +//~^ NOTE lint level defined here use std::{isize, i8, i16, i32, i64}; use std::thread; fn main() { assert!(thread::spawn(move|| { isize::MIN / -1; }).join().is_err()); - //~^ ERROR attempt to divide with overflow + //~^ NOTE attempt to divide with overflow + //~| NOTE attempted to do overflowing math //~| ERROR constant evaluation error //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i8::MIN / -1; }).join().is_err()); - //~^ ERROR attempt to divide with overflow + //~^ NOTE attempt to divide with overflow + //~| NOTE attempted to do overflowing math //~| ERROR constant evaluation error //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i16::MIN / -1; }).join().is_err()); - //~^ ERROR attempt to divide with overflow + //~^ NOTE attempt to divide with overflow + //~| NOTE attempted to do overflowing math //~| ERROR constant evaluation error //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i32::MIN / -1; }).join().is_err()); - //~^ ERROR attempt to divide with overflow + //~^ NOTE attempt to divide with overflow + //~| NOTE attempted to do overflowing math //~| ERROR constant evaluation error //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i64::MIN / -1; }).join().is_err()); - //~^ ERROR attempt to divide with overflow + //~^ NOTE attempt to divide with overflow + //~| NOTE attempted to do overflowing math //~| ERROR constant evaluation error //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1isize / 0; }).join().is_err()); - //~^ ERROR attempt to divide by zero + //~^ NOTE attempt to divide by zero + //~| NOTE attempted to do overflowing math //~| ERROR constant evaluation error //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i8 / 0; }).join().is_err()); - //~^ ERROR attempt to divide by zero + //~^ NOTE attempt to divide by zero + //~| NOTE attempted to do overflowing math //~| ERROR constant evaluation error //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i16 / 0; }).join().is_err()); - //~^ ERROR attempt to divide by zero + //~^ NOTE attempt to divide by zero + //~| NOTE attempted to do overflowing math //~| ERROR constant evaluation error //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i32 / 0; }).join().is_err()); - //~^ ERROR attempt to divide by zero + //~^ NOTE attempt to divide by zero + //~| NOTE attempted to do overflowing math //~| ERROR constant evaluation error //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i64 / 0; }).join().is_err()); - //~^ ERROR attempt to divide by zero + //~^ NOTE attempt to divide by zero + //~| NOTE attempted to do overflowing math //~| ERROR constant evaluation error //~| ERROR constant evaluation error assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err()); - //~^ ERROR attempt to calculate the remainder with overflow + //~^ NOTE attempt to calculate the remainder with overflow + //~| NOTE attempted to do overflowing math //~| ERROR constant evaluation error //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err()); - //~^ ERROR attempt to calculate the remainder with overflow + //~^ NOTE attempt to calculate the remainder with overflow + //~| NOTE attempted to do overflowing math //~| ERROR constant evaluation error //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err()); - //~^ ERROR attempt to calculate the remainder with overflow + //~^ NOTE attempt to calculate the remainder with overflow + //~| NOTE attempted to do overflowing math //~| ERROR constant evaluation error //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err()); - //~^ ERROR attempt to calculate the remainder with overflow + //~^ NOTE attempt to calculate the remainder with overflow + //~| NOTE attempted to do overflowing math //~| ERROR constant evaluation error //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err()); - //~^ ERROR attempt to calculate the remainder with overflow + //~^ NOTE attempt to calculate the remainder with overflow + //~| NOTE attempted to do overflowing math //~| ERROR constant evaluation error //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1isize % 0; }).join().is_err()); - //~^ ERROR attempt to calculate the remainder with a divisor of zero + //~^ NOTE attempt to calculate the remainder with a divisor of zero + //~| NOTE attempted to do overflowing math //~| ERROR constant evaluation error //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i8 % 0; }).join().is_err()); - //~^ ERROR attempt to calculate the remainder with a divisor of zero + //~^ NOTE attempt to calculate the remainder with a divisor of zero + //~| NOTE attempted to do overflowing math //~| ERROR constant evaluation error //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i16 % 0; }).join().is_err()); - //~^ ERROR attempt to calculate the remainder with a divisor of zero + //~^ NOTE attempt to calculate the remainder with a divisor of zero + //~| NOTE attempted to do overflowing math //~| ERROR constant evaluation error //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i32 % 0; }).join().is_err()); - //~^ ERROR attempt to calculate the remainder with a divisor of zero + //~^ NOTE attempt to calculate the remainder with a divisor of zero + //~| NOTE attempted to do overflowing math //~| ERROR constant evaluation error //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i64 % 0; }).join().is_err()); - //~^ ERROR attempt to calculate the remainder with a divisor of zero + //~^ NOTE attempt to calculate the remainder with a divisor of zero + //~| NOTE attempted to do overflowing math //~| ERROR constant evaluation error //~| ERROR constant evaluation error } diff --git a/src/test/compile-fail/lint-exceeding-bitshifts2.rs b/src/test/compile-fail/lint-exceeding-bitshifts2.rs index d95e1b370aafd..0eab143fa49e0 100644 --- a/src/test/compile-fail/lint-exceeding-bitshifts2.rs +++ b/src/test/compile-fail/lint-exceeding-bitshifts2.rs @@ -8,13 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![deny(exceeding_bitshifts)] -#![allow(unused_variables, const_err)] +#![deny(exceeding_bitshifts, const_err)] +#![allow(unused_variables)] #![allow(dead_code)] fn main() { let n = 1u8 << (4+3); - let n = 1u8 << (4+4); //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u8 << (4+4); //~ ERROR: const_err let n = 1i64 >> [63][0]; let n = 1i64 >> [64][0]; // should be linting, needs to wait for const propagation @@ -22,6 +22,6 @@ fn main() { const BITS: usize = 32; #[cfg(target_pointer_width = "64")] const BITS: usize = 64; - let n = 1_isize << BITS; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1_usize << BITS; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1_isize << BITS; //~ ERROR: const_err + let n = 1_usize << BITS; //~ ERROR: const_err } From c568807989774c8728ef10fad412418653f739c9 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 29 Jan 2018 11:38:47 +0100 Subject: [PATCH 057/110] Remove single field struct by just using the field --- src/librustc_mir/transform/const_prop.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 3b52152946a97..3b37e755af388 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -52,7 +52,7 @@ struct OptimizationFinder<'b, 'a, 'tcx:'a+'b> { mir: &'b Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>, source: MirSource, - optimizations: OptimizationList<'tcx>, + places: FxHashMap>, } impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { @@ -65,7 +65,7 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { mir, tcx, source, - optimizations: OptimizationList::default(), + places: FxHashMap::default(), } } @@ -117,7 +117,7 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { match *op { Operand::Constant(ref c) => self.eval_constant(c), Operand::Move(ref place) | Operand::Copy(ref place) => match *place { - Place::Local(loc) => self.optimizations.places.get(&loc).cloned(), + Place::Local(loc) => self.places.get(&loc).cloned(), // FIXME(oli-obk): field and index projections Place::Projection(_) => None, _ => None, @@ -353,7 +353,7 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for OptimizationFinder<'b, 'a, 'tcx> { if self.mir.local_kind(local) == LocalKind::Temp && CanConstProp::check(local, self.mir) { trace!("storing {:?} to {:?}", value, local); - assert!(self.optimizations.places.insert(local, value).is_none()); + assert!(self.places.insert(local, value).is_none()); } } } @@ -443,8 +443,3 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for OptimizationFinder<'b, 'a, 'tcx> { } } } - -#[derive(Default)] -struct OptimizationList<'tcx> { - places: FxHashMap>, -} From 0f72f0009a309059952feb973a8e066845988f2a Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 29 Jan 2018 15:02:01 +0100 Subject: [PATCH 058/110] Remove redundant warnings in rustc_trans --- src/librustc_trans/mir/block.rs | 31 ++++--------------------------- 1 file changed, 4 insertions(+), 27 deletions(-) diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index 9d70f8669b2c9..efb5338f680af 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -351,26 +351,18 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { .max(tcx.data_layout.pointer_align); // Put together the arguments to the panic entry point. - let (lang_item, args, const_err) = match *msg { + let (lang_item, args) = match *msg { mir::AssertMessage::BoundsCheck { ref len, ref index } => { let len = self.trans_operand(&mut bx, len).immediate(); let index = self.trans_operand(&mut bx, index).immediate(); - let const_err = common::const_to_opt_u128(len, false) - .and_then(|len| common::const_to_opt_u128(index, false) - .map(|index| format!( - "index out of bounds: the len is {} but the index is {}", - len, index, - ))); - let file_line_col = C_struct(bx.cx, &[filename, line, col], false); let file_line_col = consts::addr_of(bx.cx, file_line_col, align, "panic_bounds_check_loc"); (lang_items::PanicBoundsCheckFnLangItem, - vec![file_line_col, index, len], - const_err) + vec![file_line_col, index, len]) } mir::AssertMessage::Math(ref err) => { let msg_str = Symbol::intern(err.description()).as_str(); @@ -383,8 +375,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { align, "panic_loc"); (lang_items::PanicFnLangItem, - vec![msg_file_line_col], - Some(err.description().to_owned())) + vec![msg_file_line_col]) } mir::AssertMessage::GeneratorResumedAfterReturn | mir::AssertMessage::GeneratorResumedAfterPanic => { @@ -403,24 +394,10 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { align, "panic_loc"); (lang_items::PanicFnLangItem, - vec![msg_file_line_col], - None) + vec![msg_file_line_col]) } }; - // If we know we always panic, and the error message - // is also constant, then we can produce a warning. - if const_cond == Some(!expected) { - if let Some(err) = const_err { - let mut diag = bx.tcx().sess.struct_span_warn( - span, &format!( - "this expression will panic at run-time with {:?}", - err, - )); - diag.emit(); - } - } - // Obtain the panic entry point. let def_id = common::langcall(bx.tcx(), Some(span), "", lang_item); let instance = ty::Instance::mono(bx.tcx(), def_id); From 8c8a4335323c3c5fe917aff4ba4acb22804abf3e Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 29 Jan 2018 15:10:26 +0100 Subject: [PATCH 059/110] Merge const linting pass into const prop --- src/librustc_driver/driver.rs | 7 - src/librustc_mir/check_const_err.rs | 197 ----------------------- src/librustc_mir/lib.rs | 1 - src/librustc_mir/transform/const_prop.rs | 24 ++- 4 files changed, 23 insertions(+), 206 deletions(-) delete mode 100644 src/librustc_mir/check_const_err.rs diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index f232d039f6641..65649c82da6ff 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -1087,13 +1087,6 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(trans: &TransCrate, stability::check_unused_or_stable_features(tcx) }); - - time(time_passes, - "MIR linting", - || for def_id in tcx.body_owners() { - mir::check_const_err::check(tcx, def_id) - }); - time(time_passes, "lint checking", || lint::check_crate(tcx)); return Ok(f(tcx, analysis, rx, tcx.sess.compile_status())); diff --git a/src/librustc_mir/check_const_err.rs b/src/librustc_mir/check_const_err.rs deleted file mode 100644 index 9827dd58cd640..0000000000000 --- a/src/librustc_mir/check_const_err.rs +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , 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::hir; -use rustc::hir::map::Node; -use rustc::mir::visit::Visitor; -use rustc::mir::interpret::{Value, PrimVal, GlobalId}; -use rustc::middle::const_val::{ConstVal, ConstEvalErr, ErrKind}; -use rustc::hir::def::Def; -use rustc::traits; -use interpret::eval_body_with_mir; -use rustc::ty::{TyCtxt, ParamEnv}; -use rustc::ty::Instance; -use rustc::ty::layout::LayoutOf; -use rustc::hir::def_id::DefId; -use rustc::ty::subst::Substs; - -fn is_const<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool { - if let Some(node) = tcx.hir.get_if_local(def_id) { - match node { - Node::NodeItem(&hir::Item { - node: hir::ItemConst(..), .. - }) => true, - _ => false - } - } else { - match tcx.describe_def(def_id) { - Some(Def::Const(_)) => true, - _ => false - } - } -} - -pub fn check<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) { - let mir = &tcx.optimized_mir(def_id); - let substs = Substs::identity_for_item(tcx, def_id); - let instance = Instance::new(def_id, substs); - let param_env = tcx.param_env(def_id); - - if is_const(tcx, def_id) { - let cid = GlobalId { - instance, - promoted: None, - }; - eval_body_with_mir(tcx, cid, mir, param_env); - } - - ConstErrVisitor { - tcx, - mir, - }.visit_mir(mir); - let outer_def_id = if tcx.is_closure(def_id) { - tcx.closure_base_def_id(def_id) - } else { - def_id - }; - let generics = tcx.generics_of(outer_def_id); - // FIXME: miri should be able to eval stuff that doesn't need info - // from the generics - if generics.parent_types as usize + generics.types.len() > 0 { - return; - } - for i in 0.. mir.promoted.len() { - use rustc_data_structures::indexed_vec::Idx; - let cid = GlobalId { - instance, - promoted: Some(Promoted::new(i)), - }; - eval_body_with_mir(tcx, cid, mir, param_env); - } -} - -struct ConstErrVisitor<'a, 'tcx: 'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, - mir: &'a Mir<'tcx>, -} - -impl<'a, 'tcx> ConstErrVisitor<'a, 'tcx> { - fn eval_op(&self, op: &Operand<'tcx>) -> Option { - let op = match *op { - Operand::Constant(ref c) => c, - _ => return None, - }; - match op.literal { - Literal::Value { value } => match value.val { - ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))) => Some(b), - _ => return None, - }, - _ => None, - } - } -} - -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 { ref cond, expected, ref msg, .. } => { - let cond = match self.eval_op(cond) { - Some(val) => val, - None => return, - }; - if (cond == 1) == expected { - return; - } - assert!(cond <= 1); - // If we know we always panic, and the error message - // is also constant, then we can produce a warning. - - 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"); - } - } - _ => {} - } - } -} diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index f6b38dcc00150..c31e95fd826c6 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -71,7 +71,6 @@ pub mod transform; pub mod util; pub mod interpret; pub mod monomorphize; -pub mod check_const_err; pub use hair::pattern::check_crate as matchck_crate; use rustc::ty::maps::Providers; diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 3b37e755af388..35fce184bebe8 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -212,8 +212,30 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { let instance = Instance::new(self.source.def_id, substs); let ecx = mk_borrowck_eval_cx(self.tcx, instance, self.mir, span).unwrap(); - let l = ecx.value_to_primval(ValTy { value: left.0, ty: left.1 }).ok()?; let r = ecx.value_to_primval(ValTy { value: right.0, ty: right.1 }).ok()?; + let param_env = ParamEnv::empty(traits::Reveal::All); + let bits = (self.tcx, param_env).layout_of(left.ty).unwrap().size.bits(); + if r >= 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"); + } + let l = ecx.value_to_primval(ValTy { value: left.0, ty: left.1 }).ok()?; trace!("const evaluating {:?} for {:?} and {:?}", op, left, right); match ecx.binary_op(op, l, left.1, r, right.1) { Ok((val, overflow)) => { From 929a4cece569b7436bffd0ec76b672da7af42d1c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 29 Jan 2018 15:12:45 +0100 Subject: [PATCH 060/110] Use IndexVec instead of FxHashMap --- src/librustc_mir/transform/const_prop.rs | 43 +++++++++++------------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 35fce184bebe8..e1a1c47373015 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -15,16 +15,17 @@ use rustc::mir::{Constant, Literal, Location, Place, Mir, Operand, Rvalue, Local}; use rustc::mir::{NullOp, StatementKind, Statement, BasicBlock, LocalKind}; -use rustc::mir::TerminatorKind; +use rustc::mir::{TerminatorKind, ClearCrossCrate, SourceInfo}; use rustc::mir::visit::Visitor; +use rustc::ty::layout::LayoutOf; use rustc::middle::const_val::ConstVal; use rustc::ty::{TyCtxt, self, Instance}; use rustc::mir::interpret::{Value, PrimVal, GlobalId}; use interpret::{eval_body_with_mir, eval_body, mk_borrowck_eval_cx, unary_op, ValTy}; -use rustc::util::nodemap::FxHashMap; use transform::{MirPass, MirSource}; use syntax::codemap::Span; use rustc::ty::subst::Substs; +use rustc_data_structures::indexed_vec::IndexVec; pub struct ConstProp; @@ -35,9 +36,10 @@ impl MirPass for ConstProp { mir: &mut Mir<'tcx>) { trace!("ConstProp starting for {:?}", source.def_id); - // First, find optimization opportunities. This is done in a pre-pass to keep the MIR - // read-only so that we can do global analyses on the MIR in the process (e.g. - // `Place::ty()`). + // FIXME(oli-obk, eddyb) Optimize locals (or even local paths) to hold + // constants, instead of just checking for const-folding succeeding. + // That would require an uniform one-def no-mutation analysis + // and RPO (or recursing when needing the value of a local). let mut optimization_finder = OptimizationFinder::new(mir, tcx, source); optimization_finder.visit_mir(mir); @@ -52,7 +54,7 @@ struct OptimizationFinder<'b, 'a, 'tcx:'a+'b> { mir: &'b Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>, source: MirSource, - places: FxHashMap>, + places: IndexVec>>, } impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { @@ -65,7 +67,7 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { mir, tcx, source, - places: FxHashMap::default(), + places: IndexVec::from_elem(None, &mir.local_decls), } } @@ -117,7 +119,7 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { match *op { Operand::Constant(ref c) => self.eval_constant(c), Operand::Move(ref place) | Operand::Copy(ref place) => match *place { - Place::Local(loc) => self.places.get(&loc).cloned(), + Place::Local(loc) => self.places[loc].clone(), // FIXME(oli-obk): field and index projections Place::Projection(_) => None, _ => None, @@ -129,8 +131,9 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { &mut self, rvalue: &Rvalue<'tcx>, place_ty: ty::Ty<'tcx>, - span: Span, + source_info: SourceInfo, ) -> Option> { + let span = source_info.span; match *rvalue { // No need to overwrite an already evaluated constant Rvalue::Use(Operand::Constant(box Constant { @@ -213,20 +216,12 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { let ecx = mk_borrowck_eval_cx(self.tcx, instance, self.mir, span).unwrap(); let r = ecx.value_to_primval(ValTy { value: right.0, ty: right.1 }).ok()?; - let param_env = ParamEnv::empty(traits::Reveal::All); - let bits = (self.tcx, param_env).layout_of(left.ty).unwrap().size.bits(); - if r >= 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 param_env = self.tcx.param_env(self.source.def_id); + let bits = (self.tcx, param_env).layout_of(left.1).unwrap().size.bits(); + if r.to_bytes().ok()? >= bits as u128 { let scope_info = match self.mir.visibility_scope_info { ClearCrossCrate::Set(ref data) => data, - ClearCrossCrate::Clear => return, + ClearCrossCrate::Clear => return None, }; let node_id = scope_info[source_info.scope].lint_root; self.tcx.lint_node( @@ -369,13 +364,13 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for OptimizationFinder<'b, 'a, 'tcx> { let place_ty = place .ty(&self.mir.local_decls, self.tcx) .to_ty(self.tcx); - let span = statement.source_info.span; - if let Some(value) = self.const_prop(rval, place_ty, span) { + if let Some(value) = self.const_prop(rval, place_ty, statement.source_info) { if let Place::Local(local) = *place { if self.mir.local_kind(local) == LocalKind::Temp && CanConstProp::check(local, self.mir) { trace!("storing {:?} to {:?}", value, local); - assert!(self.places.insert(local, value).is_none()); + assert!(self.places[local].is_none()); + self.places[local] = Some(value); } } } From 438139f635302231f80ef627880f4ebd3fbb886e Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 29 Jan 2018 18:54:37 +0100 Subject: [PATCH 061/110] rustc_passes::consts -> rvalue_promotion --- src/librustc_driver/driver.rs | 4 ++-- src/librustc_passes/lib.rs | 4 ++-- src/librustc_passes/{consts.rs => rvalue_promotion.rs} | 0 3 files changed, 4 insertions(+), 4 deletions(-) rename src/librustc_passes/{consts.rs => rvalue_promotion.rs} (100%) diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 65649c82da6ff..06108a72869f1 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -1036,8 +1036,8 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(trans: &TransCrate, } time(time_passes, - "const checking", - || consts::check_crate(tcx)); + "rvalue promotion", + || rvalue_promotion::check_crate(tcx)); analysis.access_levels = time(time_passes, "privacy checking", || rustc_privacy::check_crate(tcx)); diff --git a/src/librustc_passes/lib.rs b/src/librustc_passes/lib.rs index a51bee9149951..1f6cc1f71fc08 100644 --- a/src/librustc_passes/lib.rs +++ b/src/librustc_passes/lib.rs @@ -39,7 +39,7 @@ use rustc::ty::maps::Providers; mod diagnostics; pub mod ast_validation; -pub mod consts; +pub mod rvalue_promotion; pub mod hir_stats; pub mod loops; mod mir_stats; @@ -47,5 +47,5 @@ mod mir_stats; __build_diagnostic_array! { librustc_passes, DIAGNOSTICS } pub fn provide(providers: &mut Providers) { - consts::provide(providers); + rvalue_promotion::provide(providers); } diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/rvalue_promotion.rs similarity index 100% rename from src/librustc_passes/consts.rs rename to src/librustc_passes/rvalue_promotion.rs From 13c0dc56dfa42e6674dd379e3a728b418bfdedcb Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 29 Jan 2018 19:06:44 +0100 Subject: [PATCH 062/110] Only report bitshift lints on bitshift ops --- src/librustc_mir/transform/const_prop.rs | 30 +++++++++++++----------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index e1a1c47373015..e0d8744f7c400 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -15,7 +15,7 @@ use rustc::mir::{Constant, Literal, Location, Place, Mir, Operand, Rvalue, Local}; use rustc::mir::{NullOp, StatementKind, Statement, BasicBlock, LocalKind}; -use rustc::mir::{TerminatorKind, ClearCrossCrate, SourceInfo}; +use rustc::mir::{TerminatorKind, ClearCrossCrate, SourceInfo, BinOp}; use rustc::mir::visit::Visitor; use rustc::ty::layout::LayoutOf; use rustc::middle::const_val::ConstVal; @@ -216,19 +216,21 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { let ecx = mk_borrowck_eval_cx(self.tcx, instance, self.mir, span).unwrap(); let r = ecx.value_to_primval(ValTy { value: right.0, ty: right.1 }).ok()?; - let param_env = self.tcx.param_env(self.source.def_id); - let bits = (self.tcx, param_env).layout_of(left.1).unwrap().size.bits(); - if r.to_bytes().ok()? >= bits as u128 { - let scope_info = match self.mir.visibility_scope_info { - ClearCrossCrate::Set(ref data) => data, - ClearCrossCrate::Clear => return None, - }; - 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"); + if op == BinOp::Shr || op == BinOp::Shl { + let param_env = self.tcx.param_env(self.source.def_id); + let bits = (self.tcx, param_env).layout_of(left.1).unwrap().size.bits(); + if r.to_bytes().ok()? >= bits as u128 { + let scope_info = match self.mir.visibility_scope_info { + ClearCrossCrate::Set(ref data) => data, + ClearCrossCrate::Clear => return None, + }; + 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"); + } } let l = ecx.value_to_primval(ValTy { value: left.0, ty: left.1 }).ok()?; trace!("const evaluating {:?} for {:?} and {:?}", op, left, right); From 600fcc71594cb74b1d3802379b5b352bcc31274c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 29 Jan 2018 19:53:46 +0100 Subject: [PATCH 063/110] Report const eval errors at the correct span --- src/librustc_mir/hair/pattern/mod.rs | 26 ++++++++++++++++------ src/librustc_mir/interpret/const_eval.rs | 6 +++-- src/librustc_mir/interpret/eval_context.rs | 4 +++- src/librustc_trans/mir/constant.rs | 1 + 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs index 1141425c554a9..3dba00d1fb1b2 100644 --- a/src/librustc_mir/hair/pattern/mod.rs +++ b/src/librustc_mir/hair/pattern/mod.rs @@ -38,6 +38,7 @@ use syntax_pos::Span; #[derive(Clone, Debug)] pub enum PatternError { + AssociatedConstInPattern(Span), StaticInPattern(Span), FloatBug, NonConstPath(Span), @@ -150,7 +151,7 @@ impl<'tcx> fmt::Display for Pattern<'tcx> { BindingMode::ByValue => mutability == Mutability::Mut, BindingMode::ByRef(_, bk) => { write!(f, "ref ")?; - bk == BorrowKind::Mut + match bk { BorrowKind::Mut { .. } => true, _ => false } } }; if is_mut { @@ -462,7 +463,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { (Mutability::Not, BindingMode::ByValue), ty::BindByReference(hir::MutMutable) => (Mutability::Not, BindingMode::ByRef( - region.unwrap(), BorrowKind::Mut)), + region.unwrap(), BorrowKind::Mut { allow_two_phase_borrow: false })), ty::BindByReference(hir::MutImmutable) => (Mutability::Not, BindingMode::ByRef( region.unwrap(), BorrowKind::Shared)), @@ -672,6 +673,10 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { -> Pattern<'tcx> { let ty = self.tables.node_id_to_type(id); let def = self.tables.qpath_def(qpath, id); + let is_associated_const = match def { + Def::AssociatedConst(_) => true, + _ => false, + }; let kind = match def { Def::Const(def_id) | Def::AssociatedConst(def_id) => { let substs = self.tables.node_substs(id); @@ -697,7 +702,11 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } }, None => { - self.errors.push(PatternError::StaticInPattern(span)); + self.errors.push(if is_associated_const { + PatternError::AssociatedConstInPattern(span) + } else { + PatternError::StaticInPattern(span) + }); PatternKind::Wild }, } @@ -814,7 +823,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { let field = Field::new(i); let val = match cv.val { ConstVal::Value(miri) => const_val_field( - self.tcx, self.param_env, instance, + self.tcx, self.param_env, instance, span, Some(variant_index), field, miri, cv.ty, ).unwrap(), _ => bug!("{:#?} is not a valid tuple", cv), @@ -842,7 +851,8 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { let field = Field::new(i); let val = match cv.val { ConstVal::Value(miri) => const_val_field( - self.tcx, self.param_env, instance, None, field, miri, cv.ty, + self.tcx, self.param_env, instance, span, + None, field, miri, cv.ty, ).unwrap(), _ => bug!("{:#?} is not a valid tuple", cv), }; @@ -859,7 +869,8 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { let field = Field::new(i); let val = match cv.val { ConstVal::Value(miri) => const_val_field( - self.tcx, self.param_env, instance, None, field, miri, cv.ty, + self.tcx, self.param_env, instance, span, + None, field, miri, cv.ty, ).unwrap(), _ => bug!("{:#?} is not a valid tuple", cv), }; @@ -877,7 +888,8 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { let field = Field::new(i); let val = match cv.val { ConstVal::Value(miri) => const_val_field( - self.tcx, self.param_env, instance, None, field, miri, cv.ty, + self.tcx, self.param_env, instance, span, + None, field, miri, cv.ty, ).unwrap(), _ => bug!("{:#?} is not a valid tuple", cv), }; diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index 6c1d762c912cf..c2f9690b9f196 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -374,6 +374,7 @@ pub fn const_val_field<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, param_env: ty::ParamEnv<'tcx>, instance: ty::Instance<'tcx>, + span: Span, variant: Option, field: mir::Field, val: Value, @@ -385,7 +386,7 @@ pub fn const_val_field<'a, 'tcx>( ty, })), Err(err) => Err(ConstEvalErr { - span: tcx.def_span(instance.def_id()), + span, kind: err.into(), }), } @@ -455,7 +456,6 @@ pub fn const_eval_provider<'a, 'tcx>( trace!("const eval: {:?}", key); let cid = key.value; let def_id = cid.instance.def.def_id(); - let span = tcx.def_span(def_id); if tcx.is_foreign_item(def_id) { let id = tcx.interpret_interner.get_cached(def_id); @@ -479,6 +479,7 @@ pub fn const_eval_provider<'a, 'tcx>( if let Some(id) = tcx.hir.as_local_node_id(def_id) { let tables = tcx.typeck_tables_of(def_id); + let span = tcx.def_span(def_id); // Do match-check before building MIR if tcx.check_match(def_id).is_err() { @@ -511,6 +512,7 @@ pub fn const_eval_provider<'a, 'tcx>( if tcx.is_static(def_id).is_some() { ecx.report(&mut err, true, None); } + let span = ecx.frame().span; ConstEvalErr { kind: err.into(), span, diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 08152019003f1..71450f4266576 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -1637,9 +1637,11 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M }; err.span_label(span, e.to_string()); let mut last_span = None; - for &Frame { instance, span, .. } in self.stack().iter().rev() { + // skip 1 because the last frame is just the environment of the constant + for &Frame { instance, span, .. } in self.stack().iter().skip(1).rev() { // make sure we don't emit frames that are duplicates of the previous if explicit_span == Some(span) { + last_span = Some(span); continue; } if let Some(last) = last_span { diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 6aa8b7e5449fd..8018073883fcc 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -203,6 +203,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { bx.tcx(), ty::ParamEnv::empty(traits::Reveal::All), self.instance, + constant.span, None, mir::Field::new(field as usize), c, From 45abb1ba8436efe960768b067a2683d577e16b8b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 29 Jan 2018 20:46:42 +0100 Subject: [PATCH 064/110] Stabilize const_indexing feature --- .../src/language-features/const-indexing.md | 19 ------------------- src/libsyntax/feature_gate.rs | 5 ++--- 2 files changed, 2 insertions(+), 22 deletions(-) delete mode 100644 src/doc/unstable-book/src/language-features/const-indexing.md diff --git a/src/doc/unstable-book/src/language-features/const-indexing.md b/src/doc/unstable-book/src/language-features/const-indexing.md deleted file mode 100644 index 42d46ce15f676..0000000000000 --- a/src/doc/unstable-book/src/language-features/const-indexing.md +++ /dev/null @@ -1,19 +0,0 @@ -# `const_indexing` - -The tracking issue for this feature is: [#29947] - -[#29947]: https://github.com/rust-lang/rust/issues/29947 - ------------------------- - -The `const_indexing` feature allows the constant evaluation of index operations -on constant arrays and repeat expressions. - -## Examples - -```rust -#![feature(const_indexing)] - -const ARR: [usize; 5] = [1, 2, 3, 4, 5]; -const ARR2: [usize; ARR[1]] = [42, 99]; -``` \ No newline at end of file diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 45d82bc7af38e..1a790bf78bd86 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -217,9 +217,6 @@ declare_features! ( // Allows the definition of `const fn` functions. (active, const_fn, "1.2.0", Some(24111)), - // Allows indexing into constant arrays. - (active, const_indexing, "1.4.0", Some(29947)), - // Allows using #[prelude_import] on glob `use` items. // // rustc internal @@ -490,6 +487,8 @@ declare_features! ( (accepted, augmented_assignments, "1.8.0", Some(28235)), // allow empty structs and enum variants with braces (accepted, braced_empty_structs, "1.8.0", Some(29720)), + // Allows indexing into constant arrays. + (accepted, const_indexing, "1.24.0", Some(29947)), (accepted, default_type_params, "1.0.0", None), (accepted, globs, "1.0.0", None), (accepted, if_let, "1.0.0", None), From ceb634a7323b9957ae94873c6bcb623bf61af34f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 29 Jan 2018 20:47:09 +0100 Subject: [PATCH 065/110] Update tests --- src/librustc_mir/hair/pattern/_match.rs | 8 +++--- src/librustc_mir/transform/const_prop.rs | 7 +++-- src/test/compile-fail/array_const_index-0.rs | 1 + src/test/compile-fail/array_const_index-1.rs | 1 + src/test/compile-fail/const-err-early.rs | 11 +++++++- src/test/compile-fail/const-err-multi.rs | 5 ++++ src/test/compile-fail/const-err.rs | 1 + src/test/compile-fail/const-eval-overflow2.rs | 28 +++++++++---------- .../compile-fail/const-eval-overflow2b.rs | 28 +++++++++---------- .../compile-fail/const-eval-overflow2c.rs | 28 +++++++++---------- src/test/compile-fail/const-slice-oob.rs | 1 + .../compile-fail/lint-exceeding-bitshifts.rs | 1 - .../compile-fail/lint-exceeding-bitshifts2.rs | 6 ++-- src/test/compile-fail/lint-type-overflow2.rs | 1 - src/test/ui/error-codes/E0080.rs | 1 + src/test/ui/infinite-recursion-const-fn.rs | 4 +-- 16 files changed, 75 insertions(+), 57 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 7f443893544f4..5476d73fb6b4b 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -185,7 +185,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { match b { Value::ByVal(PrimVal::Ptr(ptr)) => { let is_array_ptr = ty - .builtin_deref(true, ty::NoPreference) + .builtin_deref(true) .and_then(|t| t.ty.builtin_index()) .map_or(false, |t| t == tcx.types.u8); assert!(is_array_ptr); @@ -560,7 +560,7 @@ fn max_slice_length<'p, 'a: 'p, 'tcx: 'a, I>( } } => { let is_array_ptr = ty - .builtin_deref(true, ty::NoPreference) + .builtin_deref(true) .and_then(|t| t.ty.builtin_index()) .map_or(false, |t| t == cx.tcx.types.u8); if is_array_ptr { @@ -949,7 +949,7 @@ fn slice_pat_covered_by_constructor(tcx: TyCtxt, _span: Span, Value::ByVal(PrimVal::Ptr(ptr)) ), ty }) => { let is_array_ptr = ty - .builtin_deref(true, ty::NoPreference) + .builtin_deref(true) .and_then(|t| t.ty.builtin_index()) .map_or(false, |t| t == tcx.types.u8); assert!(is_array_ptr); @@ -1089,7 +1089,7 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>( Slice(..) => match value.val { ConstVal::Value(Value::ByVal(PrimVal::Ptr(ptr))) => { let is_array_ptr = value.ty - .builtin_deref(true, ty::NoPreference) + .builtin_deref(true) .and_then(|t| t.ty.builtin_index()) .map_or(false, |t| t == cx.tcx.types.u8); assert!(is_array_ptr); diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index e0d8744f7c400..039aecdb0b3c1 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -198,7 +198,6 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { Rvalue::CheckedBinaryOp(op, ref left, ref right) | Rvalue::BinaryOp(op, ref left, ref right) => { trace!("rvalue binop {:?} for {:?} and {:?}", op, left, right); - let left = self.eval_operand(left)?; let right = self.eval_operand(right)?; let def_id = if self.tcx.is_closure(self.source.def_id) { self.tcx.closure_base_def_id(self.source.def_id) @@ -218,8 +217,8 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { let r = ecx.value_to_primval(ValTy { value: right.0, ty: right.1 }).ok()?; if op == BinOp::Shr || op == BinOp::Shl { let param_env = self.tcx.param_env(self.source.def_id); - let bits = (self.tcx, param_env).layout_of(left.1).unwrap().size.bits(); - if r.to_bytes().ok()? >= bits as u128 { + let bits = (self.tcx, param_env).layout_of(place_ty).unwrap().size.bits(); + if r.to_bytes().ok().map_or(false, |b| b >= bits as u128) { let scope_info = match self.mir.visibility_scope_info { ClearCrossCrate::Set(ref data) => data, ClearCrossCrate::Clear => return None, @@ -230,8 +229,10 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { node_id, span, "bitshift exceeds the type's number of bits"); + return None; } } + let left = self.eval_operand(left)?; let l = ecx.value_to_primval(ValTy { value: left.0, ty: left.1 }).ok()?; trace!("const evaluating {:?} for {:?} and {:?}", op, left, right); match ecx.binary_op(op, l, left.1, r, right.1) { diff --git a/src/test/compile-fail/array_const_index-0.rs b/src/test/compile-fail/array_const_index-0.rs index 501c66e75cded..35e7a42256251 100644 --- a/src/test/compile-fail/array_const_index-0.rs +++ b/src/test/compile-fail/array_const_index-0.rs @@ -11,6 +11,7 @@ const A: &'static [i32] = &[]; const B: i32 = (&A)[1]; //~^ ERROR constant evaluation error +//~| ERROR E0080 //~| index out of bounds: the len is 0 but the index is 1 fn main() { diff --git a/src/test/compile-fail/array_const_index-1.rs b/src/test/compile-fail/array_const_index-1.rs index d3b43e83bfe52..db4cfa4268919 100644 --- a/src/test/compile-fail/array_const_index-1.rs +++ b/src/test/compile-fail/array_const_index-1.rs @@ -11,6 +11,7 @@ const A: [i32; 0] = []; const B: i32 = A[1]; //~^ ERROR constant evaluation error +//~| ERROR E0080 //~| index out of bounds: the len is 0 but the index is 1 fn main() { diff --git a/src/test/compile-fail/const-err-early.rs b/src/test/compile-fail/const-err-early.rs index 4a146d8de3cde..3182ffa73b08d 100644 --- a/src/test/compile-fail/const-err-early.rs +++ b/src/test/compile-fail/const-err-early.rs @@ -8,18 +8,27 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(const_indexing)] #![deny(const_err)] pub const A: i8 = -std::i8::MIN; //~ ERROR E0080 //~| ERROR const_err //~| ERROR const_err +//~| ERROR E0080 pub const B: u8 = 200u8 + 200u8; //~ ERROR E0080 +//~| ERROR E0080 pub const C: u8 = 200u8 * 4; //~ ERROR E0080 +//~| ERROR E0080 pub const D: u8 = 42u8 - (42u8 + 1); //~ ERROR E0080 +//~| ERROR E0080 pub const E: u8 = [5u8][1]; //~^ ERROR E0080 +//~| ERROR E0080 fn main() { + let _a = A; + let _b = B; + let _c = C; + let _d = D; + let _e = E; let _e = [6u8][1]; } diff --git a/src/test/compile-fail/const-err-multi.rs b/src/test/compile-fail/const-err-multi.rs index eb24a698419eb..1e3150a74d985 100644 --- a/src/test/compile-fail/const-err-multi.rs +++ b/src/test/compile-fail/const-err-multi.rs @@ -14,12 +14,17 @@ pub const A: i8 = -std::i8::MIN; //~^ ERROR E0080 //~| ERROR const_err //~| ERROR const_err +//~| ERROR E0080 pub const B: i8 = A; //~^ ERROR E0080 +//~| ERROR E0080 pub const C: u8 = A as u8; //~^ ERROR E0080 +//~| ERROR E0080 pub const D: i8 = 50 - A; //~^ ERROR E0080 +//~| ERROR E0080 fn main() { + let _ = (A, B, C, D); } diff --git a/src/test/compile-fail/const-err.rs b/src/test/compile-fail/const-err.rs index 8bd759b6d3735..5f55cc82e6d83 100644 --- a/src/test/compile-fail/const-err.rs +++ b/src/test/compile-fail/const-err.rs @@ -24,6 +24,7 @@ fn black_box(_: T) { const FOO: u8 = [5u8][1]; //~^ ERROR constant evaluation error //~| index out of bounds: the len is 1 but the index is 1 +//~| ERROR E0080 fn main() { black_box((FOO, FOO)); diff --git a/src/test/compile-fail/const-eval-overflow2.rs b/src/test/compile-fail/const-eval-overflow2.rs index 61a653589fff9..aa735cc045364 100644 --- a/src/test/compile-fail/const-eval-overflow2.rs +++ b/src/test/compile-fail/const-eval-overflow2.rs @@ -22,57 +22,57 @@ use std::{i8, i16, i32, i64, isize}; use std::{u8, u16, u32, u64, usize}; const VALS_I8: (i8,) = - ( - i8::MIN - 1, //~^ ERROR constant evaluation error //~| attempt to subtract with overflow + ( + i8::MIN - 1, ); const VALS_I16: (i16,) = - ( - i16::MIN - 1, //~^ ERROR constant evaluation error //~| attempt to subtract with overflow + ( + i16::MIN - 1, ); const VALS_I32: (i32,) = - ( - i32::MIN - 1, //~^ ERROR constant evaluation error //~| attempt to subtract with overflow + ( + i32::MIN - 1, ); const VALS_I64: (i64,) = - ( - i64::MIN - 1, //~^ ERROR constant evaluation error //~| attempt to subtract with overflow + ( + i64::MIN - 1, ); const VALS_U8: (u8,) = - ( - u8::MIN - 1, //~^ ERROR constant evaluation error //~| attempt to subtract with overflow + ( + u8::MIN - 1, ); const VALS_U16: (u16,) = ( - u16::MIN - 1, //~^ ERROR constant evaluation error //~| attempt to subtract with overflow + u16::MIN - 1, ); const VALS_U32: (u32,) = ( - u32::MIN - 1, //~^ ERROR constant evaluation error //~| attempt to subtract with overflow + u32::MIN - 1, ); const VALS_U64: (u64,) = - ( - u64::MIN - 1, //~^ ERROR constant evaluation error //~| attempt to subtract with overflow + ( + u64::MIN - 1, ); fn main() { diff --git a/src/test/compile-fail/const-eval-overflow2b.rs b/src/test/compile-fail/const-eval-overflow2b.rs index b2e84486101bc..01c3ea1ff425f 100644 --- a/src/test/compile-fail/const-eval-overflow2b.rs +++ b/src/test/compile-fail/const-eval-overflow2b.rs @@ -22,57 +22,57 @@ use std::{i8, i16, i32, i64, isize}; use std::{u8, u16, u32, u64, usize}; const VALS_I8: (i8,) = - ( - i8::MAX + 1, //~^ ERROR constant evaluation error //~| attempt to add with overflow + ( + i8::MAX + 1, ); const VALS_I16: (i16,) = - ( - i16::MAX + 1, //~^ ERROR constant evaluation error //~| attempt to add with overflow + ( + i16::MAX + 1, ); const VALS_I32: (i32,) = - ( - i32::MAX + 1, //~^ ERROR constant evaluation error //~| attempt to add with overflow + ( + i32::MAX + 1, ); const VALS_I64: (i64,) = - ( - i64::MAX + 1, //~^ ERROR constant evaluation error //~| attempt to add with overflow + ( + i64::MAX + 1, ); const VALS_U8: (u8,) = - ( - u8::MAX + 1, //~^ ERROR constant evaluation error //~| attempt to add with overflow + ( + u8::MAX + 1, ); const VALS_U16: (u16,) = ( - u16::MAX + 1, //~^ ERROR constant evaluation error //~| attempt to add with overflow + u16::MAX + 1, ); const VALS_U32: (u32,) = ( - u32::MAX + 1, //~^ ERROR constant evaluation error //~| attempt to add with overflow + u32::MAX + 1, ); const VALS_U64: (u64,) = - ( - u64::MAX + 1, //~^ ERROR constant evaluation error //~| attempt to add with overflow + ( + u64::MAX + 1, ); fn main() { diff --git a/src/test/compile-fail/const-eval-overflow2c.rs b/src/test/compile-fail/const-eval-overflow2c.rs index c4a8beaf0f057..dd7c151d85f69 100644 --- a/src/test/compile-fail/const-eval-overflow2c.rs +++ b/src/test/compile-fail/const-eval-overflow2c.rs @@ -22,57 +22,57 @@ use std::{i8, i16, i32, i64, isize}; use std::{u8, u16, u32, u64, usize}; const VALS_I8: (i8,) = - ( - i8::MIN * 2, //~^ ERROR constant evaluation error //~| attempt to multiply with overflow + ( + i8::MIN * 2, ); const VALS_I16: (i16,) = - ( - i16::MIN * 2, //~^ ERROR constant evaluation error //~| attempt to multiply with overflow + ( + i16::MIN * 2, ); const VALS_I32: (i32,) = - ( - i32::MIN * 2, //~^ ERROR constant evaluation error //~| attempt to multiply with overflow + ( + i32::MIN * 2, ); const VALS_I64: (i64,) = - ( - i64::MIN * 2, //~^ ERROR constant evaluation error //~| attempt to multiply with overflow + ( + i64::MIN * 2, ); const VALS_U8: (u8,) = - ( - u8::MAX * 2, //~^ ERROR constant evaluation error //~| attempt to multiply with overflow + ( + u8::MAX * 2, ); const VALS_U16: (u16,) = ( - u16::MAX * 2, //~^ ERROR constant evaluation error //~| attempt to multiply with overflow + u16::MAX * 2, ); const VALS_U32: (u32,) = ( - u32::MAX * 2, //~^ ERROR constant evaluation error //~| attempt to multiply with overflow + u32::MAX * 2, ); const VALS_U64: (u64,) = - ( - u64::MAX * 2, //~^ ERROR constant evaluation error //~| attempt to multiply with overflow + ( + u64::MAX * 2, ); fn main() { diff --git a/src/test/compile-fail/const-slice-oob.rs b/src/test/compile-fail/const-slice-oob.rs index 179ea9e853f3a..cb884313f3373 100644 --- a/src/test/compile-fail/const-slice-oob.rs +++ b/src/test/compile-fail/const-slice-oob.rs @@ -13,6 +13,7 @@ const FOO: &'static[u32] = &[1, 2, 3]; const BAR: u32 = FOO[5]; //~^ ERROR constant evaluation error [E0080] +//~| ERROR constant evaluation error [E0080] //~| index out of bounds: the len is 3 but the index is 5 fn main() { diff --git a/src/test/compile-fail/lint-exceeding-bitshifts.rs b/src/test/compile-fail/lint-exceeding-bitshifts.rs index 2634157c80671..34b43c5badb51 100644 --- a/src/test/compile-fail/lint-exceeding-bitshifts.rs +++ b/src/test/compile-fail/lint-exceeding-bitshifts.rs @@ -11,7 +11,6 @@ #![deny(exceeding_bitshifts)] #![allow(unused_variables)] #![allow(dead_code, const_err)] -#![feature(const_indexing)] fn main() { let n = 1u8 << 7; diff --git a/src/test/compile-fail/lint-exceeding-bitshifts2.rs b/src/test/compile-fail/lint-exceeding-bitshifts2.rs index 0eab143fa49e0..a8027034fbee8 100644 --- a/src/test/compile-fail/lint-exceeding-bitshifts2.rs +++ b/src/test/compile-fail/lint-exceeding-bitshifts2.rs @@ -14,7 +14,7 @@ fn main() { let n = 1u8 << (4+3); - let n = 1u8 << (4+4); //~ ERROR: const_err + let n = 1u8 << (4+4); //~ ERROR: bitshift exceeds let n = 1i64 >> [63][0]; let n = 1i64 >> [64][0]; // should be linting, needs to wait for const propagation @@ -22,6 +22,6 @@ fn main() { const BITS: usize = 32; #[cfg(target_pointer_width = "64")] const BITS: usize = 64; - let n = 1_isize << BITS; //~ ERROR: const_err - let n = 1_usize << BITS; //~ ERROR: const_err + let n = 1_isize << BITS; //~ ERROR: bitshift exceeds + let n = 1_usize << BITS; //~ ERROR: bitshift exceeds } diff --git a/src/test/compile-fail/lint-type-overflow2.rs b/src/test/compile-fail/lint-type-overflow2.rs index 9178d8d2f6ed2..f7cf8a68d5684 100644 --- a/src/test/compile-fail/lint-type-overflow2.rs +++ b/src/test/compile-fail/lint-type-overflow2.rs @@ -17,7 +17,6 @@ #[rustc_error] fn main() { //~ ERROR: compilation successful let x2: i8 = --128; //~ warn: literal out of range for i8 - //~^ WARN constant evaluation error let x = -3.40282357e+38_f32; //~ warn: literal out of range for f32 let x = 3.40282357e+38_f32; //~ warn: literal out of range for f32 diff --git a/src/test/ui/error-codes/E0080.rs b/src/test/ui/error-codes/E0080.rs index c8e425711284e..cca63f23b7776 100644 --- a/src/test/ui/error-codes/E0080.rs +++ b/src/test/ui/error-codes/E0080.rs @@ -10,6 +10,7 @@ enum Enum { X = (1 << 500), //~ ERROR E0080 + //~| ERROR bitshift exceeds //~| shift left with overflow Y = (1 / 0) //~ ERROR E0080 //~| const_err diff --git a/src/test/ui/infinite-recursion-const-fn.rs b/src/test/ui/infinite-recursion-const-fn.rs index 05e40abdc0f2f..f98074bc554bb 100644 --- a/src/test/ui/infinite-recursion-const-fn.rs +++ b/src/test/ui/infinite-recursion-const-fn.rs @@ -11,8 +11,8 @@ //https://github.com/rust-lang/rust/issues/31364 #![feature(const_fn)] -const fn a() -> usize { b() } +const fn a() -> usize { b() } //~ ERROR constant evaluation error const fn b() -> usize { a() } -const ARR: [i32; a()] = [5; 6]; //~ ERROR constant evaluation error +const ARR: [i32; a()] = [5; 6]; fn main(){} From 16d560fd993d5cd68423159d32d9f6b454349763 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 30 Jan 2018 09:40:26 +0100 Subject: [PATCH 066/110] Remove unused error E0020 --- src/librustc/diagnostics.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 287516474d49a..b3a904f2f5fec 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -14,19 +14,6 @@ // Each message should start and end with a new line, and be wrapped to 80 characters. // In vim you can `:set tw=80` and use `gq` to wrap paragraphs. Use `:set tw=0` to disable. register_long_diagnostics! { -E0020: r##" -This error indicates that an attempt was made to divide by zero (or take the -remainder of a zero divisor) in a static or constant expression. Erroneous -code example: - -```compile_fail -#[deny(const_err)] - -const X: i32 = 42 / 0; -// error: attempt to divide by zero in a constant expression -``` -"##, - E0038: r##" Trait objects like `Box` can only be constructed when certain requirements are satisfied by the trait in question. From 0462d092d27bf72a7c66303571d3852d596a81e9 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 30 Jan 2018 09:40:46 +0100 Subject: [PATCH 067/110] Figure out const propgatable locals in a single pass --- src/librustc_mir/transform/const_prop.rs | 58 ++++++++++++------------ src/librustc_trans/mir/operand.rs | 1 + 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 039aecdb0b3c1..51ac8b2c61a20 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -55,6 +55,7 @@ struct OptimizationFinder<'b, 'a, 'tcx:'a+'b> { tcx: TyCtxt<'a, 'tcx, 'tcx>, source: MirSource, places: IndexVec>>, + can_const_prop: IndexVec, } impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { @@ -67,6 +68,7 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { mir, tcx, source, + can_const_prop: CanConstProp::check(mir), places: IndexVec::from_elem(None, &mir.local_decls), } } @@ -271,33 +273,34 @@ fn type_size_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } struct CanConstProp { - local: Local, - can_const_prop: bool, + can_const_prop: IndexVec, // false at the beginning, once set, there are not allowed to be any more assignments - found_assignment: bool, + found_assignment: IndexVec, } impl CanConstProp { /// returns true if `local` can be propagated - fn check<'tcx>(local: Local, mir: &Mir<'tcx>) -> bool { + fn check<'tcx>(mir: &Mir<'tcx>) -> IndexVec { let mut cpv = CanConstProp { - local, - can_const_prop: true, - found_assignment: false, + can_const_prop: IndexVec::from_elem(true, &mir.local_decls), + found_assignment: IndexVec::from_elem(false, &mir.local_decls), }; + for (local, val) in cpv.can_const_prop.iter_enumerated_mut() { + *val = mir.local_kind(local) == LocalKind::Temp; + } cpv.visit_mir(mir); cpv.can_const_prop } +} - fn is_our_local(&mut self, mut place: &Place) -> bool { - while let Place::Projection(ref proj) = place { - place = &proj.base; - } - if let Place::Local(local) = *place { - local == self.local - } else { - false - } +fn place_to_local(mut place: &Place) -> Option { + while let Place::Projection(ref proj) = place { + place = &proj.base; + } + if let Place::Local(local) = *place { + Some(local) + } else { + None } } @@ -312,21 +315,21 @@ impl<'tcx> Visitor<'tcx> for CanConstProp { match statement.kind { StatementKind::SetDiscriminant { ref place, .. } | StatementKind::Assign(ref place, _) => { - if self.is_our_local(place) { - if self.found_assignment { - self.can_const_prop = false; + if let Some(local) = place_to_local(place) { + if self.found_assignment[local] { + self.can_const_prop[local] = false; } else { - self.found_assignment = true + self.found_assignment[local] = true } } }, StatementKind::InlineAsm { ref outputs, .. } => { for place in outputs { - if self.is_our_local(place) { - if self.found_assignment { - self.can_const_prop = false; + if let Some(local) = place_to_local(place) { + if self.found_assignment[local] { + self.can_const_prop[local] = false; } else { - self.found_assignment = true + self.found_assignment[local] = true } return; } @@ -338,8 +341,8 @@ impl<'tcx> Visitor<'tcx> for CanConstProp { fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { self.super_rvalue(rvalue, location); if let Rvalue::Ref(_, _, ref place) = *rvalue { - if self.is_our_local(place) { - self.can_const_prop = false; + if let Some(local) = place_to_local(place) { + self.can_const_prop[local] = false; } } } @@ -369,8 +372,7 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for OptimizationFinder<'b, 'a, 'tcx> { .to_ty(self.tcx); if let Some(value) = self.const_prop(rval, place_ty, statement.source_info) { if let Place::Local(local) = *place { - if self.mir.local_kind(local) == LocalKind::Temp - && CanConstProp::check(local, self.mir) { + if self.can_const_prop[local] { trace!("storing {:?} to {:?}", value, local); assert!(self.places[local].is_none()); self.places[local] = Some(value); diff --git a/src/librustc_trans/mir/operand.rs b/src/librustc_trans/mir/operand.rs index a66528858e14a..75df349de41ee 100644 --- a/src/librustc_trans/mir/operand.rs +++ b/src/librustc_trans/mir/operand.rs @@ -12,6 +12,7 @@ use llvm::ValueRef; use rustc::middle::const_val::ConstEvalErr; use rustc::mir; use rustc::mir::interpret::Value as MiriValue; +use rustc::ty; use rustc::ty::layout::{self, Align, LayoutOf, TyLayout}; use rustc_data_structures::indexed_vec::Idx; From 65ed591197cdb6e17f505a0b2d8c38d730491c98 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 30 Jan 2018 11:18:37 +0100 Subject: [PATCH 068/110] Move the resource limits to the session in preparation for attributes configuring them --- src/librustc/session/mod.rs | 10 ++++++++ src/librustc_mir/interpret/const_eval.rs | 9 +++---- src/librustc_mir/interpret/eval_context.rs | 28 ++++------------------ src/librustc_mir/interpret/memory.rs | 4 ++-- src/librustc_mir/interpret/mod.rs | 2 +- src/librustc_mir/interpret/step.rs | 2 +- 6 files changed, 22 insertions(+), 33 deletions(-) diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 5e9eeb973007f..232344a036783 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -103,6 +103,13 @@ pub struct Session { /// The maximum length of types during monomorphization. pub type_length_limit: Cell, + /// The maximum number of stackframes allowed in const eval + pub const_eval_stack_frame_limit: Cell, + /// The maximum number miri steps per constant + pub const_eval_step_limit: Cell, + /// The maximum number of virtual bytes per constant + pub const_eval_memory_limit: Cell, + /// The metadata::creader module may inject an allocator/panic_runtime /// dependency if it didn't already find one, and this tracks what was /// injected. @@ -1004,6 +1011,9 @@ pub fn build_session_(sopts: config::Options, features: RefCell::new(None), recursion_limit: Cell::new(64), type_length_limit: Cell::new(1048576), + const_eval_stack_frame_limit: Cell::new(100), + const_eval_step_limit: Cell::new(1_000_000), + const_eval_memory_limit: Cell::new(100 * 1024 * 1024), // 100 MB next_node_id: Cell::new(NodeId::new(1)), injected_allocator: Cell::new(None), allocator_kind: Cell::new(None), diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index c2f9690b9f196..e216f5c4d8a82 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -24,8 +24,7 @@ pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>( ) -> EvalResult<'tcx, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>> { debug!("mk_borrowck_eval_cx: {:?}", instance); let param_env = tcx.param_env(instance.def_id()); - let limits = super::ResourceLimits::default(); - let mut ecx = EvalContext::new(tcx, param_env, limits, CompileTimeEvaluator, ()); + let mut ecx = EvalContext::new(tcx, param_env, CompileTimeEvaluator, ()); // insert a stack frame so any queries have the correct substs ecx.push_stack_frame( instance, @@ -43,8 +42,7 @@ pub fn mk_eval_cx<'a, 'tcx>( param_env: ty::ParamEnv<'tcx>, ) -> EvalResult<'tcx, EvalContext<'a, 'tcx, 'tcx, CompileTimeEvaluator>> { debug!("mk_eval_cx: {:?}, {:?}", instance, param_env); - let limits = super::ResourceLimits::default(); - let mut ecx = EvalContext::new(tcx, param_env, limits, CompileTimeEvaluator, ()); + let mut ecx = EvalContext::new(tcx, param_env, CompileTimeEvaluator, ()); let mir = ecx.load_mir(instance.def)?; // insert a stack frame so any queries have the correct substs ecx.push_stack_frame( @@ -95,8 +93,7 @@ fn eval_body_and_ecx<'a, 'mir, 'tcx>( param_env: ty::ParamEnv<'tcx>, ) -> (EvalResult<'tcx, (Value, Pointer, Ty<'tcx>)>, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>) { debug!("eval_body: {:?}, {:?}", cid, param_env); - let limits = super::ResourceLimits::default(); - let mut ecx = EvalContext::new(tcx, param_env, limits, CompileTimeEvaluator, ()); + let mut ecx = EvalContext::new(tcx, param_env, CompileTimeEvaluator, ()); let res = (|| { let mut mir = match mir { Some(mir) => mir, diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 71450f4266576..676f99ea6be0c 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -43,7 +43,7 @@ pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { /// The maximum number of operations that may be executed. /// This prevents infinite loops and huge computations from freezing up const eval. /// Remove once halting problem is solved. - pub(crate) steps_remaining: u64, + pub(crate) steps_remaining: usize, } /// A stack frame. @@ -102,23 +102,6 @@ pub enum StackPopCleanup { None, } -#[derive(Copy, Clone, Debug)] -pub struct ResourceLimits { - pub memory_size: u64, - pub step_limit: u64, - pub stack_limit: usize, -} - -impl Default for ResourceLimits { - fn default() -> Self { - ResourceLimits { - memory_size: 100 * 1024 * 1024, // 100 MB - step_limit: 1_000_000, - stack_limit: 100, - } - } -} - #[derive(Copy, Clone, Debug)] pub struct TyAndPacked<'tcx> { pub ty: Ty<'tcx>, @@ -200,7 +183,6 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M pub fn new( tcx: TyCtxt<'a, 'tcx, 'tcx>, param_env: ty::ParamEnv<'tcx>, - limits: ResourceLimits, machine: M, memory_data: M::MemoryData, ) -> Self { @@ -208,10 +190,10 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M machine, tcx, param_env, - memory: Memory::new(tcx, limits.memory_size, memory_data), + memory: Memory::new(tcx, memory_data), stack: Vec::new(), - stack_limit: limits.stack_limit, - steps_remaining: limits.step_limit, + stack_limit: tcx.sess.const_eval_stack_frame_limit.get(), + steps_remaining: tcx.sess.const_eval_step_limit.get(), } } @@ -559,7 +541,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M } Aggregate(ref kind, ref operands) => { - self.inc_step_counter_and_check_limit(operands.len() as u64)?; + self.inc_step_counter_and_check_limit(operands.len())?; let (dest, active_field_index) = match **kind { mir::AggregateKind::Adt(adt_def, variant_index, _, active_field_index) => { diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index e67c36779111b..da8ff11e3d0a9 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -55,14 +55,14 @@ pub struct Memory<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { } impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, max_memory: u64, data: M::MemoryData) -> Self { + pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, data: M::MemoryData) -> Self { Memory { data, alloc_kind: HashMap::new(), alloc_map: HashMap::new(), uninitialized_statics: HashMap::new(), tcx, - memory_size: max_memory, + memory_size: tcx.sess.const_eval_memory_limit.get(), memory_usage: 0, cur_frame: usize::max_value(), } diff --git a/src/librustc_mir/interpret/mod.rs b/src/librustc_mir/interpret/mod.rs index f23ba90fd4c3f..8e0158569a867 100644 --- a/src/librustc_mir/interpret/mod.rs +++ b/src/librustc_mir/interpret/mod.rs @@ -11,7 +11,7 @@ mod step; mod terminator; mod traits; -pub use self::eval_context::{EvalContext, Frame, ResourceLimits, StackPopCleanup, +pub use self::eval_context::{EvalContext, Frame, StackPopCleanup, TyAndPacked, ValTy}; pub use self::place::{Place, PlaceExtra}; diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs index 21e81ff668ea5..94fe3d1c67b8f 100644 --- a/src/librustc_mir/interpret/step.rs +++ b/src/librustc_mir/interpret/step.rs @@ -8,7 +8,7 @@ use rustc::mir::interpret::EvalResult; use super::{EvalContext, Machine}; impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { - pub fn inc_step_counter_and_check_limit(&mut self, n: u64) -> EvalResult<'tcx> { + pub fn inc_step_counter_and_check_limit(&mut self, n: usize) -> EvalResult<'tcx> { self.steps_remaining = self.steps_remaining.saturating_sub(n); if self.steps_remaining > 0 { Ok(()) From 57ae890265d99c1c01ad50730d372b8354a8d7d7 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 30 Jan 2018 13:57:13 +0100 Subject: [PATCH 069/110] Simplify const prop checks through PlaceContext --- src/librustc/mir/interpret/mod.rs | 2 +- src/librustc_mir/transform/const_prop.rs | 104 +++++++++++------------ 2 files changed, 52 insertions(+), 54 deletions(-) diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs index b621f3f3731a6..91f7d79108bb8 100644 --- a/src/librustc/mir/interpret/mod.rs +++ b/src/librustc/mir/interpret/mod.rs @@ -171,7 +171,7 @@ pub struct Allocation { /// The alignment of the allocation to detect unaligned reads. pub align: Align, /// Whether the allocation (of a static) should be put into mutable memory when translating - /// + /// /// Only happens for `static mut` or `static` with interior mutability pub runtime_mutability: Mutability, } diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 51ac8b2c61a20..f461fb736c5ac 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -15,8 +15,8 @@ use rustc::mir::{Constant, Literal, Location, Place, Mir, Operand, Rvalue, Local}; use rustc::mir::{NullOp, StatementKind, Statement, BasicBlock, LocalKind}; -use rustc::mir::{TerminatorKind, ClearCrossCrate, SourceInfo, BinOp}; -use rustc::mir::visit::Visitor; +use rustc::mir::{TerminatorKind, ClearCrossCrate, SourceInfo, BinOp, BorrowKind}; +use rustc::mir::visit::{Visitor, PlaceContext}; use rustc::ty::layout::LayoutOf; use rustc::middle::const_val::ConstVal; use rustc::ty::{TyCtxt, self, Instance}; @@ -64,11 +64,16 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { tcx: TyCtxt<'a, 'tcx, 'tcx>, source: MirSource, ) -> OptimizationFinder<'b, 'a, 'tcx> { + let can_const_prop = CanConstProp::check( + mir, + tcx, + tcx.param_env(source.def_id), + ); OptimizationFinder { mir, tcx, source, - can_const_prop: CanConstProp::check(mir), + can_const_prop, places: IndexVec::from_elem(None, &mir.local_decls), } } @@ -272,78 +277,71 @@ fn type_size_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, (tcx, param_env).layout_of(ty).ok().map(|layout| layout.size.bytes()) } -struct CanConstProp { +struct CanConstProp<'b, 'a, 'tcx:'a+'b> { can_const_prop: IndexVec, // false at the beginning, once set, there are not allowed to be any more assignments found_assignment: IndexVec, + mir: &'b Mir<'tcx>, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: ty::ParamEnv<'tcx>, } -impl CanConstProp { +impl<'b, 'a, 'tcx:'b> CanConstProp<'b, 'a, 'tcx> { /// returns true if `local` can be propagated - fn check<'tcx>(mir: &Mir<'tcx>) -> IndexVec { + fn check( + mir: &'b Mir<'tcx>, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> IndexVec { let mut cpv = CanConstProp { can_const_prop: IndexVec::from_elem(true, &mir.local_decls), found_assignment: IndexVec::from_elem(false, &mir.local_decls), + mir, + tcx, + param_env, }; for (local, val) in cpv.can_const_prop.iter_enumerated_mut() { - *val = mir.local_kind(local) == LocalKind::Temp; + *val = mir.local_kind(local) != LocalKind::Arg; } cpv.visit_mir(mir); cpv.can_const_prop } } -fn place_to_local(mut place: &Place) -> Option { - while let Place::Projection(ref proj) = place { - place = &proj.base; - } - if let Place::Local(local) = *place { - Some(local) - } else { - None - } -} - -impl<'tcx> Visitor<'tcx> for CanConstProp { - fn visit_statement( +impl<'a, 'b, 'tcx> Visitor<'tcx> for CanConstProp<'a, 'b, 'tcx> { + fn visit_local( &mut self, - block: BasicBlock, - statement: &Statement<'tcx>, - location: Location, + &local: &Local, + context: PlaceContext<'tcx>, + _: Location, ) { - self.super_statement(block, statement, location); - match statement.kind { - StatementKind::SetDiscriminant { ref place, .. } | - StatementKind::Assign(ref place, _) => { - if let Some(local) = place_to_local(place) { - if self.found_assignment[local] { - self.can_const_prop[local] = false; - } else { - self.found_assignment[local] = true - } - } + use rustc::mir::visit::PlaceContext::*; + match context { + // Constants must have at most one write + // FIXME(oli-obk): we could be more powerful here, if the multiple writes + // only occur in independent execution paths + Store => if self.found_assignment[local] { + self.can_const_prop[local] = false; + } else { + self.found_assignment[local] = true }, - StatementKind::InlineAsm { ref outputs, .. } => { - for place in outputs { - if let Some(local) = place_to_local(place) { - if self.found_assignment[local] { - self.can_const_prop[local] = false; - } else { - self.found_assignment[local] = true - } - return; - } + // Reading constants is allowed an arbitrary number of times + Copy | Move | + StorageDead | StorageLive | + Validate | + Inspect => {}, + Borrow { kind: BorrowKind::Shared, .. } => { + // cannot const prop immutable borrows of types with interior mutability + let has_interior_mutability = self + .mir + .local_decls[local] + .ty + .is_freeze(self.tcx, self.param_env, self.mir.span); + if has_interior_mutability { + self.can_const_prop[local] = false; } } - _ => {} - } - } - fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { - self.super_rvalue(rvalue, location); - if let Rvalue::Ref(_, _, ref place) = *rvalue { - if let Some(local) = place_to_local(place) { - self.can_const_prop[local] = false; - } + _ => self.can_const_prop[local] = false, } } } From 411fcf58fdb6c08c0bfa5bcefd87ceb95fbde2af Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 30 Jan 2018 14:12:16 +0100 Subject: [PATCH 070/110] Rename OptimizationFinder and don't propagate any borrows --- src/librustc_mir/transform/const_prop.rs | 50 ++++++------------------ 1 file changed, 12 insertions(+), 38 deletions(-) diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index f461fb736c5ac..45cbdc7d0c1ee 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -15,7 +15,7 @@ use rustc::mir::{Constant, Literal, Location, Place, Mir, Operand, Rvalue, Local}; use rustc::mir::{NullOp, StatementKind, Statement, BasicBlock, LocalKind}; -use rustc::mir::{TerminatorKind, ClearCrossCrate, SourceInfo, BinOp, BorrowKind}; +use rustc::mir::{TerminatorKind, ClearCrossCrate, SourceInfo, BinOp}; use rustc::mir::visit::{Visitor, PlaceContext}; use rustc::ty::layout::LayoutOf; use rustc::middle::const_val::ConstVal; @@ -40,7 +40,7 @@ impl MirPass for ConstProp { // constants, instead of just checking for const-folding succeeding. // That would require an uniform one-def no-mutation analysis // and RPO (or recursing when needing the value of a local). - let mut optimization_finder = OptimizationFinder::new(mir, tcx, source); + let mut optimization_finder = ConstPropagator::new(mir, tcx, source); optimization_finder.visit_mir(mir); trace!("ConstProp done for {:?}", source.def_id); @@ -50,7 +50,7 @@ impl MirPass for ConstProp { type Const<'tcx> = (Value, ty::Ty<'tcx>, Span); /// Finds optimization opportunities on the MIR. -struct OptimizationFinder<'b, 'a, 'tcx:'a+'b> { +struct ConstPropagator<'b, 'a, 'tcx:'a+'b> { mir: &'b Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>, source: MirSource, @@ -58,22 +58,17 @@ struct OptimizationFinder<'b, 'a, 'tcx:'a+'b> { can_const_prop: IndexVec, } -impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { +impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { fn new( mir: &'b Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>, source: MirSource, - ) -> OptimizationFinder<'b, 'a, 'tcx> { - let can_const_prop = CanConstProp::check( - mir, - tcx, - tcx.param_env(source.def_id), - ); - OptimizationFinder { + ) -> ConstPropagator<'b, 'a, 'tcx> { + ConstPropagator { mir, tcx, source, - can_const_prop, + can_const_prop: CanConstProp::check(mir), places: IndexVec::from_elem(None, &mir.local_decls), } } @@ -277,28 +272,18 @@ fn type_size_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, (tcx, param_env).layout_of(ty).ok().map(|layout| layout.size.bytes()) } -struct CanConstProp<'b, 'a, 'tcx:'a+'b> { +struct CanConstProp { can_const_prop: IndexVec, // false at the beginning, once set, there are not allowed to be any more assignments found_assignment: IndexVec, - mir: &'b Mir<'tcx>, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - param_env: ty::ParamEnv<'tcx>, } -impl<'b, 'a, 'tcx:'b> CanConstProp<'b, 'a, 'tcx> { +impl CanConstProp { /// returns true if `local` can be propagated - fn check( - mir: &'b Mir<'tcx>, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> IndexVec { + fn check(mir: &Mir) -> IndexVec { let mut cpv = CanConstProp { can_const_prop: IndexVec::from_elem(true, &mir.local_decls), found_assignment: IndexVec::from_elem(false, &mir.local_decls), - mir, - tcx, - param_env, }; for (local, val) in cpv.can_const_prop.iter_enumerated_mut() { *val = mir.local_kind(local) != LocalKind::Arg; @@ -308,7 +293,7 @@ impl<'b, 'a, 'tcx:'b> CanConstProp<'b, 'a, 'tcx> { } } -impl<'a, 'b, 'tcx> Visitor<'tcx> for CanConstProp<'a, 'b, 'tcx> { +impl<'tcx> Visitor<'tcx> for CanConstProp { fn visit_local( &mut self, &local: &Local, @@ -330,23 +315,12 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for CanConstProp<'a, 'b, 'tcx> { StorageDead | StorageLive | Validate | Inspect => {}, - Borrow { kind: BorrowKind::Shared, .. } => { - // cannot const prop immutable borrows of types with interior mutability - let has_interior_mutability = self - .mir - .local_decls[local] - .ty - .is_freeze(self.tcx, self.param_env, self.mir.span); - if has_interior_mutability { - self.can_const_prop[local] = false; - } - } _ => self.can_const_prop[local] = false, } } } -impl<'b, 'a, 'tcx> Visitor<'tcx> for OptimizationFinder<'b, 'a, 'tcx> { +impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> { fn visit_constant( &mut self, constant: &Constant<'tcx>, From 1561b4ad504fabd3bda0f3ab6ffb8a4c3be92f81 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 30 Jan 2018 14:28:45 +0100 Subject: [PATCH 071/110] Simplify const propagator by removing unused code paths --- src/librustc_mir/transform/const_prop.rs | 123 ++++++++++------------- 1 file changed, 52 insertions(+), 71 deletions(-) diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 45cbdc7d0c1ee..fc2a149cfe940 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -359,81 +359,62 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> { &mut self, block: BasicBlock, kind: &TerminatorKind<'tcx>, - _location: Location, + location: Location, ) { - match kind { - TerminatorKind::SwitchInt { discr: value, .. } | - TerminatorKind::Yield { value, .. } | - TerminatorKind::Assert { cond: value, .. } => { - match value { - Operand::Constant(box Constant { - literal: Literal::Value { - value: &ty::Const { - val: ConstVal::Value(_), - .. - }, - }, - .. - }) => return, - _ => {}, - } - if let Some(value) = self.eval_operand(value) { - if let TerminatorKind::Assert { expected, msg, .. } = kind { - if Value::ByVal(PrimVal::from_bool(*expected)) != value.0 { - let span = self.mir[block] - .terminator - .as_ref() - .unwrap() - .source_info - .span; - let node_id = self - .tcx - .hir - .as_local_node_id(self.source.def_id) - .expect("some part of a failing const eval must be local"); - let mut lint = self.tcx.struct_span_lint_node( - ::rustc::lint::builtin::CONST_ERR, - node_id, + self.super_terminator_kind(block, kind, location); + if let TerminatorKind::Assert { expected, msg, cond, .. } = kind { + if let Some(value) = self.eval_operand(cond) { + if Value::ByVal(PrimVal::from_bool(*expected)) != value.0 { + let span = self.mir[block] + .terminator + .as_ref() + .unwrap() + .source_info + .span; + let node_id = self + .tcx + .hir + .as_local_node_id(self.source.def_id) + .expect("some part of a failing const eval must be local"); + let mut lint = self.tcx.struct_span_lint_node( + ::rustc::lint::builtin::CONST_ERR, + node_id, + span, + "constant evaluation error", + ); + use rustc::mir::AssertMessage::*; + match msg { + GeneratorResumedAfterReturn => + lint.span_label(span, "generator resumed after completion"), + GeneratorResumedAfterPanic => + lint.span_label(span, "generator resumed after panicking"), + Math(ref err) => lint.span_label(span, err.description()), + BoundsCheck { ref len, ref index } => { + let len = self.eval_operand(len).expect("len must be const"); + let len = match len.0 { + Value::ByVal(PrimVal::Bytes(n)) => n, + _ => bug!("const len not primitive: {:?}", len), + }; + let index = self + .eval_operand(index) + .expect("index must be const"); + let index = match index.0 { + Value::ByVal(PrimVal::Bytes(n)) => n, + _ => bug!("const index not primitive: {:?}", index), + }; + lint.span_label( span, - "constant evaluation error", - ); - use rustc::mir::AssertMessage::*; - match msg { - GeneratorResumedAfterReturn => - lint.span_label(span, "generator resumed after completion"), - GeneratorResumedAfterPanic => - lint.span_label(span, "generator resumed after panicking"), - Math(ref err) => lint.span_label(span, err.description()), - BoundsCheck { ref len, ref index } => { - let len = self.eval_operand(len).expect("len must be const"); - let len = match len.0 { - Value::ByVal(PrimVal::Bytes(n)) => n, - _ => bug!("const len not primitive: {:?}", len), - }; - let index = self - .eval_operand(index) - .expect("index must be const"); - let index = match index.0 { - Value::ByVal(PrimVal::Bytes(n)) => n, - _ => bug!("const index not primitive: {:?}", index), - }; - lint.span_label( - span, - format!( - "index out of bounds: \ - the len is {} but the index is {}", - len, - index, - ), - ) - }, - }.emit(); - } - } + format!( + "index out of bounds: \ + the len is {} but the index is {}", + len, + index, + ), + ) + }, + }.emit(); } } - // FIXME: do this optimization for function calls - _ => {}, } } } From 05a03d753796e5adf89a97222a1c085ad1cbb904 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 30 Jan 2018 14:32:47 +0100 Subject: [PATCH 072/110] Update tests --- src/test/compile-fail/const-eval-overflow2.rs | 8 ++++++++ src/test/compile-fail/const-eval-overflow2b.rs | 8 ++++++++ src/test/compile-fail/const-eval-overflow2c.rs | 8 ++++++++ 3 files changed, 24 insertions(+) diff --git a/src/test/compile-fail/const-eval-overflow2.rs b/src/test/compile-fail/const-eval-overflow2.rs index aa735cc045364..ef1cdb2814fc7 100644 --- a/src/test/compile-fail/const-eval-overflow2.rs +++ b/src/test/compile-fail/const-eval-overflow2.rs @@ -26,6 +26,7 @@ const VALS_I8: (i8,) = //~| attempt to subtract with overflow ( i8::MIN - 1, + //~^ ERROR constant evaluation error ); const VALS_I16: (i16,) = @@ -33,6 +34,7 @@ const VALS_I16: (i16,) = //~| attempt to subtract with overflow ( i16::MIN - 1, + //~^ ERROR constant evaluation error ); const VALS_I32: (i32,) = @@ -40,6 +42,7 @@ const VALS_I32: (i32,) = //~| attempt to subtract with overflow ( i32::MIN - 1, + //~^ ERROR constant evaluation error ); const VALS_I64: (i64,) = @@ -47,6 +50,7 @@ const VALS_I64: (i64,) = //~| attempt to subtract with overflow ( i64::MIN - 1, + //~^ ERROR constant evaluation error ); const VALS_U8: (u8,) = @@ -54,18 +58,21 @@ const VALS_U8: (u8,) = //~| attempt to subtract with overflow ( u8::MIN - 1, + //~^ ERROR constant evaluation error ); const VALS_U16: (u16,) = ( //~^ ERROR constant evaluation error //~| attempt to subtract with overflow u16::MIN - 1, + //~^ ERROR constant evaluation error ); const VALS_U32: (u32,) = ( //~^ ERROR constant evaluation error //~| attempt to subtract with overflow u32::MIN - 1, + //~^ ERROR constant evaluation error ); const VALS_U64: (u64,) = @@ -73,6 +80,7 @@ const VALS_U64: (u64,) = //~| attempt to subtract with overflow ( u64::MIN - 1, + //~^ ERROR constant evaluation error ); fn main() { diff --git a/src/test/compile-fail/const-eval-overflow2b.rs b/src/test/compile-fail/const-eval-overflow2b.rs index 01c3ea1ff425f..347ba7603e2f3 100644 --- a/src/test/compile-fail/const-eval-overflow2b.rs +++ b/src/test/compile-fail/const-eval-overflow2b.rs @@ -26,6 +26,7 @@ const VALS_I8: (i8,) = //~| attempt to add with overflow ( i8::MAX + 1, + //~^ ERROR constant evaluation error ); const VALS_I16: (i16,) = @@ -33,6 +34,7 @@ const VALS_I16: (i16,) = //~| attempt to add with overflow ( i16::MAX + 1, + //~^ ERROR constant evaluation error ); const VALS_I32: (i32,) = @@ -40,6 +42,7 @@ const VALS_I32: (i32,) = //~| attempt to add with overflow ( i32::MAX + 1, + //~^ ERROR constant evaluation error ); const VALS_I64: (i64,) = @@ -47,6 +50,7 @@ const VALS_I64: (i64,) = //~| attempt to add with overflow ( i64::MAX + 1, + //~^ ERROR constant evaluation error ); const VALS_U8: (u8,) = @@ -54,18 +58,21 @@ const VALS_U8: (u8,) = //~| attempt to add with overflow ( u8::MAX + 1, + //~^ ERROR constant evaluation error ); const VALS_U16: (u16,) = ( //~^ ERROR constant evaluation error //~| attempt to add with overflow u16::MAX + 1, + //~^ ERROR constant evaluation error ); const VALS_U32: (u32,) = ( //~^ ERROR constant evaluation error //~| attempt to add with overflow u32::MAX + 1, + //~^ ERROR constant evaluation error ); const VALS_U64: (u64,) = @@ -73,6 +80,7 @@ const VALS_U64: (u64,) = //~| attempt to add with overflow ( u64::MAX + 1, + //~^ ERROR constant evaluation error ); fn main() { diff --git a/src/test/compile-fail/const-eval-overflow2c.rs b/src/test/compile-fail/const-eval-overflow2c.rs index dd7c151d85f69..5ebbab1b6499d 100644 --- a/src/test/compile-fail/const-eval-overflow2c.rs +++ b/src/test/compile-fail/const-eval-overflow2c.rs @@ -26,6 +26,7 @@ const VALS_I8: (i8,) = //~| attempt to multiply with overflow ( i8::MIN * 2, + //~^ ERROR constant evaluation error ); const VALS_I16: (i16,) = @@ -33,6 +34,7 @@ const VALS_I16: (i16,) = //~| attempt to multiply with overflow ( i16::MIN * 2, + //~^ ERROR constant evaluation error ); const VALS_I32: (i32,) = @@ -40,6 +42,7 @@ const VALS_I32: (i32,) = //~| attempt to multiply with overflow ( i32::MIN * 2, + //~^ ERROR constant evaluation error ); const VALS_I64: (i64,) = @@ -47,6 +50,7 @@ const VALS_I64: (i64,) = //~| attempt to multiply with overflow ( i64::MIN * 2, + //~^ ERROR constant evaluation error ); const VALS_U8: (u8,) = @@ -54,18 +58,21 @@ const VALS_U8: (u8,) = //~| attempt to multiply with overflow ( u8::MAX * 2, + //~^ ERROR constant evaluation error ); const VALS_U16: (u16,) = ( //~^ ERROR constant evaluation error //~| attempt to multiply with overflow u16::MAX * 2, + //~^ ERROR constant evaluation error ); const VALS_U32: (u32,) = ( //~^ ERROR constant evaluation error //~| attempt to multiply with overflow u32::MAX * 2, + //~^ ERROR constant evaluation error ); const VALS_U64: (u64,) = @@ -73,6 +80,7 @@ const VALS_U64: (u64,) = //~| attempt to multiply with overflow ( u64::MAX * 2, + //~^ ERROR constant evaluation error ); fn main() { From dde68dcfe536189ddde835299b59830d9f97b1f0 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 30 Jan 2018 16:38:14 +0100 Subject: [PATCH 073/110] Can only const prop temporaries Variables might error in branches that are not reachable due to the variable value. --- src/librustc_mir/transform/const_prop.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index fc2a149cfe940..09c72c4bf9387 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -286,7 +286,12 @@ impl CanConstProp { found_assignment: IndexVec::from_elem(false, &mir.local_decls), }; for (local, val) in cpv.can_const_prop.iter_enumerated_mut() { - *val = mir.local_kind(local) != LocalKind::Arg; + // cannot use args at all + // cannot use locals because if x < y { y - x } else { x - y } would + // lint for x != y + // FIXME(oli-obk): lint variables until they are used in a condition + // FIXME(oli-obk): lint if return value is constant + *val = mir.local_kind(local) == LocalKind::Temp; } cpv.visit_mir(mir); cpv.can_const_prop From a678044c77e8b341daae3fc7b2f7c74e45cf2c01 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 30 Jan 2018 16:46:37 +0100 Subject: [PATCH 074/110] Add regression test for const prop --- .../no_lint_for_statically_known_error.rs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/test/ui/const-eval/no_lint_for_statically_known_error.rs diff --git a/src/test/ui/const-eval/no_lint_for_statically_known_error.rs b/src/test/ui/const-eval/no_lint_for_statically_known_error.rs new file mode 100644 index 0000000000000..968a7de469107 --- /dev/null +++ b/src/test/ui/const-eval/no_lint_for_statically_known_error.rs @@ -0,0 +1,28 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// must-compile-successfully + +// if `X` were used instead of `x`, `X - 10` would result in a lint. +// This file should never produce a lint, no matter how the const +// propagator is improved. + +#![deny(warnings)] + +const X: u32 = 5; + +fn main() { + let x = X; + if x > 10 { + println!("{}", x - 10); + } else { + println!("{}", 10 - x); + } +} From 64fbf5d5adc80aa23006cc7ed159ffa278639bc3 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 31 Jan 2018 09:31:24 +0100 Subject: [PATCH 075/110] Compute the ParamEnv only once and use it to call tcx.const_eval --- src/librustc_mir/interpret/const_eval.rs | 18 +++++++------ src/librustc_mir/transform/const_prop.rs | 34 +++++++++++++++++------- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index e216f5c4d8a82..b141106aa60f5 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -61,7 +61,7 @@ pub fn eval_body_with_mir<'a, 'mir, 'tcx>( mir: &'mir mir::Mir<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> Option<(Value, Pointer, Ty<'tcx>)> { - let (res, ecx) = eval_body_and_ecx(tcx, cid, Some(mir), param_env); + let (res, ecx, _) = eval_body_and_ecx(tcx, cid, Some(mir), param_env); match res { Ok(val) => Some(val), Err(mut err) => { @@ -76,7 +76,7 @@ pub fn eval_body<'a, 'tcx>( cid: GlobalId<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> Option<(Value, Pointer, Ty<'tcx>)> { - let (res, ecx) = eval_body_and_ecx(tcx, cid, None, param_env); + let (res, ecx, _) = eval_body_and_ecx(tcx, cid, None, param_env); match res { Ok(val) => Some(val), Err(mut err) => { @@ -91,9 +91,12 @@ fn eval_body_and_ecx<'a, 'mir, 'tcx>( cid: GlobalId<'tcx>, mir: Option<&'mir mir::Mir<'tcx>>, param_env: ty::ParamEnv<'tcx>, -) -> (EvalResult<'tcx, (Value, Pointer, Ty<'tcx>)>, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>) { +) -> (EvalResult<'tcx, (Value, Pointer, Ty<'tcx>)>, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>, Span) { debug!("eval_body: {:?}, {:?}", cid, param_env); let mut ecx = EvalContext::new(tcx, param_env, CompileTimeEvaluator, ()); + // we start out with the best span we have + // and try improving it down the road when more information is available + let mut span = tcx.def_span(cid.instance.def_id()); let res = (|| { let mut mir = match mir { Some(mir) => mir, @@ -102,6 +105,7 @@ fn eval_body_and_ecx<'a, 'mir, 'tcx>( if let Some(index) = cid.promoted { mir = &mir.promoted[index]; } + span = mir.span; let layout = ecx.layout_of(mir.return_ty().subst(tcx, cid.instance.substs))?; let alloc = tcx.interpret_interner.get_cached(cid.instance.def_id()); let alloc = match alloc { @@ -120,8 +124,7 @@ fn eval_body_and_ecx<'a, 'mir, 'tcx>( if tcx.is_static(cid.instance.def_id()).is_some() { tcx.interpret_interner.cache(cid.instance.def_id(), ptr.alloc_id); } - let span = tcx.def_span(cid.instance.def_id()); - let internally_mutable = !layout.ty.is_freeze(tcx, param_env, span); + let internally_mutable = !layout.ty.is_freeze(tcx, param_env, mir.span); let mutability = tcx.is_static(cid.instance.def_id()); let mutability = if mutability == Some(hir::Mutability::MutMutable) || internally_mutable { Mutability::Mutable @@ -152,7 +155,7 @@ fn eval_body_and_ecx<'a, 'mir, 'tcx>( }; Ok((value, ptr, layout.ty)) })(); - (res, ecx) + (res, ecx, span) } pub struct CompileTimeEvaluator; @@ -499,7 +502,7 @@ pub fn const_eval_provider<'a, 'tcx>( } }; - let (res, ecx) = eval_body_and_ecx(tcx, cid, None, key.param_env); + let (res, ecx, span) = eval_body_and_ecx(tcx, cid, None, key.param_env); res.map(|(miri_value, _, miri_ty)| { tcx.mk_const(ty::Const { val: ConstVal::Value(miri_value), @@ -509,7 +512,6 @@ pub fn const_eval_provider<'a, 'tcx>( if tcx.is_static(def_id).is_some() { ecx.report(&mut err, true, None); } - let span = ecx.frame().span; ConstEvalErr { kind: err.into(), span, diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 09c72c4bf9387..d3ab494139a50 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -21,11 +21,12 @@ use rustc::ty::layout::LayoutOf; use rustc::middle::const_val::ConstVal; use rustc::ty::{TyCtxt, self, Instance}; use rustc::mir::interpret::{Value, PrimVal, GlobalId}; -use interpret::{eval_body_with_mir, eval_body, mk_borrowck_eval_cx, unary_op, ValTy}; +use interpret::{eval_body_with_mir, mk_borrowck_eval_cx, unary_op, ValTy}; use transform::{MirPass, MirSource}; use syntax::codemap::Span; use rustc::ty::subst::Substs; use rustc_data_structures::indexed_vec::IndexVec; +use rustc::ty::ParamEnv; pub struct ConstProp; @@ -56,6 +57,7 @@ struct ConstPropagator<'b, 'a, 'tcx:'a+'b> { source: MirSource, places: IndexVec>>, can_const_prop: IndexVec, + param_env: ParamEnv<'tcx>, } impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { @@ -64,24 +66,40 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { tcx: TyCtxt<'a, 'tcx, 'tcx>, source: MirSource, ) -> ConstPropagator<'b, 'a, 'tcx> { + let param_env = tcx.param_env(source.def_id); ConstPropagator { mir, tcx, source, + param_env, can_const_prop: CanConstProp::check(mir), places: IndexVec::from_elem(None, &mir.local_decls), } } + fn const_eval(&self, cid: GlobalId<'tcx>, span: Span) -> Option> { + let value = match self.tcx.const_eval(self.param_env.and(cid)) { + Ok(val) => val, + // FIXME: report some errors + Err(_) => return None, + }; + let val = match value.val { + ConstVal::Value(v) => v, + _ => bug!("eval produced: {:?}", value), + }; + let val = (val, value.ty, span); + trace!("evaluated {:?} to {:?}", cid, val); + Some(val) + } + fn eval_constant(&mut self, c: &Constant<'tcx>) -> Option> { match c.literal { Literal::Value { value } => match value.val { ConstVal::Value(v) => Some((v, value.ty, c.span)), ConstVal::Unevaluated(did, substs) => { - let param_env = self.tcx.param_env(self.source.def_id); let instance = Instance::resolve( self.tcx, - param_env, + self.param_env, did, substs, )?; @@ -89,10 +107,7 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { instance, promoted: None, }; - let (value, _, ty) = eval_body(self.tcx, cid, param_env)?; - let val = (value, ty, c.span); - trace!("evaluated {:?} to {:?}", c, val); - Some(val) + self.const_eval(cid, c.span) }, }, // evaluate the promoted and replace the constant with the evaluated result @@ -108,8 +123,9 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { instance, promoted: Some(index), }; - let param_env = self.tcx.param_env(self.source.def_id); - let (value, _, ty) = eval_body_with_mir(self.tcx, cid, self.mir, param_env)?; + // cannot use `const_eval` here, because that would require having the MIR + // for the current function available, but we're producing said MIR right now + let (value, _, ty) = eval_body_with_mir(self.tcx, cid, self.mir, self.param_env)?; let val = (value, ty, c.span); trace!("evaluated {:?} to {:?}", c, val); Some(val) From d2f363ba89f41f3abb1e85d44eaae93ea338d3a4 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 31 Jan 2018 09:57:25 +0100 Subject: [PATCH 076/110] Report all errors other than those about insufficient type information --- src/librustc_mir/transform/const_prop.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index d3ab494139a50..9dee1e1633e02 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -80,8 +80,10 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { fn const_eval(&self, cid: GlobalId<'tcx>, span: Span) -> Option> { let value = match self.tcx.const_eval(self.param_env.and(cid)) { Ok(val) => val, - // FIXME: report some errors - Err(_) => return None, + Err(err) => { + err.report(self.tcx, span, "const prop"); + return None; + }, }; let val = match value.val { ConstVal::Value(v) => v, From 8c53d54b98edd77e32fbf4f4fd27d312b251ec6b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 31 Jan 2018 10:20:30 +0100 Subject: [PATCH 077/110] Update tests --- src/librustc_mir/transform/const_prop.rs | 2 +- src/test/compile-fail/const-err-multi.rs | 3 +++ src/test/compile-fail/const-err.rs | 1 + src/test/compile-fail/const-eval-overflow2.rs | 16 ++++++++-------- src/test/compile-fail/const-eval-overflow2b.rs | 16 ++++++++-------- src/test/compile-fail/const-eval-overflow2c.rs | 16 ++++++++-------- src/test/ui/infinite-recursion-const-fn.rs | 4 ++-- 7 files changed, 31 insertions(+), 27 deletions(-) diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 9dee1e1633e02..e0203953fb2a4 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -81,7 +81,7 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { let value = match self.tcx.const_eval(self.param_env.and(cid)) { Ok(val) => val, Err(err) => { - err.report(self.tcx, span, "const prop"); + err.report(self.tcx, span, "constant propagated"); return None; }, }; diff --git a/src/test/compile-fail/const-err-multi.rs b/src/test/compile-fail/const-err-multi.rs index 1e3150a74d985..5dd8651005df8 100644 --- a/src/test/compile-fail/const-err-multi.rs +++ b/src/test/compile-fail/const-err-multi.rs @@ -14,6 +14,9 @@ pub const A: i8 = -std::i8::MIN; //~^ ERROR E0080 //~| ERROR const_err //~| ERROR const_err +//~| ERROR constant evaluation error +//~| ERROR constant evaluation error +//~| ERROR constant evaluation error //~| ERROR E0080 pub const B: i8 = A; //~^ ERROR E0080 diff --git a/src/test/compile-fail/const-err.rs b/src/test/compile-fail/const-err.rs index 5f55cc82e6d83..b0cf639cd762f 100644 --- a/src/test/compile-fail/const-err.rs +++ b/src/test/compile-fail/const-err.rs @@ -23,6 +23,7 @@ fn black_box(_: T) { // Make sure that the two uses get two errors. const FOO: u8 = [5u8][1]; //~^ ERROR constant evaluation error +//~| ERROR constant evaluation error //~| index out of bounds: the len is 1 but the index is 1 //~| ERROR E0080 diff --git a/src/test/compile-fail/const-eval-overflow2.rs b/src/test/compile-fail/const-eval-overflow2.rs index ef1cdb2814fc7..d715847e03fe3 100644 --- a/src/test/compile-fail/const-eval-overflow2.rs +++ b/src/test/compile-fail/const-eval-overflow2.rs @@ -23,64 +23,64 @@ use std::{u8, u16, u32, u64, usize}; const VALS_I8: (i8,) = //~^ ERROR constant evaluation error + //~| ERROR constant evaluation error //~| attempt to subtract with overflow ( i8::MIN - 1, - //~^ ERROR constant evaluation error ); const VALS_I16: (i16,) = //~^ ERROR constant evaluation error + //~| ERROR constant evaluation error //~| attempt to subtract with overflow ( i16::MIN - 1, - //~^ ERROR constant evaluation error ); const VALS_I32: (i32,) = //~^ ERROR constant evaluation error + //~| ERROR constant evaluation error //~| attempt to subtract with overflow ( i32::MIN - 1, - //~^ ERROR constant evaluation error ); const VALS_I64: (i64,) = //~^ ERROR constant evaluation error + //~| ERROR constant evaluation error //~| attempt to subtract with overflow ( i64::MIN - 1, - //~^ ERROR constant evaluation error ); const VALS_U8: (u8,) = //~^ ERROR constant evaluation error + //~| ERROR constant evaluation error //~| attempt to subtract with overflow ( u8::MIN - 1, - //~^ ERROR constant evaluation error ); const VALS_U16: (u16,) = ( //~^ ERROR constant evaluation error + //~| ERROR constant evaluation error //~| attempt to subtract with overflow u16::MIN - 1, - //~^ ERROR constant evaluation error ); const VALS_U32: (u32,) = ( //~^ ERROR constant evaluation error + //~| ERROR constant evaluation error //~| attempt to subtract with overflow u32::MIN - 1, - //~^ ERROR constant evaluation error ); const VALS_U64: (u64,) = //~^ ERROR constant evaluation error + //~| ERROR constant evaluation error //~| attempt to subtract with overflow ( u64::MIN - 1, - //~^ ERROR constant evaluation error ); fn main() { diff --git a/src/test/compile-fail/const-eval-overflow2b.rs b/src/test/compile-fail/const-eval-overflow2b.rs index 347ba7603e2f3..f6463900eeb53 100644 --- a/src/test/compile-fail/const-eval-overflow2b.rs +++ b/src/test/compile-fail/const-eval-overflow2b.rs @@ -23,64 +23,64 @@ use std::{u8, u16, u32, u64, usize}; const VALS_I8: (i8,) = //~^ ERROR constant evaluation error + //~| ERROR constant evaluation error //~| attempt to add with overflow ( i8::MAX + 1, - //~^ ERROR constant evaluation error ); const VALS_I16: (i16,) = //~^ ERROR constant evaluation error + //~| ERROR constant evaluation error //~| attempt to add with overflow ( i16::MAX + 1, - //~^ ERROR constant evaluation error ); const VALS_I32: (i32,) = //~^ ERROR constant evaluation error + //~| ERROR constant evaluation error //~| attempt to add with overflow ( i32::MAX + 1, - //~^ ERROR constant evaluation error ); const VALS_I64: (i64,) = //~^ ERROR constant evaluation error + //~| ERROR constant evaluation error //~| attempt to add with overflow ( i64::MAX + 1, - //~^ ERROR constant evaluation error ); const VALS_U8: (u8,) = //~^ ERROR constant evaluation error + //~| ERROR constant evaluation error //~| attempt to add with overflow ( u8::MAX + 1, - //~^ ERROR constant evaluation error ); const VALS_U16: (u16,) = ( //~^ ERROR constant evaluation error + //~| ERROR constant evaluation error //~| attempt to add with overflow u16::MAX + 1, - //~^ ERROR constant evaluation error ); const VALS_U32: (u32,) = ( //~^ ERROR constant evaluation error + //~| ERROR constant evaluation error //~| attempt to add with overflow u32::MAX + 1, - //~^ ERROR constant evaluation error ); const VALS_U64: (u64,) = //~^ ERROR constant evaluation error + //~| ERROR constant evaluation error //~| attempt to add with overflow ( u64::MAX + 1, - //~^ ERROR constant evaluation error ); fn main() { diff --git a/src/test/compile-fail/const-eval-overflow2c.rs b/src/test/compile-fail/const-eval-overflow2c.rs index 5ebbab1b6499d..8665b9d97a84d 100644 --- a/src/test/compile-fail/const-eval-overflow2c.rs +++ b/src/test/compile-fail/const-eval-overflow2c.rs @@ -23,64 +23,64 @@ use std::{u8, u16, u32, u64, usize}; const VALS_I8: (i8,) = //~^ ERROR constant evaluation error + //~| ERROR constant evaluation error //~| attempt to multiply with overflow ( i8::MIN * 2, - //~^ ERROR constant evaluation error ); const VALS_I16: (i16,) = //~^ ERROR constant evaluation error + //~| ERROR constant evaluation error //~| attempt to multiply with overflow ( i16::MIN * 2, - //~^ ERROR constant evaluation error ); const VALS_I32: (i32,) = //~^ ERROR constant evaluation error + //~| ERROR constant evaluation error //~| attempt to multiply with overflow ( i32::MIN * 2, - //~^ ERROR constant evaluation error ); const VALS_I64: (i64,) = //~^ ERROR constant evaluation error + //~| ERROR constant evaluation error //~| attempt to multiply with overflow ( i64::MIN * 2, - //~^ ERROR constant evaluation error ); const VALS_U8: (u8,) = //~^ ERROR constant evaluation error + //~| ERROR constant evaluation error //~| attempt to multiply with overflow ( u8::MAX * 2, - //~^ ERROR constant evaluation error ); const VALS_U16: (u16,) = ( //~^ ERROR constant evaluation error + //~| ERROR constant evaluation error //~| attempt to multiply with overflow u16::MAX * 2, - //~^ ERROR constant evaluation error ); const VALS_U32: (u32,) = ( //~^ ERROR constant evaluation error + //~| ERROR constant evaluation error //~| attempt to multiply with overflow u32::MAX * 2, - //~^ ERROR constant evaluation error ); const VALS_U64: (u64,) = //~^ ERROR constant evaluation error + //~| ERROR constant evaluation error //~| attempt to multiply with overflow ( u64::MAX * 2, - //~^ ERROR constant evaluation error ); fn main() { diff --git a/src/test/ui/infinite-recursion-const-fn.rs b/src/test/ui/infinite-recursion-const-fn.rs index f98074bc554bb..05e40abdc0f2f 100644 --- a/src/test/ui/infinite-recursion-const-fn.rs +++ b/src/test/ui/infinite-recursion-const-fn.rs @@ -11,8 +11,8 @@ //https://github.com/rust-lang/rust/issues/31364 #![feature(const_fn)] -const fn a() -> usize { b() } //~ ERROR constant evaluation error +const fn a() -> usize { b() } const fn b() -> usize { a() } -const ARR: [i32; a()] = [5; 6]; +const ARR: [i32; a()] = [5; 6]; //~ ERROR constant evaluation error fn main(){} From bd03371f711adcb7724c939ea053f4090e87c16f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 31 Jan 2018 10:39:30 +0100 Subject: [PATCH 078/110] Add stack traces to miri errors --- src/librustc/ich/impls_ty.rs | 14 ++++++++--- src/librustc/middle/const_val.rs | 34 ++++++++++++++++---------- src/librustc/mir/interpret/error.rs | 9 +++---- src/librustc/traits/error_reporting.rs | 2 +- src/librustc/traits/fulfill.rs | 2 +- src/librustc/ty/structural_impls.rs | 10 ++++---- 6 files changed, 43 insertions(+), 28 deletions(-) diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 72bc5af3b7e2d..a398549f35930 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -440,6 +440,11 @@ impl_stable_hash_for!(struct ::middle::const_val::ConstEvalErr<'tcx> { kind }); +impl_stable_hash_for!(struct ::middle::const_val::FrameInfo { + span, + location +}); + impl<'a, 'gcx> HashStable> for ::middle::const_val::ErrKind<'gcx> { fn hash_stable(&self, @@ -468,7 +473,10 @@ for ::middle::const_val::ErrKind<'gcx> { LayoutError(ref layout_error) => { layout_error.hash_stable(hcx, hasher); } - Miri(ref err) => err.hash_stable(hcx, hasher), + Miri(ref err, ref trace) => { + err.hash_stable(hcx, hasher); + trace.hash_stable(hcx, hasher); + }, } } } @@ -489,9 +497,9 @@ for ::mir::interpret::EvalError<'gcx> { hasher: &mut StableHasher) { use mir::interpret::EvalErrorKind::*; - mem::discriminant(&*self.kind).hash_stable(hcx, hasher); + mem::discriminant(&self.kind).hash_stable(hcx, hasher); - match *self.kind { + match self.kind { DanglingPointerDeref | DoubleFree | InvalidMemoryAccess | diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index c987ed0ea5d3f..d6d23c5cad8ad 100644 --- a/src/librustc/middle/const_val.rs +++ b/src/librustc/middle/const_val.rs @@ -19,6 +19,7 @@ use graphviz::IntoCow; use syntax_pos::Span; use std::borrow::Cow; +use std::rc::Rc; pub type EvalResult<'tcx> = Result<&'tcx ty::Const<'tcx>, ConstEvalErr<'tcx>>; @@ -51,7 +52,7 @@ impl<'tcx> ConstVal<'tcx> { #[derive(Clone, Debug)] pub struct ConstEvalErr<'tcx> { pub span: Span, - pub kind: ErrKind<'tcx>, + pub kind: Rc>, } #[derive(Clone, Debug)] @@ -66,13 +67,13 @@ pub enum ErrKind<'tcx> { TypeckError, CheckMatchError, - Miri(::mir::interpret::EvalError<'tcx>), + Miri(::mir::interpret::EvalError<'tcx>, Vec), } -impl<'tcx> From<::mir::interpret::EvalError<'tcx>> for ErrKind<'tcx> { - fn from(err: ::mir::interpret::EvalError<'tcx>) -> ErrKind<'tcx> { - ErrKind::Miri(err) - } +#[derive(Clone, Debug)] +pub struct FrameInfo { + pub span: Span, + pub location: String, } impl<'tcx> From for ErrKind<'tcx> { @@ -85,21 +86,23 @@ impl<'tcx> From for ErrKind<'tcx> { } #[derive(Clone, Debug)] -pub enum ConstEvalErrDescription<'a> { +pub enum ConstEvalErrDescription<'a, 'tcx: 'a> { Simple(Cow<'a, str>), + Backtrace(&'a ::mir::interpret::EvalError<'tcx>, &'a [FrameInfo]), } -impl<'a> ConstEvalErrDescription<'a> { +impl<'a, 'tcx> ConstEvalErrDescription<'a, 'tcx> { /// Return a one-line description of the error, for lints and such pub fn into_oneline(self) -> Cow<'a, str> { match self { ConstEvalErrDescription::Simple(simple) => simple, + ConstEvalErrDescription::Backtrace(miri, _) => format!("{}", miri).into_cow(), } } } impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { - pub fn description(&self) -> ConstEvalErrDescription { + pub fn description(&'a self) -> ConstEvalErrDescription<'a, 'tcx> { use self::ErrKind::*; use self::ConstEvalErrDescription::*; @@ -110,7 +113,7 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { }) } - match self.kind { + match *self.kind { NonConstPath => simple!("non-constant path in constant expression"), UnimplementedConstVal(what) => simple!("unimplemented constant expression: {}", what), @@ -124,8 +127,7 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { TypeckError => simple!("type-checking failed"), CheckMatchError => simple!("match-checking failed"), - // FIXME: report a full backtrace - Miri(ref err) => simple!("{}", err), + Miri(ref err, ref trace) => Backtrace(err, trace), } } @@ -150,6 +152,12 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { ConstEvalErrDescription::Simple(message) => { diag.span_label(self.span, message); } + ConstEvalErrDescription::Backtrace(miri, frames) => { + diag.span_label(self.span, format!("{}", miri)); + for frame in frames { + diag.span_label(frame.span, format!("inside call to {}", frame.location)); + } + } } if !primary_span.contains(self.span) { @@ -163,7 +171,7 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { primary_span: Span, primary_kind: &str) { - match self.kind { + match *self.kind { ErrKind::TypeckError | ErrKind::CheckMatchError => return, _ => {} } diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs index 3e8aeaff57e62..90d10df151530 100644 --- a/src/librustc/mir/interpret/error.rs +++ b/src/librustc/mir/interpret/error.rs @@ -1,6 +1,5 @@ use std::error::Error; use std::{fmt, env}; -use std::rc::Rc; use mir; use ty::{FnSig, Ty, layout}; @@ -15,7 +14,7 @@ use backtrace::Backtrace; #[derive(Debug, Clone)] pub struct EvalError<'tcx> { - pub kind: Rc>, + pub kind: EvalErrorKind<'tcx>, pub backtrace: Option, } @@ -26,7 +25,7 @@ impl<'tcx> From> for EvalError<'tcx> { _ => None }; EvalError { - kind: Rc::new(kind), + kind, backtrace, } } @@ -132,7 +131,7 @@ pub type EvalResult<'tcx, T = ()> = Result>; impl<'tcx> Error for EvalError<'tcx> { fn description(&self) -> &str { use self::EvalErrorKind::*; - match *self.kind { + match self.kind { MachineError(ref inner) => inner, FunctionPointerTyMismatch(..) => "tried to call a function through a function pointer of a different type", @@ -253,7 +252,7 @@ impl<'tcx> Error for EvalError<'tcx> { impl<'tcx> fmt::Display for EvalError<'tcx> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::EvalErrorKind::*; - match *self.kind { + match self.kind { PointerOutOfBounds { ptr, access, allocation_size } => { write!(f, "{} at offset {}, outside bounds of allocation {} which has size {}", if access { "memory access" } else { "pointer computed" }, diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 2e554aff13ee3..cd2d0d7e2a043 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -775,7 +775,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } ConstEvalFailure(ref err) => { - if let ::middle::const_val::ErrKind::TypeckError = err.kind { + if let ::middle::const_val::ErrKind::TypeckError = *err.kind { return; } err.struct_error(self.tcx, span, "constant expression") diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index 5c082b0610fe1..15925a19d75a2 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -536,7 +536,7 @@ fn process_predicate<'a, 'gcx, 'tcx>( } else { Err(CodeSelectionError(ConstEvalFailure(ConstEvalErr { span: obligation.cause.span, - kind: ErrKind::UnimplementedConstVal("could not resolve"), + kind: ErrKind::UnimplementedConstVal("could not resolve").into(), }))) } }, diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 4e1f3664d7f85..c1a4f5ef45884 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -578,10 +578,10 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> { impl<'a, 'tcx> Lift<'tcx> for ConstEvalErr<'a> { type Lifted = ConstEvalErr<'tcx>; fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option { - tcx.lift(&self.kind).map(|kind| { + tcx.lift(&*self.kind).map(|kind| { ConstEvalErr { span: self.span, - kind, + kind: Rc::new(kind), } }) } @@ -591,7 +591,7 @@ impl<'a, 'tcx> Lift<'tcx> for interpret::EvalError<'a> { type Lifted = interpret::EvalError<'tcx>; fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option { use ::mir::interpret::EvalErrorKind::*; - let kind = match *self.kind { + let kind = match self.kind { MachineError(ref err) => MachineError(err.clone()), FunctionPointerTyMismatch(a, b) => FunctionPointerTyMismatch( tcx.lift(&a)?, @@ -691,7 +691,7 @@ impl<'a, 'tcx> Lift<'tcx> for interpret::EvalError<'a> { TypeckError => TypeckError, }; Some(interpret::EvalError { - kind: Rc::new(kind), + kind: kind, backtrace: self.backtrace.clone(), }) } @@ -714,7 +714,7 @@ impl<'a, 'tcx> Lift<'tcx> for const_val::ErrKind<'a> { TypeckError => TypeckError, CheckMatchError => CheckMatchError, - Miri(ref e) => return tcx.lift(e).map(Miri), + Miri(ref e, ref frames) => return tcx.lift(e).map(|e| Miri(e, frames.clone())), }) } } From 8500c2fab2a7e93a4a918417c4f01d136247ef89 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 31 Jan 2018 15:06:45 +0100 Subject: [PATCH 079/110] Const eval error refactoring --- src/librustc/ich/impls_ty.rs | 1 + src/librustc/middle/const_val.rs | 7 ++ src/librustc/mir/interpret/error.rs | 5 ++ src/librustc/ty/structural_impls.rs | 1 + src/librustc_mir/interpret/const_eval.rs | 92 ++++++++++------------ src/librustc_mir/interpret/eval_context.rs | 87 ++++++++++++-------- 6 files changed, 109 insertions(+), 84 deletions(-) diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index a398549f35930..d5476f742c896 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -531,6 +531,7 @@ for ::mir::interpret::EvalError<'gcx> { TypeckError | DerefFunctionPointer | ExecuteMemory | + ReferencedConstant | OverflowingMath => {} MachineError(ref err) => err.hash_stable(hcx, hasher), FunctionPointerTyMismatch(a, b) => { diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index d6d23c5cad8ad..151cc9b1ce2ab 100644 --- a/src/librustc/middle/const_val.rs +++ b/src/librustc/middle/const_val.rs @@ -173,6 +173,13 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { { match *self.kind { ErrKind::TypeckError | ErrKind::CheckMatchError => return, + ErrKind::Miri(ref miri, _) => { + match miri.kind { + ::mir::interpret::EvalErrorKind::TypeckError | + ::mir::interpret::EvalErrorKind::Layout(_) => return, + _ => {}, + } + } _ => {} } self.struct_error(tcx, primary_span, primary_kind).emit(); diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs index 90d10df151530..bb27628fa9c73 100644 --- a/src/librustc/mir/interpret/error.rs +++ b/src/librustc/mir/interpret/error.rs @@ -124,6 +124,9 @@ pub enum EvalErrorKind<'tcx> { UnimplementedTraitSelection, /// Abort in case type errors are reached TypeckError, + /// Cannot compute this constant because it depends on another one + /// which already produced an error + ReferencedConstant, } pub type EvalResult<'tcx, T = ()> = Result>; @@ -245,6 +248,8 @@ impl<'tcx> Error for EvalError<'tcx> { "there were unresolved type arguments during trait selection", TypeckError => "encountered constants with type errors, stopping evaluation", + ReferencedConstant => + "referenced constant has errors", } } } diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index c1a4f5ef45884..243e17ee5c7ec 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -689,6 +689,7 @@ impl<'a, 'tcx> Lift<'tcx> for interpret::EvalError<'a> { PathNotFound(ref v) => PathNotFound(v.clone()), UnimplementedTraitSelection => UnimplementedTraitSelection, TypeckError => TypeckError, + ReferencedConstant => ReferencedConstant, }; Some(interpret::EvalError { kind: kind, diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index b141106aa60f5..a4100a9d28af6 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -1,7 +1,6 @@ use rustc::hir; use rustc::middle::const_val::{ConstEvalErr, ConstVal, ErrKind}; use rustc::middle::const_val::ErrKind::{TypeckError, CheckMatchError}; -use rustc::traits; use rustc::mir; use rustc::ty::{self, TyCtxt, Ty, Instance}; use rustc::ty::layout::{self, LayoutOf}; @@ -15,6 +14,7 @@ use super::{Place, EvalContext, StackPopCleanup, ValTy, PlaceExtra, Memory}; use std::fmt; use std::error::Error; +use std::rc::Rc; pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, @@ -236,7 +236,7 @@ impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator { let mir = match ecx.load_mir(instance.def) { Ok(mir) => mir, Err(err) => { - if let EvalErrorKind::NoMirFor(ref path) = *err.kind { + if let EvalErrorKind::NoMirFor(ref path) = err.kind { return Err( ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path)) .into(), @@ -333,15 +333,8 @@ impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator { ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, cid: GlobalId<'tcx>, ) -> EvalResult<'tcx, AllocId> { - let param_env = ty::ParamEnv::empty(traits::Reveal::All); // ensure the static is computed - if let Err(err) = ecx.tcx.const_eval(param_env.and(cid)) { - match err.kind { - ErrKind::Miri(miri) => return Err(miri), - ErrKind::TypeckError => return err!(TypeckError), - other => bug!("const eval returned {:?}", other), - } - }; + ecx.const_eval(cid)?; Ok(ecx .tcx .interpret_interner @@ -377,52 +370,47 @@ pub fn const_val_field<'a, 'tcx>( span: Span, variant: Option, field: mir::Field, - val: Value, + value: Value, ty: Ty<'tcx>, ) -> ::rustc::middle::const_val::EvalResult<'tcx> { - match const_val_field_inner(tcx, param_env, instance, variant, field, val, ty) { + trace!("const_val_field: {:?}, {:?}, {:?}, {:?}", instance, field, value, ty); + let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap(); + let result = (|| { + let (mut field, ty) = match value { + 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, + align, + extra: variant.map_or(PlaceExtra::None, PlaceExtra::DowncastVariant), + }; + let layout = ecx.layout_of(ty)?; + let (place, layout) = ecx.place_field(place, field, layout)?; + let (ptr, align) = place.to_ptr_align(); + (Value::ByRef(ptr, align), layout.ty) + } + }; + if let Value::ByRef(ptr, align) = field { + if let Some(val) = ecx.try_read_value(ptr, align, ty)? { + field = val; + } + } + Ok((field, ty)) + })(); + match result { Ok((field, ty)) => Ok(tcx.mk_const(ty::Const { val: ConstVal::Value(field), ty, })), - Err(err) => Err(ConstEvalErr { - span, - kind: err.into(), - }), - } -} - -fn const_val_field_inner<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - instance: ty::Instance<'tcx>, - variant: Option, - field: mir::Field, - value: Value, - ty: Ty<'tcx>, -) -> EvalResult<'tcx, (Value, Ty<'tcx>)> { - trace!("const_val_field: {:?}, {:?}, {:?}, {:?}", instance, field, value, ty); - let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap(); - let (mut field, ty) = match value { - Value::ByValPair(..) | Value::ByVal(_) => ecx.read_field(value, variant, field, ty)?.expect("const_val_field on non-field"), - Value::ByRef(ptr, align) => { - let place = Place::Ptr { - ptr, - align, - extra: variant.map_or(PlaceExtra::None, PlaceExtra::DowncastVariant), - }; - let layout = ecx.layout_of(ty)?; - let (place, layout) = ecx.place_field(place, field, layout)?; - let (ptr, align) = place.to_ptr_align(); - (Value::ByRef(ptr, align), layout.ty) - } - }; - if let Value::ByRef(ptr, align) = field { - if let Some(val) = ecx.try_read_value(ptr, align, ty)? { - field = val; - } + Err(err) => { + let trace = ecx.generate_stacktrace(None); + let err = ErrKind::Miri(err, trace); + Err(ConstEvalErr { + kind: err.into(), + span, + }) + }, } - Ok((field, ty)) } pub fn const_discr<'a, 'tcx>( @@ -484,7 +472,7 @@ pub fn const_eval_provider<'a, 'tcx>( // Do match-check before building MIR if tcx.check_match(def_id).is_err() { return Err(ConstEvalErr { - kind: CheckMatchError, + kind: Rc::new(CheckMatchError), span, }); } @@ -496,7 +484,7 @@ pub fn const_eval_provider<'a, 'tcx>( // Do not continue into miri if typeck errors occurred; it will fail horribly if tables.tainted_by_errors { return Err(ConstEvalErr { - kind: TypeckError, + kind: Rc::new(TypeckError), span, }); } @@ -512,6 +500,8 @@ pub fn const_eval_provider<'a, 'tcx>( if tcx.is_static(def_id).is_some() { ecx.report(&mut err, true, None); } + let trace = ecx.generate_stacktrace(None); + let err = ErrKind::Miri(err, trace); ConstEvalErr { kind: err.into(), span, diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 676f99ea6be0c..483649204fbec 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -10,6 +10,7 @@ use rustc::ty::layout::{self, Size, Align, HasDataLayout, LayoutOf, TyLayout}; use rustc::ty::subst::{Subst, Substs}; use rustc::ty::{self, Ty, TyCtxt}; use rustc_data_structures::indexed_vec::Idx; +use rustc::middle::const_val::FrameInfo; use syntax::codemap::{self, DUMMY_SP, Span}; use syntax::ast::Mutability; use rustc::mir::interpret::{ @@ -934,17 +935,28 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M return Ok(Value::ByRef(ptr.into(), layout.align)) } } - let cv = match self.tcx.const_eval(self.param_env.and(gid)) { - Ok(val) => val, - Err(err) => match err.kind { - ErrKind::Miri(miri) => return Err(miri), - ErrKind::TypeckError => return err!(TypeckError), - other => bug!("const eval returned {:?}", other), - }, - }; + let cv = self.const_eval(gid)?; self.const_to_value(&cv.val, ty) } + pub fn const_eval(&self, gid: GlobalId<'tcx>) -> EvalResult<'tcx, &'tcx ty::Const<'tcx>> { + let param_env = if self.tcx.is_static(gid.instance.def_id()).is_some() { + use rustc::traits; + ty::ParamEnv::empty(traits::Reveal::All) + } else { + self.param_env + }; + self.tcx.const_eval(param_env.and(gid)).map_err(|err| match *err.kind { + ErrKind::Miri(ref err, _) => match err.kind { + EvalErrorKind::TypeckError | + EvalErrorKind::Layout(_) => EvalErrorKind::TypeckError.into(), + _ => EvalErrorKind::ReferencedConstant.into(), + }, + ErrKind::TypeckError => EvalErrorKind::TypeckError.into(), + ref other => bug!("const eval returned {:?}", other), + }) + } + pub fn force_allocation(&mut self, place: Place) -> EvalResult<'tcx, Place> { let new_place = match place { Place::Local { frame, local } => { @@ -1496,7 +1508,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M match self.stack[frame].get_local(local) { Err(err) => { - if let EvalErrorKind::DeadLocal = *err.kind { + if let EvalErrorKind::DeadLocal = err.kind { write!(msg, " is dead").unwrap(); } else { panic!("Failed to access local: {:?}", err); @@ -1558,9 +1570,38 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M Ok(()) } + pub fn generate_stacktrace(&self, explicit_span: Option) -> Vec { + let mut last_span = None; + let mut frames = Vec::new(); + // skip 1 because the last frame is just the environment of the constant + for &Frame { instance, span, .. } in self.stack().iter().skip(1).rev() { + // make sure we don't emit frames that are duplicates of the previous + if explicit_span == Some(span) { + last_span = Some(span); + continue; + } + if let Some(last) = last_span { + if last == span { + continue; + } + } else { + last_span = Some(span); + } + let location = if self.tcx.def_key(instance.def_id()).disambiguated_data.data == DefPathData::ClosureExpr { + "closure".to_owned() + } else { + instance.to_string() + }; + frames.push(FrameInfo { span, location }); + } + frames + } + pub fn report(&self, e: &mut EvalError, as_err: bool, explicit_span: Option) { - if let EvalErrorKind::TypeckError = *e.kind { - return; + match e.kind { + EvalErrorKind::Layout(_) | + EvalErrorKind::TypeckError => return, + _ => {}, } if let Some(ref mut backtrace) = e.backtrace { let mut trace_text = "\n\nAn error occurred in miri:\n".to_string(); @@ -1618,28 +1659,8 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M ) }; err.span_label(span, e.to_string()); - let mut last_span = None; - // skip 1 because the last frame is just the environment of the constant - for &Frame { instance, span, .. } in self.stack().iter().skip(1).rev() { - // make sure we don't emit frames that are duplicates of the previous - if explicit_span == Some(span) { - last_span = Some(span); - continue; - } - if let Some(last) = last_span { - if last == span { - continue; - } - } else { - last_span = Some(span); - } - if self.tcx.def_key(instance.def_id()).disambiguated_data.data == - DefPathData::ClosureExpr - { - err.span_note(span, "inside call to closure"); - continue; - } - err.span_note(span, &format!("inside call to {}", instance)); + for FrameInfo { span, location } in self.generate_stacktrace(explicit_span) { + err.span_note(span, &format!("inside call to {}", location)); } err.emit(); } else { From 3c663d06075489e8290905da74b6306cc9c3f0e8 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 31 Jan 2018 15:45:59 +0100 Subject: [PATCH 080/110] Tidy fix --- src/librustc/traits/fulfill.rs | 3 ++- src/librustc_mir/hair/pattern/mod.rs | 1 + src/librustc_mir/interpret/const_eval.rs | 2 +- src/librustc_mir/transform/const_prop.rs | 6 ++---- src/librustc_typeck/check/mod.rs | 1 - 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index 15925a19d75a2..bb2c7977f265b 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -536,7 +536,8 @@ fn process_predicate<'a, 'gcx, 'tcx>( } else { Err(CodeSelectionError(ConstEvalFailure(ConstEvalErr { span: obligation.cause.span, - kind: ErrKind::UnimplementedConstVal("could not resolve").into(), + kind: ErrKind::UnimplementedConstVal("could not resolve") + .into(), }))) } }, diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs index 3dba00d1fb1b2..8c0b227899a3b 100644 --- a/src/librustc_mir/hair/pattern/mod.rs +++ b/src/librustc_mir/hair/pattern/mod.rs @@ -42,6 +42,7 @@ pub enum PatternError { StaticInPattern(Span), FloatBug, NonConstPath(Span), + AssociatedConstInPattern(Span), } #[derive(Copy, Clone, Debug)] diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index a4100a9d28af6..071103c600019 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -457,7 +457,7 @@ pub fn const_eval_provider<'a, 'tcx>( }, }; let ty = tcx.type_of(def_id); - let layout = (tcx, key.param_env).layout_of(ty).unwrap(); + let layout = tcx.layout_of(key.param_env.and(ty)).unwrap(); let ptr = MemoryPointer::new(id, 0); return Ok(tcx.mk_const(ty::Const { val: ConstVal::Value(Value::ByRef(ptr.into(), layout.align)), diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index e0203953fb2a4..b43227c9f1a22 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -17,7 +17,6 @@ use rustc::mir::{Constant, Literal, Location, Place, Mir, Operand, Rvalue, Local use rustc::mir::{NullOp, StatementKind, Statement, BasicBlock, LocalKind}; use rustc::mir::{TerminatorKind, ClearCrossCrate, SourceInfo, BinOp}; use rustc::mir::visit::{Visitor, PlaceContext}; -use rustc::ty::layout::LayoutOf; use rustc::middle::const_val::ConstVal; use rustc::ty::{TyCtxt, self, Instance}; use rustc::mir::interpret::{Value, PrimVal, GlobalId}; @@ -237,7 +236,7 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { let r = ecx.value_to_primval(ValTy { value: right.0, ty: right.1 }).ok()?; if op == BinOp::Shr || op == BinOp::Shl { let param_env = self.tcx.param_env(self.source.def_id); - let bits = (self.tcx, param_env).layout_of(place_ty).unwrap().size.bits(); + let bits = self.tcx.layout_of(param_env.and(place_ty)).unwrap().size.bits(); if r.to_bytes().ok().map_or(false, |b| b >= bits as u128) { let scope_info = match self.mir.visibility_scope_info { ClearCrossCrate::Set(ref data) => data, @@ -286,8 +285,7 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { fn type_size_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, param_env: ty::ParamEnv<'tcx>, ty: ty::Ty<'tcx>) -> Option { - use rustc::ty::layout::LayoutOf; - (tcx, param_env).layout_of(ty).ok().map(|layout| layout.size.bytes()) + tcx.layout_of(param_env.and(ty)).ok().map(|layout| layout.size.bytes()) } struct CanConstProp { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 4e1fd987d90da..19085ff039ec5 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -101,7 +101,6 @@ use rustc::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability use rustc::ty::fold::TypeFoldable; use rustc::ty::maps::Providers; use rustc::ty::util::{Representability, IntTypeExt, Discr}; -use rustc::ty::layout::LayoutOf; use errors::{DiagnosticBuilder, DiagnosticId}; use require_c_abi_if_variadic; From b985399831b26e253e49e2e32fed1f657c8ca525 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 6 Feb 2018 09:52:08 +0100 Subject: [PATCH 081/110] Reduce code duplication --- src/librustc_mir/hair/pattern/mod.rs | 98 +++++++++------------------- 1 file changed, 32 insertions(+), 66 deletions(-) diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs index 8c0b227899a3b..d5dde0164fb5d 100644 --- a/src/librustc_mir/hair/pattern/mod.rs +++ b/src/librustc_mir/hair/pattern/mod.rs @@ -775,6 +775,26 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { span: Span, ) -> Pattern<'tcx> { debug!("const_to_pat: cv={:#?}", cv); + let adt_subpattern = |i, variant_opt| { + let field = Field::new(i); + let val = match cv.val { + ConstVal::Value(miri) => const_val_field( + self.tcx, self.param_env, instance, span, + variant_opt, field, miri, cv.ty, + ).unwrap(), + _ => bug!("{:#?} is not a valid adt", cv), + }; + self.const_to_pat(instance, val, id, span) + }; + let adt_subpatterns = |n, variant_opt| { + (0..n).map(|i| { + let field = Field::new(i); + FieldPattern { + field, + pattern: adt_subpattern(i, variant_opt), + } + }).collect::>() + }; let kind = match cv.ty.sty { ty::TyFloat(_) => { let id = self.tcx.hir.hir_to_node_id(id); @@ -811,91 +831,37 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { .discriminants(self.tcx) .position(|var| var.val == discr) .unwrap(); + let subpatterns = adt_subpatterns( + adt_def.variants[variant_index].fields.len(), + Some(variant_index), + ); PatternKind::Variant { adt_def, substs, variant_index, - subpatterns: adt_def - .variants[variant_index] - .fields - .iter() - .enumerate() - .map(|(i, _)| { - let field = Field::new(i); - let val = match cv.val { - ConstVal::Value(miri) => const_val_field( - self.tcx, self.param_env, instance, span, - Some(variant_index), field, miri, cv.ty, - ).unwrap(), - _ => bug!("{:#?} is not a valid tuple", cv), - }; - FieldPattern { - field, - pattern: self.const_to_pat(instance, val, id, span), - } - }).collect(), + subpatterns, } }, - _ => return Pattern { - span, - ty: cv.ty, - kind: Box::new(PatternKind::Constant { - value: cv, - }), + ConstVal::Unevaluated(..) => + span_bug!(span, "{:#?} is not a valid enum constant", cv), } - } }, ty::TyAdt(adt_def, _) => { let struct_var = adt_def.non_enum_variant(); PatternKind::Leaf { - subpatterns: struct_var.fields.iter().enumerate().map(|(i, _)| { - let field = Field::new(i); - let val = match cv.val { - ConstVal::Value(miri) => const_val_field( - self.tcx, self.param_env, instance, span, - None, field, miri, cv.ty, - ).unwrap(), - _ => bug!("{:#?} is not a valid tuple", cv), - }; - FieldPattern { - field, - pattern: self.const_to_pat(instance, val, id, span), + subpatterns: adt_subpatterns(struct_var.fields.len(), None), } - }).collect() } - } ty::TyTuple(fields, _) => { PatternKind::Leaf { - subpatterns: (0..fields.len()).map(|i| { - let field = Field::new(i); - let val = match cv.val { - ConstVal::Value(miri) => const_val_field( - self.tcx, self.param_env, instance, span, - None, field, miri, cv.ty, - ).unwrap(), - _ => bug!("{:#?} is not a valid tuple", cv), - }; - FieldPattern { - field, - pattern: self.const_to_pat(instance, val, id, span), - } - }).collect() + subpatterns: adt_subpatterns(fields.len(), None), } } ty::TyArray(_, n) => { 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::Value(miri) => const_val_field( - self.tcx, self.param_env, instance, span, - None, field, miri, cv.ty, - ).unwrap(), - _ => bug!("{:#?} is not a valid tuple", cv), - }; - self.const_to_pat(instance, val, id, span) - }).collect(), + prefix: (0..n.val.unwrap_u64()) + .map(|i| adt_subpattern(i as usize, None)) + .collect(), slice: None, suffix: Vec::new(), } From 1f9d846a5882dbdc4c599fa33e6256d2e37841a6 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 6 Feb 2018 09:52:56 +0100 Subject: [PATCH 082/110] Add some documentation to pattern/const conversions --- src/librustc_mir/hair/pattern/mod.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs index d5dde0164fb5d..072b28850f0cf 100644 --- a/src/librustc_mir/hair/pattern/mod.rs +++ b/src/librustc_mir/hair/pattern/mod.rs @@ -667,6 +667,9 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } } + /// Takes a HIR Path. If the path is a constant, evaluates it and feeds + /// it to `const_to_pat`. Any other path (like enum variants without fields) + /// is converted to the corresponding pattern via `lower_variant_or_leaf` fn lower_path(&mut self, qpath: &hir::QPath, id: hir::HirId, @@ -722,6 +725,10 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } } + /// Converts literals, paths and negation of literals to patterns. + /// The special case for negation exists to allow things like -128i8 + /// which would overflow if we tried to evaluate 128i8 and then negate + /// afterwards. fn lower_lit(&mut self, expr: &'tcx hir::Expr) -> PatternKind<'tcx> { match expr.node { hir::ExprLit(ref lit) => { @@ -767,6 +774,9 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } } + /// Converts an evaluated constant to a pattern (if possible). + /// This means aggregate values (like structs and enums) are converted + /// to a pattern that matches the value (as if you'd compare via eq). fn const_to_pat( &self, instance: ty::Instance<'tcx>, @@ -844,14 +854,14 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { }, ConstVal::Unevaluated(..) => span_bug!(span, "{:#?} is not a valid enum constant", cv), - } + } }, ty::TyAdt(adt_def, _) => { let struct_var = adt_def.non_enum_variant(); PatternKind::Leaf { subpatterns: adt_subpatterns(struct_var.fields.len(), None), - } } + } ty::TyTuple(fields, _) => { PatternKind::Leaf { subpatterns: adt_subpatterns(fields.len(), None), From f5cc5ca8c73779e5fc4cac911f7d0b63b41b0ccd Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 6 Feb 2018 10:24:53 +0100 Subject: [PATCH 083/110] Remove dead code --- src/librustc_mir/hair/pattern/_match.rs | 5 ----- src/test/ui/const-eval/issue-47971.rs | 21 +++++++++++++++++++ .../lint/unused_parens_json_suggestion.stderr | 3 +++ src/test/ui/lint/use_suggestion_json.stderr | 13 ++++++++++++ 4 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 src/test/ui/const-eval/issue-47971.rs diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 5476d73fb6b4b..a3295aac80157 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -905,7 +905,6 @@ fn constructor_sub_pattern_tys<'a, 'tcx: 'a>(cx: &MatchCheckCtxt<'a, 'tcx>, ty::TySlice(ty) | ty::TyArray(ty, _) => match *ctor { Slice(length) => (0..length).map(|_| ty).collect(), ConstantValue(_) => vec![], - Single => vec![ty], _ => bug!("bad slice pattern {:?} {:?}", ctor, ty) }, ty::TyRef(_, ref ty_and_mut) => vec![ty_and_mut.ty], @@ -914,9 +913,6 @@ fn constructor_sub_pattern_tys<'a, 'tcx: 'a>(cx: &MatchCheckCtxt<'a, 'tcx>, // Use T as the sub pattern type of Box. vec![substs.type_at(0)] } else { - if let ConstantValue(_) = *ctor { - return vec![]; - } adt.variants[ctor.variant_index_for_adt(adt)].fields.iter().map(|field| { let is_visible = adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx); @@ -1025,7 +1021,6 @@ fn constructor_covered_by_range(ctor: &Constructor, (end == RangeEnd::Excluded && to == Ordering::Equal); Ok(some_or_ok!(cmp_from(&from.val)) && end) } - Variant(_) | Single => Ok(true), _ => bug!(), } diff --git a/src/test/ui/const-eval/issue-47971.rs b/src/test/ui/const-eval/issue-47971.rs new file mode 100644 index 0000000000000..4a2f0a7f38cda --- /dev/null +++ b/src/test/ui/const-eval/issue-47971.rs @@ -0,0 +1,21 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// must-compile-successfully + +#![feature(const_fn)] + +struct S(pub &'static u32, pub u32); + +const fn g(ss: &S) -> &u32 { &ss.1 } + +static T: S = S(g(&T), 0); + +fn main () { } diff --git a/src/test/ui/lint/unused_parens_json_suggestion.stderr b/src/test/ui/lint/unused_parens_json_suggestion.stderr index cd4379d90cf31..ac29d635a920d 100644 --- a/src/test/ui/lint/unused_parens_json_suggestion.stderr +++ b/src/test/ui/lint/unused_parens_json_suggestion.stderr @@ -24,6 +24,7 @@ ], "label": null, "suggested_replacement": null, + "suggestion_approximate": null, "expansion": null } ], @@ -51,6 +52,7 @@ ], "label": null, "suggested_replacement": null, + "suggestion_approximate": null, "expansion": null } ], @@ -80,6 +82,7 @@ ], "label": null, "suggested_replacement": "1 / (2 + 3)", + "suggestion_approximate": null, "expansion": null } ], diff --git a/src/test/ui/lint/use_suggestion_json.stderr b/src/test/ui/lint/use_suggestion_json.stderr index 00624f7e5ca47..33cd61342f0e7 100644 --- a/src/test/ui/lint/use_suggestion_json.stderr +++ b/src/test/ui/lint/use_suggestion_json.stderr @@ -89,6 +89,7 @@ mod foo { ], "label": "not found in this scope", "suggested_replacement": null, + "suggestion_approximate": null, "expansion": null } ], @@ -118,6 +119,7 @@ mod foo { "suggested_replacement": "use std::collections::binary_heap::Iter; ", + "suggestion_approximate": null, "expansion": null }, { @@ -140,6 +142,7 @@ mod foo { "suggested_replacement": "use std::collections::btree_map::Iter; ", + "suggestion_approximate": null, "expansion": null }, { @@ -162,6 +165,7 @@ mod foo { "suggested_replacement": "use std::collections::btree_set::Iter; ", + "suggestion_approximate": null, "expansion": null }, { @@ -184,6 +188,7 @@ mod foo { "suggested_replacement": "use std::collections::hash_map::Iter; ", + "suggestion_approximate": null, "expansion": null }, { @@ -206,6 +211,7 @@ mod foo { "suggested_replacement": "use std::collections::hash_set::Iter; ", + "suggestion_approximate": null, "expansion": null }, { @@ -228,6 +234,7 @@ mod foo { "suggested_replacement": "use std::collections::linked_list::Iter; ", + "suggestion_approximate": null, "expansion": null }, { @@ -250,6 +257,7 @@ mod foo { "suggested_replacement": "use std::collections::vec_deque::Iter; ", + "suggestion_approximate": null, "expansion": null }, { @@ -272,6 +280,7 @@ mod foo { "suggested_replacement": "use std::option::Iter; ", + "suggestion_approximate": null, "expansion": null }, { @@ -294,6 +303,7 @@ mod foo { "suggested_replacement": "use std::path::Iter; ", + "suggestion_approximate": null, "expansion": null }, { @@ -316,6 +326,7 @@ mod foo { "suggested_replacement": "use std::result::Iter; ", + "suggestion_approximate": null, "expansion": null }, { @@ -338,6 +349,7 @@ mod foo { "suggested_replacement": "use std::slice::Iter; ", + "suggestion_approximate": null, "expansion": null }, { @@ -360,6 +372,7 @@ mod foo { "suggested_replacement": "use std::sync::mpsc::Iter; ", + "suggestion_approximate": null, "expansion": null } ], From 421622d8f79756b0b8629f5779dc0071a5012bc0 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 6 Feb 2018 10:51:54 +0100 Subject: [PATCH 084/110] Stage 2 doesn't see suggestion_approximate --- .../ui/lint/unused_parens_json_suggestion.stderr | 3 --- src/test/ui/lint/use_suggestion_json.stderr | 13 ------------- 2 files changed, 16 deletions(-) diff --git a/src/test/ui/lint/unused_parens_json_suggestion.stderr b/src/test/ui/lint/unused_parens_json_suggestion.stderr index ac29d635a920d..cd4379d90cf31 100644 --- a/src/test/ui/lint/unused_parens_json_suggestion.stderr +++ b/src/test/ui/lint/unused_parens_json_suggestion.stderr @@ -24,7 +24,6 @@ ], "label": null, "suggested_replacement": null, - "suggestion_approximate": null, "expansion": null } ], @@ -52,7 +51,6 @@ ], "label": null, "suggested_replacement": null, - "suggestion_approximate": null, "expansion": null } ], @@ -82,7 +80,6 @@ ], "label": null, "suggested_replacement": "1 / (2 + 3)", - "suggestion_approximate": null, "expansion": null } ], diff --git a/src/test/ui/lint/use_suggestion_json.stderr b/src/test/ui/lint/use_suggestion_json.stderr index 33cd61342f0e7..00624f7e5ca47 100644 --- a/src/test/ui/lint/use_suggestion_json.stderr +++ b/src/test/ui/lint/use_suggestion_json.stderr @@ -89,7 +89,6 @@ mod foo { ], "label": "not found in this scope", "suggested_replacement": null, - "suggestion_approximate": null, "expansion": null } ], @@ -119,7 +118,6 @@ mod foo { "suggested_replacement": "use std::collections::binary_heap::Iter; ", - "suggestion_approximate": null, "expansion": null }, { @@ -142,7 +140,6 @@ mod foo { "suggested_replacement": "use std::collections::btree_map::Iter; ", - "suggestion_approximate": null, "expansion": null }, { @@ -165,7 +162,6 @@ mod foo { "suggested_replacement": "use std::collections::btree_set::Iter; ", - "suggestion_approximate": null, "expansion": null }, { @@ -188,7 +184,6 @@ mod foo { "suggested_replacement": "use std::collections::hash_map::Iter; ", - "suggestion_approximate": null, "expansion": null }, { @@ -211,7 +206,6 @@ mod foo { "suggested_replacement": "use std::collections::hash_set::Iter; ", - "suggestion_approximate": null, "expansion": null }, { @@ -234,7 +228,6 @@ mod foo { "suggested_replacement": "use std::collections::linked_list::Iter; ", - "suggestion_approximate": null, "expansion": null }, { @@ -257,7 +250,6 @@ mod foo { "suggested_replacement": "use std::collections::vec_deque::Iter; ", - "suggestion_approximate": null, "expansion": null }, { @@ -280,7 +272,6 @@ mod foo { "suggested_replacement": "use std::option::Iter; ", - "suggestion_approximate": null, "expansion": null }, { @@ -303,7 +294,6 @@ mod foo { "suggested_replacement": "use std::path::Iter; ", - "suggestion_approximate": null, "expansion": null }, { @@ -326,7 +316,6 @@ mod foo { "suggested_replacement": "use std::result::Iter; ", - "suggestion_approximate": null, "expansion": null }, { @@ -349,7 +338,6 @@ mod foo { "suggested_replacement": "use std::slice::Iter; ", - "suggestion_approximate": null, "expansion": null }, { @@ -372,7 +360,6 @@ mod foo { "suggested_replacement": "use std::sync::mpsc::Iter; ", - "suggestion_approximate": null, "expansion": null } ], From 5b698a1040fcb866503fa71bf42f28315bce18d7 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 6 Feb 2018 14:04:35 +0100 Subject: [PATCH 085/110] Unregress error spans in constant errors --- src/librustc/middle/const_val.rs | 2 +- src/librustc_mir/hair/pattern/mod.rs | 2 +- src/librustc_mir/interpret/const_eval.rs | 15 +++++++-------- src/librustc_mir/interpret/eval_context.rs | 17 +++++++++++++---- src/librustc_mir/transform/const_prop.rs | 2 +- src/librustc_trans/mir/constant.rs | 1 - src/test/ui/const-eval/index_out_of_bound.rs | 1 - src/test/ui/const-fn-error.rs | 2 +- src/test/ui/infinite-recursion-const-fn.rs | 4 ++-- 9 files changed, 26 insertions(+), 20 deletions(-) diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index 151cc9b1ce2ab..8c3dfd0bce752 100644 --- a/src/librustc/middle/const_val.rs +++ b/src/librustc/middle/const_val.rs @@ -155,7 +155,7 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { ConstEvalErrDescription::Backtrace(miri, frames) => { diag.span_label(self.span, format!("{}", miri)); for frame in frames { - diag.span_label(frame.span, format!("inside call to {}", frame.location)); + diag.span_label(frame.span, format!("inside call to `{}`", frame.location)); } } } diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs index 072b28850f0cf..1314f5eb58d32 100644 --- a/src/librustc_mir/hair/pattern/mod.rs +++ b/src/librustc_mir/hair/pattern/mod.rs @@ -789,7 +789,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { let field = Field::new(i); let val = match cv.val { ConstVal::Value(miri) => const_val_field( - self.tcx, self.param_env, instance, span, + self.tcx, self.param_env, instance, variant_opt, field, miri, cv.ty, ).unwrap(), _ => bug!("{:#?} is not a valid adt", cv), diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index 071103c600019..ebb620701407e 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -61,7 +61,7 @@ pub fn eval_body_with_mir<'a, 'mir, 'tcx>( mir: &'mir mir::Mir<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> Option<(Value, Pointer, Ty<'tcx>)> { - let (res, ecx, _) = eval_body_and_ecx(tcx, cid, Some(mir), param_env); + let (res, ecx) = eval_body_and_ecx(tcx, cid, Some(mir), param_env); match res { Ok(val) => Some(val), Err(mut err) => { @@ -76,7 +76,7 @@ pub fn eval_body<'a, 'tcx>( cid: GlobalId<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> Option<(Value, Pointer, Ty<'tcx>)> { - let (res, ecx, _) = eval_body_and_ecx(tcx, cid, None, param_env); + let (res, ecx) = eval_body_and_ecx(tcx, cid, None, param_env); match res { Ok(val) => Some(val), Err(mut err) => { @@ -91,7 +91,7 @@ fn eval_body_and_ecx<'a, 'mir, 'tcx>( cid: GlobalId<'tcx>, mir: Option<&'mir mir::Mir<'tcx>>, param_env: ty::ParamEnv<'tcx>, -) -> (EvalResult<'tcx, (Value, Pointer, Ty<'tcx>)>, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>, Span) { +) -> (EvalResult<'tcx, (Value, Pointer, Ty<'tcx>)>, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>) { debug!("eval_body: {:?}, {:?}", cid, param_env); let mut ecx = EvalContext::new(tcx, param_env, CompileTimeEvaluator, ()); // we start out with the best span we have @@ -155,7 +155,7 @@ fn eval_body_and_ecx<'a, 'mir, 'tcx>( }; Ok((value, ptr, layout.ty)) })(); - (res, ecx, span) + (res, ecx) } pub struct CompileTimeEvaluator; @@ -367,7 +367,6 @@ pub fn const_val_field<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, param_env: ty::ParamEnv<'tcx>, instance: ty::Instance<'tcx>, - span: Span, variant: Option, field: mir::Field, value: Value, @@ -403,7 +402,7 @@ pub fn const_val_field<'a, 'tcx>( ty, })), Err(err) => { - let trace = ecx.generate_stacktrace(None); + let (trace, span) = ecx.generate_stacktrace(None); let err = ErrKind::Miri(err, trace); Err(ConstEvalErr { kind: err.into(), @@ -490,7 +489,7 @@ pub fn const_eval_provider<'a, 'tcx>( } }; - let (res, ecx, span) = eval_body_and_ecx(tcx, cid, None, key.param_env); + let (res, ecx) = eval_body_and_ecx(tcx, cid, None, key.param_env); res.map(|(miri_value, _, miri_ty)| { tcx.mk_const(ty::Const { val: ConstVal::Value(miri_value), @@ -500,7 +499,7 @@ pub fn const_eval_provider<'a, 'tcx>( if tcx.is_static(def_id).is_some() { ecx.report(&mut err, true, None); } - let trace = ecx.generate_stacktrace(None); + let (trace, span) = ecx.generate_stacktrace(None); let err = ErrKind::Miri(err, trace); ConstEvalErr { kind: err.into(), diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 483649204fbec..2dc23e759c870 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -1570,7 +1570,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M Ok(()) } - pub fn generate_stacktrace(&self, explicit_span: Option) -> Vec { + pub fn generate_stacktrace(&self, explicit_span: Option) -> (Vec, Span) { let mut last_span = None; let mut frames = Vec::new(); // skip 1 because the last frame is just the environment of the constant @@ -1594,7 +1594,15 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M }; frames.push(FrameInfo { span, location }); } - frames + let frame = self.frame(); + let bb = &frame.mir.basic_blocks()[frame.block]; + let span = if let Some(stmt) = bb.statements.get(frame.stmt) { + stmt.source_info.span + } else { + bb.terminator().source_info.span + }; + trace!("generate stacktrace: {:#?}, {:?}", frames, explicit_span); + (frames, span) } pub fn report(&self, e: &mut EvalError, as_err: bool, explicit_span: Option) { @@ -1658,9 +1666,10 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M "constant evaluation error", ) }; + let (frames, span) = self.generate_stacktrace(explicit_span); err.span_label(span, e.to_string()); - for FrameInfo { span, location } in self.generate_stacktrace(explicit_span) { - err.span_note(span, &format!("inside call to {}", location)); + for FrameInfo { span, location } in frames { + err.span_note(span, &format!("inside call to `{}`", location)); } err.emit(); } else { diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index b43227c9f1a22..de17872e96ff2 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -80,7 +80,7 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { let value = match self.tcx.const_eval(self.param_env.and(cid)) { Ok(val) => val, Err(err) => { - err.report(self.tcx, span, "constant propagated"); + err.report(self.tcx, err.span, "constant propagated"); return None; }, }; diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 8018073883fcc..6aa8b7e5449fd 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -203,7 +203,6 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { bx.tcx(), ty::ParamEnv::empty(traits::Reveal::All), self.instance, - constant.span, None, mir::Field::new(field as usize), c, diff --git a/src/test/ui/const-eval/index_out_of_bound.rs b/src/test/ui/const-eval/index_out_of_bound.rs index 632804f2fd366..e7ffbe81b9ae7 100644 --- a/src/test/ui/const-eval/index_out_of_bound.rs +++ b/src/test/ui/const-eval/index_out_of_bound.rs @@ -10,6 +10,5 @@ static FOO: i32 = [][0]; //~^ ERROR E0080 -//~| ERROR E0080 fn main() {} diff --git a/src/test/ui/const-fn-error.rs b/src/test/ui/const-fn-error.rs index dc1526a7079d4..9e09f66776c9b 100644 --- a/src/test/ui/const-fn-error.rs +++ b/src/test/ui/const-fn-error.rs @@ -18,6 +18,7 @@ const fn f(x: usize) -> usize { for i in 0..x { //~^ ERROR E0015 //~| ERROR E0019 + //~| ERROR E0080 sum += i; } sum @@ -26,5 +27,4 @@ const fn f(x: usize) -> usize { #[allow(unused_variables)] fn main() { let a : [i32; f(X)]; - //~^ ERROR E0080 } diff --git a/src/test/ui/infinite-recursion-const-fn.rs b/src/test/ui/infinite-recursion-const-fn.rs index 05e40abdc0f2f..51de304405a0a 100644 --- a/src/test/ui/infinite-recursion-const-fn.rs +++ b/src/test/ui/infinite-recursion-const-fn.rs @@ -12,7 +12,7 @@ #![feature(const_fn)] const fn a() -> usize { b() } -const fn b() -> usize { a() } -const ARR: [i32; a()] = [5; 6]; //~ ERROR constant evaluation error +const fn b() -> usize { a() } //~ ERROR constant evaluation error +const ARR: [i32; a()] = [5; 6]; fn main(){} From 0a1278aea808ddfe2178fdc49b96ccc0e9e608b6 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 6 Feb 2018 14:04:42 +0100 Subject: [PATCH 086/110] Typo --- src/librustc_passes/rvalue_promotion.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_passes/rvalue_promotion.rs b/src/librustc_passes/rvalue_promotion.rs index 013bc07d8e9a7..bda351eed40b2 100644 --- a/src/librustc_passes/rvalue_promotion.rs +++ b/src/librustc_passes/rvalue_promotion.rs @@ -291,7 +291,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node // References to a static that are themselves within a static // are inherently promotable with the exception - // of "#[thread_loca]" statics, which may not + // of "#[thread_local]" statics, which may not // outlive the current function Def::Static(did, _) => { From f68dc0190a369b6ce9bcfaae26d1b07941028da0 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 6 Feb 2018 14:05:06 +0100 Subject: [PATCH 087/110] Add a test for transmuting via unions in constants --- src/test/ui/const-eval/const_transmute.rs | 62 +++++++++++++++++++ src/test/ui/const-eval/const_transmute.stderr | 0 2 files changed, 62 insertions(+) create mode 100644 src/test/ui/const-eval/const_transmute.rs create mode 100644 src/test/ui/const-eval/const_transmute.stderr diff --git a/src/test/ui/const-eval/const_transmute.rs b/src/test/ui/const-eval/const_transmute.rs new file mode 100644 index 0000000000000..a64a1d212abf3 --- /dev/null +++ b/src/test/ui/const-eval/const_transmute.rs @@ -0,0 +1,62 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// must-compile-successfully +// run-pass + +union Transmute { + t: T, + u: U, +} + +trait Bar { + fn bar(&self) -> u32; +} + +struct Foo { + foo: u32, + bar: bool, +} + +impl Bar for Foo { + fn bar(&self) -> u32 { + self.foo + } +} + +impl Drop for Foo { + fn drop(&mut self) { + assert!(!self.bar); + self.bar = true; + println!("dropping Foo"); + } +} + +#[derive(Copy, Clone)] +struct Fat<'a>(&'a Foo, &'static VTable); + +struct VTable { + drop: Option fn(&'a mut Foo)>, + size: usize, + align: usize, + bar: for<'a> fn(&'a Foo) -> u32, +} + +const FOO: &Bar = &Foo { foo: 128, bar: false }; +const G: Fat = unsafe { Transmute { t: FOO }.u }; +const F: Option fn(&'a mut Foo)> = G.1.drop; +const H: for<'a> fn(&'a Foo) -> u32 = G.1.bar; + +fn main() { + let mut foo = Foo { foo: 99, bar: false }; + (F.unwrap())(&mut foo); + std::mem::forget(foo); // already ran the drop impl + assert_eq!(H(&Foo { foo: 42, bar: false }), 42); +} diff --git a/src/test/ui/const-eval/const_transmute.stderr b/src/test/ui/const-eval/const_transmute.stderr new file mode 100644 index 0000000000000..e69de29bb2d1d From f363e08c9dbddc339eed14b59580de784f74d4e2 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 6 Feb 2018 14:15:36 +0100 Subject: [PATCH 088/110] Update compile-fail tests --- src/test/compile-fail/array_const_index-0.rs | 1 - src/test/compile-fail/array_const_index-1.rs | 1 - src/test/compile-fail/const-err-early.rs | 5 --- src/test/compile-fail/const-err-multi.rs | 7 ---- src/test/compile-fail/const-err.rs | 2 -- src/test/compile-fail/const-eval-overflow2.rs | 32 +++++-------------- .../compile-fail/const-eval-overflow2b.rs | 32 +++++-------------- .../compile-fail/const-eval-overflow2c.rs | 32 +++++-------------- src/test/compile-fail/const-slice-oob.rs | 1 - 9 files changed, 24 insertions(+), 89 deletions(-) diff --git a/src/test/compile-fail/array_const_index-0.rs b/src/test/compile-fail/array_const_index-0.rs index 35e7a42256251..501c66e75cded 100644 --- a/src/test/compile-fail/array_const_index-0.rs +++ b/src/test/compile-fail/array_const_index-0.rs @@ -11,7 +11,6 @@ const A: &'static [i32] = &[]; const B: i32 = (&A)[1]; //~^ ERROR constant evaluation error -//~| ERROR E0080 //~| index out of bounds: the len is 0 but the index is 1 fn main() { diff --git a/src/test/compile-fail/array_const_index-1.rs b/src/test/compile-fail/array_const_index-1.rs index db4cfa4268919..d3b43e83bfe52 100644 --- a/src/test/compile-fail/array_const_index-1.rs +++ b/src/test/compile-fail/array_const_index-1.rs @@ -11,7 +11,6 @@ const A: [i32; 0] = []; const B: i32 = A[1]; //~^ ERROR constant evaluation error -//~| ERROR E0080 //~| index out of bounds: the len is 0 but the index is 1 fn main() { diff --git a/src/test/compile-fail/const-err-early.rs b/src/test/compile-fail/const-err-early.rs index 3182ffa73b08d..53077d74ce50d 100644 --- a/src/test/compile-fail/const-err-early.rs +++ b/src/test/compile-fail/const-err-early.rs @@ -13,16 +13,11 @@ pub const A: i8 = -std::i8::MIN; //~ ERROR E0080 //~| ERROR const_err //~| ERROR const_err -//~| ERROR E0080 pub const B: u8 = 200u8 + 200u8; //~ ERROR E0080 -//~| ERROR E0080 pub const C: u8 = 200u8 * 4; //~ ERROR E0080 -//~| ERROR E0080 pub const D: u8 = 42u8 - (42u8 + 1); //~ ERROR E0080 -//~| ERROR E0080 pub const E: u8 = [5u8][1]; //~^ ERROR E0080 -//~| ERROR E0080 fn main() { let _a = A; diff --git a/src/test/compile-fail/const-err-multi.rs b/src/test/compile-fail/const-err-multi.rs index 5dd8651005df8..d72097848feaa 100644 --- a/src/test/compile-fail/const-err-multi.rs +++ b/src/test/compile-fail/const-err-multi.rs @@ -14,19 +14,12 @@ pub const A: i8 = -std::i8::MIN; //~^ ERROR E0080 //~| ERROR const_err //~| ERROR const_err -//~| ERROR constant evaluation error -//~| ERROR constant evaluation error -//~| ERROR constant evaluation error -//~| ERROR E0080 pub const B: i8 = A; //~^ ERROR E0080 -//~| ERROR E0080 pub const C: u8 = A as u8; //~^ ERROR E0080 -//~| ERROR E0080 pub const D: i8 = 50 - A; //~^ ERROR E0080 -//~| ERROR E0080 fn main() { let _ = (A, B, C, D); diff --git a/src/test/compile-fail/const-err.rs b/src/test/compile-fail/const-err.rs index b0cf639cd762f..8bd759b6d3735 100644 --- a/src/test/compile-fail/const-err.rs +++ b/src/test/compile-fail/const-err.rs @@ -23,9 +23,7 @@ fn black_box(_: T) { // Make sure that the two uses get two errors. const FOO: u8 = [5u8][1]; //~^ ERROR constant evaluation error -//~| ERROR constant evaluation error //~| index out of bounds: the len is 1 but the index is 1 -//~| ERROR E0080 fn main() { black_box((FOO, FOO)); diff --git a/src/test/compile-fail/const-eval-overflow2.rs b/src/test/compile-fail/const-eval-overflow2.rs index d715847e03fe3..25787b7bb4b92 100644 --- a/src/test/compile-fail/const-eval-overflow2.rs +++ b/src/test/compile-fail/const-eval-overflow2.rs @@ -22,65 +22,49 @@ use std::{i8, i16, i32, i64, isize}; use std::{u8, u16, u32, u64, usize}; const VALS_I8: (i8,) = - //~^ ERROR constant evaluation error - //~| ERROR constant evaluation error - //~| attempt to subtract with overflow ( i8::MIN - 1, + //~^ ERROR constant evaluation error ); const VALS_I16: (i16,) = - //~^ ERROR constant evaluation error - //~| ERROR constant evaluation error - //~| attempt to subtract with overflow ( i16::MIN - 1, + //~^ ERROR constant evaluation error ); const VALS_I32: (i32,) = - //~^ ERROR constant evaluation error - //~| ERROR constant evaluation error - //~| attempt to subtract with overflow ( i32::MIN - 1, + //~^ ERROR constant evaluation error ); const VALS_I64: (i64,) = - //~^ ERROR constant evaluation error - //~| ERROR constant evaluation error - //~| attempt to subtract with overflow ( i64::MIN - 1, + //~^ ERROR constant evaluation error ); const VALS_U8: (u8,) = - //~^ ERROR constant evaluation error - //~| ERROR constant evaluation error - //~| attempt to subtract with overflow ( u8::MIN - 1, + //~^ ERROR constant evaluation error ); const VALS_U16: (u16,) = ( - //~^ ERROR constant evaluation error - //~| ERROR constant evaluation error - //~| attempt to subtract with overflow u16::MIN - 1, + //~^ ERROR constant evaluation error ); const VALS_U32: (u32,) = ( - //~^ ERROR constant evaluation error - //~| ERROR constant evaluation error - //~| attempt to subtract with overflow u32::MIN - 1, + //~^ ERROR constant evaluation error ); const VALS_U64: (u64,) = - //~^ ERROR constant evaluation error - //~| ERROR constant evaluation error - //~| attempt to subtract with overflow ( u64::MIN - 1, + //~^ ERROR constant evaluation error ); fn main() { diff --git a/src/test/compile-fail/const-eval-overflow2b.rs b/src/test/compile-fail/const-eval-overflow2b.rs index f6463900eeb53..d8332757b642b 100644 --- a/src/test/compile-fail/const-eval-overflow2b.rs +++ b/src/test/compile-fail/const-eval-overflow2b.rs @@ -22,65 +22,49 @@ use std::{i8, i16, i32, i64, isize}; use std::{u8, u16, u32, u64, usize}; const VALS_I8: (i8,) = - //~^ ERROR constant evaluation error - //~| ERROR constant evaluation error - //~| attempt to add with overflow ( i8::MAX + 1, + //~^ ERROR constant evaluation error ); const VALS_I16: (i16,) = - //~^ ERROR constant evaluation error - //~| ERROR constant evaluation error - //~| attempt to add with overflow ( i16::MAX + 1, + //~^ ERROR constant evaluation error ); const VALS_I32: (i32,) = - //~^ ERROR constant evaluation error - //~| ERROR constant evaluation error - //~| attempt to add with overflow ( i32::MAX + 1, + //~^ ERROR constant evaluation error ); const VALS_I64: (i64,) = - //~^ ERROR constant evaluation error - //~| ERROR constant evaluation error - //~| attempt to add with overflow ( i64::MAX + 1, + //~^ ERROR constant evaluation error ); const VALS_U8: (u8,) = - //~^ ERROR constant evaluation error - //~| ERROR constant evaluation error - //~| attempt to add with overflow ( u8::MAX + 1, + //~^ ERROR constant evaluation error ); const VALS_U16: (u16,) = ( - //~^ ERROR constant evaluation error - //~| ERROR constant evaluation error - //~| attempt to add with overflow u16::MAX + 1, + //~^ ERROR constant evaluation error ); const VALS_U32: (u32,) = ( - //~^ ERROR constant evaluation error - //~| ERROR constant evaluation error - //~| attempt to add with overflow u32::MAX + 1, + //~^ ERROR constant evaluation error ); const VALS_U64: (u64,) = - //~^ ERROR constant evaluation error - //~| ERROR constant evaluation error - //~| attempt to add with overflow ( u64::MAX + 1, + //~^ ERROR constant evaluation error ); fn main() { diff --git a/src/test/compile-fail/const-eval-overflow2c.rs b/src/test/compile-fail/const-eval-overflow2c.rs index 8665b9d97a84d..2a8e4dc2c34fe 100644 --- a/src/test/compile-fail/const-eval-overflow2c.rs +++ b/src/test/compile-fail/const-eval-overflow2c.rs @@ -22,65 +22,49 @@ use std::{i8, i16, i32, i64, isize}; use std::{u8, u16, u32, u64, usize}; const VALS_I8: (i8,) = - //~^ ERROR constant evaluation error - //~| ERROR constant evaluation error - //~| attempt to multiply with overflow ( i8::MIN * 2, + //~^ ERROR constant evaluation error ); const VALS_I16: (i16,) = - //~^ ERROR constant evaluation error - //~| ERROR constant evaluation error - //~| attempt to multiply with overflow ( i16::MIN * 2, + //~^ ERROR constant evaluation error ); const VALS_I32: (i32,) = - //~^ ERROR constant evaluation error - //~| ERROR constant evaluation error - //~| attempt to multiply with overflow ( i32::MIN * 2, + //~^ ERROR constant evaluation error ); const VALS_I64: (i64,) = - //~^ ERROR constant evaluation error - //~| ERROR constant evaluation error - //~| attempt to multiply with overflow ( i64::MIN * 2, + //~^ ERROR constant evaluation error ); const VALS_U8: (u8,) = - //~^ ERROR constant evaluation error - //~| ERROR constant evaluation error - //~| attempt to multiply with overflow ( u8::MAX * 2, + //~^ ERROR constant evaluation error ); const VALS_U16: (u16,) = ( - //~^ ERROR constant evaluation error - //~| ERROR constant evaluation error - //~| attempt to multiply with overflow u16::MAX * 2, + //~^ ERROR constant evaluation error ); const VALS_U32: (u32,) = ( - //~^ ERROR constant evaluation error - //~| ERROR constant evaluation error - //~| attempt to multiply with overflow u32::MAX * 2, + //~^ ERROR constant evaluation error ); const VALS_U64: (u64,) = - //~^ ERROR constant evaluation error - //~| ERROR constant evaluation error - //~| attempt to multiply with overflow ( u64::MAX * 2, + //~^ ERROR constant evaluation error ); fn main() { diff --git a/src/test/compile-fail/const-slice-oob.rs b/src/test/compile-fail/const-slice-oob.rs index cb884313f3373..179ea9e853f3a 100644 --- a/src/test/compile-fail/const-slice-oob.rs +++ b/src/test/compile-fail/const-slice-oob.rs @@ -13,7 +13,6 @@ const FOO: &'static[u32] = &[1, 2, 3]; const BAR: u32 = FOO[5]; //~^ ERROR constant evaluation error [E0080] -//~| ERROR constant evaluation error [E0080] //~| index out of bounds: the len is 3 but the index is 5 fn main() { From 1e653aa96b392a04ed489eadc4cd040bc2859b4b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 6 Feb 2018 15:35:43 +0100 Subject: [PATCH 089/110] Report a best guess span if no stack is available anymore --- src/librustc_mir/interpret/const_eval.rs | 7 ++++--- src/librustc_mir/interpret/eval_context.rs | 20 +++++++++++++++----- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index ebb620701407e..b44e91d7b7473 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -24,7 +24,7 @@ pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>( ) -> EvalResult<'tcx, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>> { debug!("mk_borrowck_eval_cx: {:?}", instance); let param_env = tcx.param_env(instance.def_id()); - let mut ecx = EvalContext::new(tcx, param_env, CompileTimeEvaluator, ()); + let mut ecx = EvalContext::new(tcx, param_env, CompileTimeEvaluator, (), span); // insert a stack frame so any queries have the correct substs ecx.push_stack_frame( instance, @@ -42,7 +42,8 @@ pub fn mk_eval_cx<'a, 'tcx>( param_env: ty::ParamEnv<'tcx>, ) -> EvalResult<'tcx, EvalContext<'a, 'tcx, 'tcx, CompileTimeEvaluator>> { debug!("mk_eval_cx: {:?}, {:?}", instance, param_env); - let mut ecx = EvalContext::new(tcx, param_env, CompileTimeEvaluator, ()); + let span = tcx.def_span(instance.def_id()); + let mut ecx = EvalContext::new(tcx, param_env, CompileTimeEvaluator, (), span); let mir = ecx.load_mir(instance.def)?; // insert a stack frame so any queries have the correct substs ecx.push_stack_frame( @@ -93,10 +94,10 @@ fn eval_body_and_ecx<'a, 'mir, 'tcx>( param_env: ty::ParamEnv<'tcx>, ) -> (EvalResult<'tcx, (Value, Pointer, Ty<'tcx>)>, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>) { debug!("eval_body: {:?}, {:?}", cid, param_env); - let mut ecx = EvalContext::new(tcx, param_env, CompileTimeEvaluator, ()); // we start out with the best span we have // and try improving it down the road when more information is available let mut span = tcx.def_span(cid.instance.def_id()); + let mut ecx = EvalContext::new(tcx, param_env, CompileTimeEvaluator, (), mir.map(|mir| mir.span).unwrap_or(span)); let res = (|| { let mut mir = match mir { Some(mir) => mir, diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 2dc23e759c870..23ab6be1b46c2 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -45,6 +45,11 @@ pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { /// This prevents infinite loops and huge computations from freezing up const eval. /// Remove once halting problem is solved. pub(crate) steps_remaining: usize, + + /// The span that is used if no more stack frames are available + /// + /// This happens after successful evaluation when the result is inspected + root_span: codemap::Span, } /// A stack frame. @@ -186,6 +191,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M param_env: ty::ParamEnv<'tcx>, machine: M, memory_data: M::MemoryData, + root_span: codemap::Span, ) -> Self { EvalContext { machine, @@ -195,6 +201,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M stack: Vec::new(), stack_limit: tcx.sess.const_eval_stack_frame_limit.get(), steps_remaining: tcx.sess.const_eval_step_limit.get(), + root_span, } } @@ -1594,12 +1601,15 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M }; frames.push(FrameInfo { span, location }); } - let frame = self.frame(); - let bb = &frame.mir.basic_blocks()[frame.block]; - let span = if let Some(stmt) = bb.statements.get(frame.stmt) { - stmt.source_info.span + let span = if let Some(frame) = self.stack().last() { + let bb = &frame.mir.basic_blocks()[frame.block]; + if let Some(stmt) = bb.statements.get(frame.stmt) { + stmt.source_info.span + } else { + bb.terminator().source_info.span + } } else { - bb.terminator().source_info.span + self.root_span }; trace!("generate stacktrace: {:#?}, {:?}", frames, explicit_span); (frames, span) From 2f0e07730088b7656c244030f360406758f788ea Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 6 Feb 2018 18:33:59 +0100 Subject: [PATCH 090/110] Report tcx errors with the span of the currently evaluating statement --- src/librustc_mir/interpret/const_eval.rs | 9 +-- src/librustc_mir/interpret/eval_context.rs | 62 ++++++-------------- src/librustc_mir/interpret/memory.rs | 7 ++- src/librustc_mir/interpret/place.rs | 4 +- src/librustc_mir/interpret/step.rs | 4 ++ src/librustc_mir/interpret/terminator/mod.rs | 11 ++-- src/librustc_mir/interpret/traits.rs | 5 +- src/test/ui/infinite-recursion-const-fn.rs | 4 +- 8 files changed, 41 insertions(+), 65 deletions(-) diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index b44e91d7b7473..ee5874be9d70a 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -24,7 +24,7 @@ pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>( ) -> EvalResult<'tcx, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>> { debug!("mk_borrowck_eval_cx: {:?}", instance); let param_env = tcx.param_env(instance.def_id()); - let mut ecx = EvalContext::new(tcx, param_env, CompileTimeEvaluator, (), span); + let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ()); // insert a stack frame so any queries have the correct substs ecx.push_stack_frame( instance, @@ -43,7 +43,7 @@ pub fn mk_eval_cx<'a, 'tcx>( ) -> EvalResult<'tcx, EvalContext<'a, 'tcx, 'tcx, CompileTimeEvaluator>> { debug!("mk_eval_cx: {:?}, {:?}", instance, param_env); let span = tcx.def_span(instance.def_id()); - let mut ecx = EvalContext::new(tcx, param_env, CompileTimeEvaluator, (), span); + let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ()); let mir = ecx.load_mir(instance.def)?; // insert a stack frame so any queries have the correct substs ecx.push_stack_frame( @@ -96,8 +96,9 @@ fn eval_body_and_ecx<'a, 'mir, 'tcx>( debug!("eval_body: {:?}, {:?}", cid, param_env); // we start out with the best span we have // and try improving it down the road when more information is available - let mut span = tcx.def_span(cid.instance.def_id()); - let mut ecx = EvalContext::new(tcx, param_env, CompileTimeEvaluator, (), mir.map(|mir| mir.span).unwrap_or(span)); + let span = tcx.def_span(cid.instance.def_id()); + let mut span = mir.map(|mir| mir.span).unwrap_or(span); + let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ()); let res = (|| { let mut mir = match mir { Some(mir) => mir, diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 23ab6be1b46c2..f46d563106083 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -5,13 +5,13 @@ use rustc::hir::def_id::DefId; use rustc::hir::map::definitions::DefPathData; use rustc::middle::const_val::{ConstVal, ErrKind}; use rustc::mir; -use rustc::traits::Reveal; use rustc::ty::layout::{self, Size, Align, HasDataLayout, LayoutOf, TyLayout}; use rustc::ty::subst::{Subst, Substs}; use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::maps::TyCtxtAt; use rustc_data_structures::indexed_vec::Idx; use rustc::middle::const_val::FrameInfo; -use syntax::codemap::{self, DUMMY_SP, Span}; +use syntax::codemap::{self, Span}; use syntax::ast::Mutability; use rustc::mir::interpret::{ GlobalId, Value, Pointer, PrimVal, PrimValKind, @@ -27,7 +27,7 @@ pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { pub machine: M, /// The results of the type checker, from rustc. - pub tcx: TyCtxt<'a, 'tcx, 'tcx>, + pub tcx: TyCtxtAt<'a, 'tcx, 'tcx>, /// Bounds in scope for polymorphic evaluations. pub param_env: ty::ParamEnv<'tcx>, @@ -45,11 +45,6 @@ pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { /// This prevents infinite loops and huge computations from freezing up const eval. /// Remove once halting problem is solved. pub(crate) steps_remaining: usize, - - /// The span that is used if no more stack frames are available - /// - /// This happens after successful evaluation when the result is inspected - root_span: codemap::Span, } /// A stack frame. @@ -154,7 +149,7 @@ impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> layout::HasTyCtxt<'tcx> for &'a EvalContext<'a, 'mir, 'tcx, M> { #[inline] fn tcx<'b>(&'b self) -> TyCtxt<'b, 'tcx, 'tcx> { - self.tcx + *self.tcx } } @@ -162,7 +157,7 @@ impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> layout::HasTyCtxt<'tcx> for &'c &'b mut EvalContext<'a, 'mir, 'tcx, M> { #[inline] fn tcx<'d>(&'d self) -> TyCtxt<'d, 'tcx, 'tcx> { - self.tcx + *self.tcx } } @@ -187,11 +182,10 @@ impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> LayoutOf> impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { pub fn new( - tcx: TyCtxt<'a, 'tcx, 'tcx>, + tcx: TyCtxtAt<'a, 'tcx, 'tcx>, param_env: ty::ParamEnv<'tcx>, machine: M, memory_data: M::MemoryData, - root_span: codemap::Span, ) -> Self { EvalContext { machine, @@ -201,7 +195,6 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M stack: Vec::new(), stack_limit: tcx.sess.const_eval_stack_frame_limit.get(), steps_remaining: tcx.sess.const_eval_step_limit.get(), - root_span, } } @@ -255,7 +248,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M pub(super) fn resolve(&self, def_id: DefId, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, ty::Instance<'tcx>> { let substs = self.tcx.trans_apply_param_substs(self.substs(), &substs); ty::Instance::resolve( - self.tcx, + *self.tcx, self.param_env, def_id, substs, @@ -263,7 +256,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M } pub(super) fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { - ty.is_sized(self.tcx.at(DUMMY_SP), self.param_env) + ty.is_sized(self.tcx, self.param_env) } pub fn load_mir( @@ -290,7 +283,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M // miri doesn't care about lifetimes, and will choke on some crazy ones // let's simply get rid of them let without_lifetimes = self.tcx.erase_regions(&ty); - let substituted = without_lifetimes.subst(self.tcx, substs); + let substituted = without_lifetimes.subst(*self.tcx, substs); let substituted = self.tcx.fully_normalize_monormophic_ty(&substituted); substituted } @@ -725,7 +718,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M ty::TyClosure(def_id, substs) => { let substs = self.tcx.trans_apply_param_substs(self.substs(), &substs); let instance = ty::Instance::resolve_closure( - self.tcx, + *self.tcx, def_id, substs, ty::ClosureKind::FnOnce, @@ -748,8 +741,8 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M let place = self.eval_place(place)?; let discr_val = self.read_discriminant_value(place, ty)?; if let ty::TyAdt(adt_def, _) = ty.sty { - trace!("Read discriminant {}, valid discriminants {:?}", discr_val, adt_def.discriminants(self.tcx).collect::>()); - if adt_def.discriminants(self.tcx).all(|v| { + trace!("Read discriminant {}, valid discriminants {:?}", discr_val, adt_def.discriminants(*self.tcx).collect::>()); + if adt_def.discriminants(*self.tcx).all(|v| { discr_val != v.val }) { @@ -797,7 +790,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M pub fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, ValTy<'tcx>> { use rustc::mir::Operand::*; - let ty = self.monomorphize(op.ty(self.mir(), self.tcx), self.substs()); + let ty = self.monomorphize(op.ty(self.mir(), *self.tcx), self.substs()); match *op { // FIXME: do some more logic on `move` to invalidate the old location Copy(ref place) | @@ -905,7 +898,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M } layout::Variants::Tagged { .. } => { let discr_val = dest_ty.ty_adt_def().unwrap() - .discriminant_for_variant(self.tcx, variant_index) + .discriminant_for_variant(*self.tcx, variant_index) .val; let (discr_dest, discr) = self.place_field(dest, mir::Field::new(0), layout)?; @@ -1412,7 +1405,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M } (_, &ty::TyDynamic(ref data, _)) => { let trait_ref = data.principal().unwrap().with_self_ty( - self.tcx, + *self.tcx, src_pointee_ty, ); let trait_ref = self.tcx.erase_regions(&trait_ref); @@ -1601,18 +1594,8 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M }; frames.push(FrameInfo { span, location }); } - let span = if let Some(frame) = self.stack().last() { - let bb = &frame.mir.basic_blocks()[frame.block]; - if let Some(stmt) = bb.statements.get(frame.stmt) { - stmt.source_info.span - } else { - bb.terminator().source_info.span - } - } else { - self.root_span - }; trace!("generate stacktrace: {:#?}, {:?}", frames, explicit_span); - (frames, span) + (frames, self.tcx.span) } pub fn report(&self, e: &mut EvalError, as_err: bool, explicit_span: Option) { @@ -1660,7 +1643,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M }); trace!("reporting const eval failure at {:?}", span); let mut err = if as_err { - ::rustc::middle::const_val::struct_error(self.tcx, span, "constant evaluation error") + ::rustc::middle::const_val::struct_error(*self.tcx, span, "constant evaluation error") } else { let node_id = self .stack() @@ -1722,14 +1705,3 @@ impl<'mir, 'tcx> Frame<'mir, 'tcx> { return Ok(old); } } - -// TODO(solson): Upstream these methods into rustc::ty::layout. - -pub fn resolve_drop_in_place<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - ty: Ty<'tcx>, -) -> ty::Instance<'tcx> { - let def_id = tcx.require_lang_item(::rustc::middle::lang_items::DropInPlaceFnLangItem); - let substs = tcx.intern_substs(&[ty.into()]); - ty::Instance::resolve(tcx, ty::ParamEnv::empty(Reveal::All), def_id, substs).unwrap() -} diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index da8ff11e3d0a9..7dceacabb292f 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -2,7 +2,8 @@ use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian, BigEndian}; use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque}; use std::{ptr, io}; -use rustc::ty::{Instance, TyCtxt}; +use rustc::ty::Instance; +use rustc::ty::maps::TyCtxtAt; use rustc::ty::layout::{self, Align, TargetDataLayout}; use syntax::ast::Mutability; @@ -51,11 +52,11 @@ pub struct Memory<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { /// The current stack frame. Used to check accesses against locks. pub cur_frame: usize, - pub tcx: TyCtxt<'a, 'tcx, 'tcx>, + pub tcx: TyCtxtAt<'a, 'tcx, 'tcx>, } impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, data: M::MemoryData) -> Self { + pub fn new(tcx: TyCtxtAt<'a, 'tcx, 'tcx>, data: M::MemoryData) -> Self { Memory { data, alloc_kind: HashMap::new(), diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 4ca68068728d7..d27de3ef6bfc4 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -209,7 +209,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { extra: PlaceExtra::None, } } else { - let instance = ty::Instance::mono(self.tcx, static_.def_id); + let instance = ty::Instance::mono(*self.tcx, static_.def_id); let cid = GlobalId { instance, promoted: None @@ -445,7 +445,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { pub fn place_ty(&self, place: &mir::Place<'tcx>) -> Ty<'tcx> { self.monomorphize( - place.ty(self.mir(), self.tcx).to_ty(self.tcx), + place.ty(self.mir(), *self.tcx).to_ty(*self.tcx), self.substs(), ) } diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs index 94fe3d1c67b8f..54fd364d3f820 100644 --- a/src/librustc_mir/interpret/step.rs +++ b/src/librustc_mir/interpret/step.rs @@ -51,6 +51,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { // Some statements (e.g. box) push new stack frames. We have to record the stack frame number // *before* executing the statement. let frame_idx = self.cur_frame(); + self.tcx.span = stmt.source_info.span; + self.memory.tcx.span = stmt.source_info.span; match stmt.kind { Assign(ref place, ref rvalue) => self.eval_rvalue_into_place(rvalue, place)?, @@ -99,6 +101,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<'tcx> { trace!("{:?}", terminator.kind); + self.tcx.span = terminator.source_info.span; + self.memory.tcx.span = terminator.source_info.span; self.eval_terminator(terminator)?; if !self.stack.is_empty() { trace!("// {:?}", self.frame().block); diff --git a/src/librustc_mir/interpret/terminator/mod.rs b/src/librustc_mir/interpret/terminator/mod.rs index b1b5f77a2e6ef..51dee0aaff47c 100644 --- a/src/librustc_mir/interpret/terminator/mod.rs +++ b/src/librustc_mir/interpret/terminator/mod.rs @@ -5,8 +5,7 @@ use syntax::codemap::Span; use syntax::abi::Abi; use rustc::mir::interpret::{EvalResult, PrimVal, Value}; -use super::{EvalContext, eval_context, - Place, Machine, ValTy}; +use super::{EvalContext, Place, Machine, ValTy}; use rustc_data_structures::indexed_vec::Idx; use interpret::memory::HasMemory; @@ -72,10 +71,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { ty::TyFnPtr(sig) => { let fn_ptr = self.value_to_primval(func)?.to_ptr()?; let instance = self.memory.get_fn(fn_ptr)?; - let instance_ty = instance.ty(self.tcx); + let instance_ty = instance.ty(*self.tcx); match instance_ty.sty { ty::TyFnDef(..) => { - let real_sig = instance_ty.fn_sig(self.tcx); + let real_sig = instance_ty.fn_sig(*self.tcx); let sig = self.tcx.erase_late_bound_regions_and_normalize(&sig); let real_sig = self.tcx.erase_late_bound_regions_and_normalize(&real_sig); if !self.check_sig_compat(sig, real_sig)? { @@ -88,7 +87,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { } ty::TyFnDef(def_id, substs) => ( self.resolve(def_id, substs)?, - func.ty.fn_sig(self.tcx), + func.ty.fn_sig(*self.tcx), ), _ => { let msg = format!("can't handle callee of type {:?}", func.ty); @@ -117,7 +116,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { let ty = self.tcx.trans_apply_param_substs(self.substs(), &ty); trace!("TerminatorKind::drop: {:?}, type {}", location, ty); - let instance = eval_context::resolve_drop_in_place(self.tcx, ty); + let instance = ::monomorphize::resolve_drop_in_place(*self.tcx, ty); self.drop_place( place, instance, diff --git a/src/librustc_mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs index 03b215fa0b8ea..c67cf1c84bf0b 100644 --- a/src/librustc_mir/interpret/traits.rs +++ b/src/librustc_mir/interpret/traits.rs @@ -3,8 +3,7 @@ use rustc::ty::layout::{Size, Align, LayoutOf}; use syntax::ast::Mutability; use rustc::mir::interpret::{PrimVal, Value, MemoryPointer, EvalResult}; -use super::{EvalContext, eval_context, - Machine}; +use super::{EvalContext, Machine}; impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { /// Creates a dynamic vtable for the given type and vtable origin. This is used only for @@ -34,7 +33,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { None, )?; - let drop = eval_context::resolve_drop_in_place(self.tcx, ty); + let drop = ::monomorphize::resolve_drop_in_place(*self.tcx, ty); let drop = self.memory.create_fn_alloc(drop); self.memory.write_ptr_sized_unsigned(vtable, ptr_align, PrimVal::Ptr(drop))?; diff --git a/src/test/ui/infinite-recursion-const-fn.rs b/src/test/ui/infinite-recursion-const-fn.rs index 51de304405a0a..f98074bc554bb 100644 --- a/src/test/ui/infinite-recursion-const-fn.rs +++ b/src/test/ui/infinite-recursion-const-fn.rs @@ -11,8 +11,8 @@ //https://github.com/rust-lang/rust/issues/31364 #![feature(const_fn)] -const fn a() -> usize { b() } -const fn b() -> usize { a() } //~ ERROR constant evaluation error +const fn a() -> usize { b() } //~ ERROR constant evaluation error +const fn b() -> usize { a() } const ARR: [i32; a()] = [5; 6]; fn main(){} From d98a2b90b2043e8b25701841c10e4598a3da2845 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 9 Feb 2018 09:38:45 +0100 Subject: [PATCH 091/110] Rebase fallout --- src/librustc_mir/hair/cx/expr.rs | 1 - src/librustc_mir/hair/pattern/mod.rs | 23 +- src/librustc_mir/hair/pattern/pattern.rs | 1187 ----------------- .../transform/uniform_array_move_out.rs | 4 +- src/test/mir-opt/match_false_edges.rs | 2 +- src/test/ui/error-codes/E0030-teach.rs | 2 +- 6 files changed, 22 insertions(+), 1197 deletions(-) delete mode 100644 src/librustc_mir/hair/pattern/pattern.rs diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index 6b5a5c5984216..da25969bf1177 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -18,7 +18,6 @@ use rustc::middle::const_val::ConstVal; use rustc::mir::interpret::{GlobalId, Value, PrimVal}; use rustc::ty::{self, AdtKind, VariantDef, Ty}; use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow, AutoBorrowMutability}; -use rustc::mir::interpret::{Value, PrimVal}; use rustc::ty::cast::CastKind as TyCastKind; use rustc::hir; use rustc::hir::def_id::LocalDefId; diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs index 1314f5eb58d32..42e095a0b5beb 100644 --- a/src/librustc_mir/hair/pattern/mod.rs +++ b/src/librustc_mir/hair/pattern/mod.rs @@ -42,7 +42,6 @@ pub enum PatternError { StaticInPattern(Span), FloatBug, NonConstPath(Span), - AssociatedConstInPattern(Span), } #[derive(Copy, Clone, Debug)] @@ -382,10 +381,24 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { "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(); + let mut err = struct_span_err!( + self.tcx.sess, + lo_expr.span, + E0030, + "lower range bound must be less than or equal to upper" + ); + err.span_label( + lo_expr.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(); }, (RangeEnd::Included, _) => {} } diff --git a/src/librustc_mir/hair/pattern/pattern.rs b/src/librustc_mir/hair/pattern/pattern.rs deleted file mode 100644 index 2678984092a52..0000000000000 --- a/src/librustc_mir/hair/pattern/pattern.rs +++ /dev/null @@ -1,1187 +0,0 @@ -// Copyright 2016 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use interpret::{const_val_field, const_discr}; - -use rustc::middle::const_val::ConstVal; -use rustc::mir::{Field, BorrowKind, Mutability}; -use rustc::mir::interpret::{GlobalId, Value, PrimVal}; -use rustc::ty::{self, TyCtxt, AdtDef, Ty, Region}; -use rustc::ty::subst::{Substs, Kind}; -use rustc::hir::{self, PatKind, RangeEnd}; -use rustc::hir::def::{Def, CtorKind}; -use rustc::hir::pat_util::EnumerateAndAdjustIterator; - -use rustc_data_structures::indexed_vec::Idx; -use rustc_const_math::ConstFloat; - -use std::cmp::Ordering; -use std::fmt; -use syntax::ast; -use syntax::ptr::P; -use syntax_pos::Span; - -#[derive(Clone, Debug)] -pub enum PatternError { - AssociatedConstInPattern(Span), - StaticInPattern(Span), - FloatBug, - NonConstPath(Span), -} - -#[derive(Copy, Clone, Debug)] -pub enum BindingMode<'tcx> { - ByValue, - ByRef(Region<'tcx>, BorrowKind), -} - -#[derive(Clone, Debug)] -pub struct FieldPattern<'tcx> { - pub field: Field, - pub pattern: Pattern<'tcx>, -} - -#[derive(Clone, Debug)] -pub struct Pattern<'tcx> { - pub ty: Ty<'tcx>, - pub span: Span, - pub kind: Box>, -} - -#[derive(Clone, Debug)] -pub enum PatternKind<'tcx> { - Wild, - - /// x, ref x, x @ P, etc - Binding { - mutability: Mutability, - name: ast::Name, - mode: BindingMode<'tcx>, - var: ast::NodeId, - ty: Ty<'tcx>, - subpattern: Option>, - }, - - /// Foo(...) or Foo{...} or Foo, where `Foo` is a variant name from an adt with >1 variants - Variant { - adt_def: &'tcx AdtDef, - substs: &'tcx Substs<'tcx>, - variant_index: usize, - subpatterns: Vec>, - }, - - /// (...), Foo(...), Foo{...}, or Foo, where `Foo` is a variant name from an adt with 1 variant - Leaf { - subpatterns: Vec>, - }, - - /// box P, &P, &mut P, etc - Deref { - subpattern: Pattern<'tcx>, - }, - - Constant { - value: &'tcx ty::Const<'tcx>, - }, - - Range { - lo: &'tcx ty::Const<'tcx>, - hi: &'tcx ty::Const<'tcx>, - end: RangeEnd, - }, - - /// matches against a slice, checking the length and extracting elements. - /// irrefutable when there is a slice pattern and both `prefix` and `suffix` are empty. - /// e.g. `&[ref xs..]`. - Slice { - prefix: Vec>, - slice: Option>, - suffix: Vec>, - }, - - /// fixed match against an array, irrefutable - Array { - prefix: Vec>, - slice: Option>, - suffix: Vec>, - }, -} - -fn print_const_val(value: &ty::Const, f: &mut fmt::Formatter) -> fmt::Result { - match value.val { - ConstVal::Value(v) => print_miri_value(v, value.ty, f), - ConstVal::Unevaluated(..) => bug!("{:?} not printable in a pattern", value) - } -} - -fn print_miri_value(value: Value, ty: Ty, f: &mut fmt::Formatter) -> fmt::Result { - use rustc::ty::TypeVariants::*; - match (value, &ty.sty) { - (Value::ByVal(PrimVal::Bytes(0)), &TyBool) => write!(f, "false"), - (Value::ByVal(PrimVal::Bytes(1)), &TyBool) => write!(f, "true"), - (Value::ByVal(PrimVal::Bytes(n)), &TyUint(..)) => write!(f, "{:?}", n), - (Value::ByVal(PrimVal::Bytes(n)), &TyInt(..)) => write!(f, "{:?}", n as i128), - (Value::ByVal(PrimVal::Bytes(n)), &TyChar) => - write!(f, "{:?}", ::std::char::from_u32(n as u32).unwrap()), - _ => bug!("{:?}: {} not printable in a pattern", value, ty), - } -} - -impl<'tcx> fmt::Display for Pattern<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self.kind { - PatternKind::Wild => write!(f, "_"), - PatternKind::Binding { mutability, name, mode, ref subpattern, .. } => { - let is_mut = match mode { - BindingMode::ByValue => mutability == Mutability::Mut, - BindingMode::ByRef(_, bk) => { - write!(f, "ref ")?; - match bk { BorrowKind::Mut { .. } => true, _ => false } - } - }; - if is_mut { - write!(f, "mut ")?; - } - write!(f, "{}", name)?; - if let Some(ref subpattern) = *subpattern { - write!(f, " @ {}", subpattern)?; - } - Ok(()) - } - PatternKind::Variant { ref subpatterns, .. } | - PatternKind::Leaf { ref subpatterns } => { - let variant = match *self.kind { - PatternKind::Variant { adt_def, variant_index, .. } => { - Some(&adt_def.variants[variant_index]) - } - _ => if let ty::TyAdt(adt, _) = self.ty.sty { - if !adt.is_enum() { - Some(&adt.variants[0]) - } else { - None - } - } else { - None - } - }; - - let mut first = true; - let mut start_or_continue = || if first { first = false; "" } else { ", " }; - - if let Some(variant) = variant { - write!(f, "{}", variant.name)?; - - // Only for TyAdt we can have `S {...}`, - // which we handle separately here. - if variant.ctor_kind == CtorKind::Fictive { - write!(f, " {{ ")?; - - let mut printed = 0; - for p in subpatterns { - if let PatternKind::Wild = *p.pattern.kind { - continue; - } - let name = variant.fields[p.field.index()].name; - write!(f, "{}{}: {}", start_or_continue(), name, p.pattern)?; - printed += 1; - } - - if printed < variant.fields.len() { - write!(f, "{}..", start_or_continue())?; - } - - return write!(f, " }}"); - } - } - - let num_fields = variant.map_or(subpatterns.len(), |v| v.fields.len()); - if num_fields != 0 || variant.is_none() { - write!(f, "(")?; - for i in 0..num_fields { - write!(f, "{}", start_or_continue())?; - - // Common case: the field is where we expect it. - if let Some(p) = subpatterns.get(i) { - if p.field.index() == i { - write!(f, "{}", p.pattern)?; - continue; - } - } - - // Otherwise, we have to go looking for it. - if let Some(p) = subpatterns.iter().find(|p| p.field.index() == i) { - write!(f, "{}", p.pattern)?; - } else { - write!(f, "_")?; - } - } - write!(f, ")")?; - } - - Ok(()) - } - PatternKind::Deref { ref subpattern } => { - match self.ty.sty { - ty::TyAdt(def, _) if def.is_box() => write!(f, "box ")?, - ty::TyRef(_, mt) => { - write!(f, "&")?; - if mt.mutbl == hir::MutMutable { - write!(f, "mut ")?; - } - } - _ => bug!("{} is a bad Deref pattern type", self.ty) - } - write!(f, "{}", subpattern) - } - PatternKind::Constant { value } => { - print_const_val(value, f) - } - PatternKind::Range { lo, hi, end } => { - print_const_val(lo, f)?; - match end { - RangeEnd::Included => write!(f, "...")?, - RangeEnd::Excluded => write!(f, "..")?, - } - print_const_val(hi, f) - } - PatternKind::Slice { ref prefix, ref slice, ref suffix } | - PatternKind::Array { ref prefix, ref slice, ref suffix } => { - let mut first = true; - let mut start_or_continue = || if first { first = false; "" } else { ", " }; - write!(f, "[")?; - for p in prefix { - write!(f, "{}{}", start_or_continue(), p)?; - } - if let Some(ref slice) = *slice { - write!(f, "{}", start_or_continue())?; - match *slice.kind { - PatternKind::Wild => {} - _ => write!(f, "{}", slice)? - } - write!(f, "..")?; - } - for p in suffix { - write!(f, "{}{}", start_or_continue(), p)?; - } - write!(f, "]") - } - } - } -} - -pub struct PatternContext<'a, 'tcx: 'a> { - pub tcx: TyCtxt<'a, 'tcx, 'tcx>, - pub param_env: ty::ParamEnv<'tcx>, - pub tables: &'a ty::TypeckTables<'tcx>, - pub substs: &'tcx Substs<'tcx>, - pub errors: Vec, -} - -impl<'a, 'tcx> Pattern<'tcx> { - pub fn from_hir(tcx: TyCtxt<'a, 'tcx, 'tcx>, - param_env_and_substs: ty::ParamEnvAnd<'tcx, &'tcx Substs<'tcx>>, - tables: &'a ty::TypeckTables<'tcx>, - pat: &'tcx hir::Pat) -> Self { - let mut pcx = PatternContext::new(tcx, param_env_and_substs, tables); - let result = pcx.lower_pattern(pat); - if !pcx.errors.is_empty() { - let msg = format!("encountered errors lowering pattern: {:?}", pcx.errors); - tcx.sess.delay_span_bug(pat.span, &msg); - } - debug!("Pattern::from_hir({:?}) = {:?}", pat, result); - result - } -} - -impl<'a, 'tcx> PatternContext<'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 { - PatternContext { - tcx, - param_env: param_env_and_substs.param_env, - tables, - substs: param_env_and_substs.value, - errors: vec![] - } - } - - pub fn lower_pattern(&mut self, pat: &'tcx hir::Pat) -> Pattern<'tcx> { - // When implicit dereferences have been inserted in this pattern, the unadjusted lowered - // pattern has the type that results *after* dereferencing. For example, in this code: - // - // ``` - // match &&Some(0i32) { - // Some(n) => { ... }, - // _ => { ... }, - // } - // ``` - // - // the type assigned to `Some(n)` in `unadjusted_pat` would be `Option` (this is - // determined in rustc_typeck::check::match). The adjustments would be - // - // `vec![&&Option, &Option]`. - // - // Applying the adjustments, we want to instead output `&&Some(n)` (as a HAIR pattern). So - // we wrap the unadjusted pattern in `PatternKind::Deref` repeatedly, consuming the - // adjustments in *reverse order* (last-in-first-out, so that the last `Deref` inserted - // gets the least-dereferenced type). - let unadjusted_pat = self.lower_pattern_unadjusted(pat); - self.tables - .pat_adjustments() - .get(pat.hir_id) - .unwrap_or(&vec![]) - .iter() - .rev() - .fold(unadjusted_pat, |pat, ref_ty| { - debug!("{:?}: wrapping pattern with type {:?}", pat, ref_ty); - Pattern { - span: pat.span, - ty: ref_ty, - kind: Box::new(PatternKind::Deref { subpattern: pat }), - } - }, - ) - } - - fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat) -> Pattern<'tcx> { - let mut ty = self.tables.node_id_to_type(pat.hir_id); - - let kind = match pat.node { - PatKind::Wild => PatternKind::Wild, - - PatKind::Lit(ref value) => self.lower_lit(value), - - 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 - } - } - - PatKind::Path(ref qpath) => { - return self.lower_path(qpath, pat.hir_id, pat.span); - } - - PatKind::Ref(ref subpattern, _) | - PatKind::Box(ref subpattern) => { - PatternKind::Deref { subpattern: self.lower_pattern(subpattern) } - } - - PatKind::Slice(ref prefix, ref slice, ref suffix) => { - let ty = self.tables.node_id_to_type(pat.hir_id); - match ty.sty { - ty::TyRef(_, mt) => - PatternKind::Deref { - subpattern: Pattern { - ty: mt.ty, - span: pat.span, - kind: Box::new(self.slice_or_array_pattern( - pat.span, mt.ty, prefix, slice, suffix)) - }, - }, - - ty::TySlice(..) | - ty::TyArray(..) => - self.slice_or_array_pattern(pat.span, ty, prefix, slice, suffix), - - ref sty => - span_bug!( - pat.span, - "unexpanded type for vector pattern: {:?}", - sty), - } - } - - PatKind::Tuple(ref subpatterns, ddpos) => { - let ty = self.tables.node_id_to_type(pat.hir_id); - match ty.sty { - ty::TyTuple(ref tys, _) => { - let subpatterns = - subpatterns.iter() - .enumerate_and_adjust(tys.len(), ddpos) - .map(|(i, subpattern)| FieldPattern { - field: Field::new(i), - pattern: self.lower_pattern(subpattern) - }) - .collect(); - - PatternKind::Leaf { subpatterns: subpatterns } - } - - ref sty => span_bug!(pat.span, "unexpected type for tuple pattern: {:?}", sty), - } - } - - PatKind::Binding(_, id, ref ident, ref sub) => { - let var_ty = self.tables.node_id_to_type(pat.hir_id); - let region = match var_ty.sty { - ty::TyRef(r, _) => Some(r), - _ => None, - }; - let bm = *self.tables.pat_binding_modes().get(pat.hir_id) - .expect("missing binding mode"); - let (mutability, mode) = match bm { - ty::BindByValue(hir::MutMutable) => - (Mutability::Mut, BindingMode::ByValue), - ty::BindByValue(hir::MutImmutable) => - (Mutability::Not, BindingMode::ByValue), - ty::BindByReference(hir::MutMutable) => - (Mutability::Not, BindingMode::ByRef( - region.unwrap(), BorrowKind::Mut { allow_two_phase_borrow: false })), - ty::BindByReference(hir::MutImmutable) => - (Mutability::Not, BindingMode::ByRef( - region.unwrap(), BorrowKind::Shared)), - }; - - // A ref x pattern is the same node used for x, and as such it has - // x's type, which is &T, where we want T (the type being matched). - if let ty::BindByReference(_) = bm { - if let ty::TyRef(_, mt) = ty.sty { - ty = mt.ty; - } else { - bug!("`ref {}` has wrong type {}", ident.node, ty); - } - } - - PatternKind::Binding { - mutability, - mode, - name: ident.node, - var: id, - ty: var_ty, - subpattern: self.lower_opt_pattern(sub), - } - } - - PatKind::TupleStruct(ref qpath, ref subpatterns, ddpos) => { - let def = self.tables.qpath_def(qpath, pat.hir_id); - let adt_def = match ty.sty { - ty::TyAdt(adt_def, _) => adt_def, - _ => span_bug!(pat.span, "tuple struct pattern not applied to an ADT"), - }; - let variant_def = adt_def.variant_of_def(def); - - let subpatterns = - subpatterns.iter() - .enumerate_and_adjust(variant_def.fields.len(), ddpos) - .map(|(i, field)| FieldPattern { - field: Field::new(i), - pattern: self.lower_pattern(field), - }) - .collect(); - self.lower_variant_or_leaf(def, pat.span, ty, subpatterns) - } - - PatKind::Struct(ref qpath, ref fields, _) => { - let def = self.tables.qpath_def(qpath, pat.hir_id); - let adt_def = match ty.sty { - ty::TyAdt(adt_def, _) => adt_def, - _ => { - span_bug!( - pat.span, - "struct pattern not applied to an ADT"); - } - }; - let variant_def = adt_def.variant_of_def(def); - - let subpatterns = - fields.iter() - .map(|field| { - let index = variant_def.index_of_field_named(field.node.name); - let index = index.unwrap_or_else(|| { - span_bug!( - pat.span, - "no field with name {:?}", - field.node.name); - }); - FieldPattern { - field: Field::new(index), - pattern: self.lower_pattern(&field.node.pat), - } - }) - .collect(); - - self.lower_variant_or_leaf(def, pat.span, ty, subpatterns) - } - }; - - Pattern { - span: pat.span, - ty, - kind: Box::new(kind), - } - } - - fn lower_patterns(&mut self, pats: &'tcx [P]) -> Vec> { - pats.iter().map(|p| self.lower_pattern(p)).collect() - } - - fn lower_opt_pattern(&mut self, pat: &'tcx Option>) -> Option> - { - pat.as_ref().map(|p| self.lower_pattern(p)) - } - - fn flatten_nested_slice_patterns( - &mut self, - prefix: Vec>, - slice: Option>, - suffix: Vec>) - -> (Vec>, Option>, Vec>) - { - let orig_slice = match slice { - Some(orig_slice) => orig_slice, - None => return (prefix, slice, suffix) - }; - let orig_prefix = prefix; - let orig_suffix = suffix; - - // dance because of intentional borrow-checker stupidity. - let kind = *orig_slice.kind; - match kind { - PatternKind::Slice { prefix, slice, mut suffix } | - PatternKind::Array { prefix, slice, mut suffix } => { - let mut orig_prefix = orig_prefix; - - orig_prefix.extend(prefix); - suffix.extend(orig_suffix); - - (orig_prefix, slice, suffix) - } - _ => { - (orig_prefix, Some(Pattern { - kind: box kind, ..orig_slice - }), orig_suffix) - } - } - } - - fn slice_or_array_pattern( - &mut self, - span: Span, - ty: Ty<'tcx>, - prefix: &'tcx [P], - slice: &'tcx Option>, - suffix: &'tcx [P]) - -> PatternKind<'tcx> - { - let prefix = self.lower_patterns(prefix); - let slice = self.lower_opt_pattern(slice); - let suffix = self.lower_patterns(suffix); - let (prefix, slice, suffix) = - self.flatten_nested_slice_patterns(prefix, slice, suffix); - - match ty.sty { - ty::TySlice(..) => { - // matching a slice or fixed-length array - PatternKind::Slice { prefix: prefix, slice: slice, suffix: suffix } - } - - ty::TyArray(_, len) => { - // fixed-length array - let len = len.val.unwrap_u64(); - assert!(len >= prefix.len() as u64 + suffix.len() as u64); - PatternKind::Array { prefix: prefix, slice: slice, suffix: suffix } - } - - _ => { - span_bug!(span, "bad slice pattern type {:?}", ty); - } - } - } - - fn lower_variant_or_leaf( - &mut self, - def: Def, - span: Span, - ty: Ty<'tcx>, - subpatterns: Vec>) - -> PatternKind<'tcx> - { - match def { - Def::Variant(variant_id) | Def::VariantCtor(variant_id, ..) => { - let enum_id = self.tcx.parent_def_id(variant_id).unwrap(); - let adt_def = self.tcx.adt_def(enum_id); - if adt_def.is_enum() { - let substs = match ty.sty { - ty::TyAdt(_, substs) | - ty::TyFnDef(_, substs) => substs, - _ => bug!("inappropriate type for def: {:?}", ty.sty), - }; - PatternKind::Variant { - adt_def, - substs, - variant_index: adt_def.variant_index_with_id(variant_id), - subpatterns, - } - } else { - PatternKind::Leaf { subpatterns: subpatterns } - } - } - - Def::Struct(..) | Def::StructCtor(..) | Def::Union(..) | - Def::TyAlias(..) | Def::AssociatedTy(..) | Def::SelfTy(..) => { - PatternKind::Leaf { subpatterns: subpatterns } - } - - _ => { - self.errors.push(PatternError::NonConstPath(span)); - PatternKind::Wild - } - } - } - - fn lower_path(&mut self, - qpath: &hir::QPath, - id: hir::HirId, - span: Span) - -> Pattern<'tcx> { - let ty = self.tables.node_id_to_type(id); - let def = self.tables.qpath_def(qpath, id); - let is_associated_const = match def { - Def::AssociatedConst(_) => true, - _ => false, - }; - let kind = match def { - Def::Const(def_id) | Def::AssociatedConst(def_id) => { - let substs = self.tables.node_substs(id); - match ty::Instance::resolve( - self.tcx, - self.param_env, - def_id, - substs, - ) { - Some(instance) => { - let cid = GlobalId { - instance, - promoted: None, - }; - match self.tcx.at(span).const_eval(self.param_env.and(cid)) { - Ok(value) => { - return self.const_to_pat(instance, value, id, span) - }, - Err(err) => { - err.report(self.tcx, span, "pattern"); - PatternKind::Wild - }, - } - }, - None => { - self.errors.push(if is_associated_const { - PatternError::AssociatedConstInPattern(span) - } else { - PatternError::StaticInPattern(span) - }); - PatternKind::Wild - }, - } - } - _ => self.lower_variant_or_leaf(def, span, ty, vec![]), - }; - - Pattern { - span, - ty, - kind: Box::new(kind), - } - } - - fn lower_lit(&mut self, expr: &'tcx hir::Expr) -> PatternKind<'tcx> { - match expr.node { - hir::ExprLit(ref lit) => { - let ty = self.tables.expr_ty(expr); - match 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(()) => { - self.errors.push(PatternError::FloatBug); - 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 lit_to_const(&lit.node, self.tcx, ty, true) { - 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(()) => { - self.errors.push(PatternError::FloatBug); - PatternKind::Wild - }, - } - } - _ => span_bug!(expr.span, "not a literal: {:?}", expr), - } - } - - fn const_to_pat( - &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(_) => { - 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"); - PatternKind::Wild - } - ty::TyAdt(adt_def, _) if !self.tcx.has_attr(adt_def.did, "structural_match") => { - let msg = format!("to use a constant of type `{}` in a pattern, \ - `{}` must be annotated with `#[derive(PartialEq, Eq)]`", - self.tcx.item_path_str(adt_def.did), - self.tcx.item_path_str(adt_def.did)); - self.tcx.sess.span_err(span, &msg); - PatternKind::Wild - }, - ty::TyAdt(adt_def, substs) if adt_def.is_enum() => { - match cv.val { - ConstVal::Value(val) => { - let discr = const_discr( - self.tcx, self.param_env, instance, val, cv.ty - ).unwrap(); - let variant_index = adt_def - .discriminants(self.tcx) - .position(|var| var.val == discr) - .unwrap(); - PatternKind::Variant { - adt_def, - substs, - variant_index, - subpatterns: adt_def - .variants[variant_index] - .fields - .iter() - .enumerate() - .map(|(i, _)| { - 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, - ).unwrap(), - _ => bug!("{:#?} is not a valid tuple", cv), - }; - FieldPattern { - field, - pattern: self.const_to_pat(instance, val, id, span), - } - }).collect(), - } - }, - _ => return Pattern { - span, - ty: cv.ty, - kind: Box::new(PatternKind::Constant { - value: cv, - }), - } - } - }, - ty::TyAdt(adt_def, _) => { - let struct_var = adt_def.non_enum_variant(); - PatternKind::Leaf { - subpatterns: struct_var.fields.iter().enumerate().map(|(i, _)| { - let field = Field::new(i); - let val = match cv.val { - 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, id, span), - } - }).collect() - } - } - ty::TyTuple(fields, _) => { - PatternKind::Leaf { - subpatterns: (0..fields.len()).map(|i| { - let field = Field::new(i); - let val = match cv.val { - 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, id, span), - } - }).collect() - } - } - ty::TyArray(_, n) => { - 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::Value(miri) => const_val_field( - self.tcx, self.param_env, instance, None, field, miri, cv.ty, - ).unwrap(), - _ => bug!("{:#?} is not a valid tuple", cv), - }; - self.const_to_pat(instance, val, id, span) - }).collect(), - slice: None, - suffix: Vec::new(), - } - } - _ => { - PatternKind::Constant { - value: cv, - } - }, - }; - - Pattern { - span, - ty: cv.ty, - kind: Box::new(kind), - } - } -} - -pub trait PatternFoldable<'tcx> : Sized { - fn fold_with>(&self, folder: &mut F) -> Self { - self.super_fold_with(folder) - } - - fn super_fold_with>(&self, folder: &mut F) -> Self; -} - -pub trait PatternFolder<'tcx> : Sized { - fn fold_pattern(&mut self, pattern: &Pattern<'tcx>) -> Pattern<'tcx> { - pattern.super_fold_with(self) - } - - fn fold_pattern_kind(&mut self, kind: &PatternKind<'tcx>) -> PatternKind<'tcx> { - kind.super_fold_with(self) - } -} - - -impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Box { - fn super_fold_with>(&self, folder: &mut F) -> Self { - let content: T = (**self).fold_with(folder); - box content - } -} - -impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Vec { - fn super_fold_with>(&self, folder: &mut F) -> Self { - self.iter().map(|t| t.fold_with(folder)).collect() - } -} - -impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Option { - fn super_fold_with>(&self, folder: &mut F) -> Self{ - self.as_ref().map(|t| t.fold_with(folder)) - } -} - -macro_rules! CloneImpls { - (<$lt_tcx:tt> $($ty:ty),+) => { - $( - impl<$lt_tcx> PatternFoldable<$lt_tcx> for $ty { - fn super_fold_with>(&self, _: &mut F) -> Self { - Clone::clone(self) - } - } - )+ - } -} - -CloneImpls!{ <'tcx> - Span, Field, Mutability, ast::Name, ast::NodeId, usize, &'tcx ty::Const<'tcx>, - Region<'tcx>, Ty<'tcx>, BindingMode<'tcx>, &'tcx AdtDef, - &'tcx Substs<'tcx>, &'tcx Kind<'tcx> -} - -impl<'tcx> PatternFoldable<'tcx> for FieldPattern<'tcx> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - FieldPattern { - field: self.field.fold_with(folder), - pattern: self.pattern.fold_with(folder) - } - } -} - -impl<'tcx> PatternFoldable<'tcx> for Pattern<'tcx> { - fn fold_with>(&self, folder: &mut F) -> Self { - folder.fold_pattern(self) - } - - fn super_fold_with>(&self, folder: &mut F) -> Self { - Pattern { - ty: self.ty.fold_with(folder), - span: self.span.fold_with(folder), - kind: self.kind.fold_with(folder) - } - } -} - -impl<'tcx> PatternFoldable<'tcx> for PatternKind<'tcx> { - fn fold_with>(&self, folder: &mut F) -> Self { - folder.fold_pattern_kind(self) - } - - fn super_fold_with>(&self, folder: &mut F) -> Self { - match *self { - PatternKind::Wild => PatternKind::Wild, - PatternKind::Binding { - mutability, - name, - mode, - var, - ty, - ref subpattern, - } => PatternKind::Binding { - mutability: mutability.fold_with(folder), - name: name.fold_with(folder), - mode: mode.fold_with(folder), - var: var.fold_with(folder), - ty: ty.fold_with(folder), - subpattern: subpattern.fold_with(folder), - }, - PatternKind::Variant { - adt_def, - substs, - variant_index, - ref subpatterns, - } => PatternKind::Variant { - adt_def: adt_def.fold_with(folder), - substs: substs.fold_with(folder), - variant_index: variant_index.fold_with(folder), - subpatterns: subpatterns.fold_with(folder) - }, - PatternKind::Leaf { - ref subpatterns, - } => PatternKind::Leaf { - subpatterns: subpatterns.fold_with(folder), - }, - PatternKind::Deref { - ref subpattern, - } => PatternKind::Deref { - subpattern: subpattern.fold_with(folder), - }, - PatternKind::Constant { - value - } => PatternKind::Constant { - value: value.fold_with(folder) - }, - PatternKind::Range { - lo, - hi, - end, - } => PatternKind::Range { - lo: lo.fold_with(folder), - hi: hi.fold_with(folder), - end, - }, - PatternKind::Slice { - ref prefix, - ref slice, - ref suffix, - } => PatternKind::Slice { - prefix: prefix.fold_with(folder), - slice: slice.fold_with(folder), - suffix: suffix.fold_with(folder) - }, - PatternKind::Array { - ref prefix, - ref slice, - ref suffix - } => PatternKind::Array { - prefix: prefix.fold_with(folder), - slice: slice.fold_with(folder), - suffix: suffix.fold_with(folder) - }, - } - } -} - -pub fn compare_const_vals(a: &ConstVal, b: &ConstVal, ty: Ty) -> Option { - use rustc_const_math::ConstFloat; - trace!("compare_const_vals: {:?}, {:?}", a, b); - use rustc::mir::interpret::{Value, PrimVal}; - match (a, b) { - (&ConstVal::Value(Value::ByVal(PrimVal::Bytes(a))), - &ConstVal::Value(Value::ByVal(PrimVal::Bytes(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, - } -} - -fn lit_to_const<'a, 'tcx>(lit: &'tcx ast::LitKind, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - ty: Ty<'tcx>, - neg: bool) - -> Result, ()> { - use syntax::ast::*; - - 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 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)), - }; - Ok(ConstVal::Value(lit)) -} - -fn parse_float<'tcx>(num: &str, fty: ast::FloatTy) - -> Result { - ConstFloat::from_str(num, fty).map_err(|_| ()) -} diff --git a/src/librustc_mir/transform/uniform_array_move_out.rs b/src/librustc_mir/transform/uniform_array_move_out.rs index e46de17047986..983a44920ea6d 100644 --- a/src/librustc_mir/transform/uniform_array_move_out.rs +++ b/src/librustc_mir/transform/uniform_array_move_out.rs @@ -81,8 +81,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UniformArrayMoveOutVisitor<'a, 'tcx> { } else { let place_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx); if let ty::TyArray(item_ty, const_size) = place_ty.sty { - if let Some(size) = const_size.val.to_const_int().and_then(|v| v.to_u64()) { - assert!(size <= (u32::max_value() as u64), + if let Some(size) = const_size.val.to_raw_bits() { + assert!(size <= (u32::max_value() as u128), "unform array move out doesn't supported for array bigger then u32"); self.uniform(location, dst_place, proj, item_ty, size as u32); diff --git a/src/test/mir-opt/match_false_edges.rs b/src/test/mir-opt/match_false_edges.rs index 596bb4e115dfc..53f178619975e 100644 --- a/src/test/mir-opt/match_false_edges.rs +++ b/src/test/mir-opt/match_false_edges.rs @@ -239,7 +239,7 @@ fn main() { // _11 = const guard2(move _12) -> [return: bb14, unwind: bb1]; // } // bb14: { // end of guard2 -// StorageDead(_11); +// StorageDead(_12); // switchInt(move _11) -> [false: bb15, otherwise: bb3]; // } // bb15: { // to pre_binding4 diff --git a/src/test/ui/error-codes/E0030-teach.rs b/src/test/ui/error-codes/E0030-teach.rs index 2af32eda62be9..e49b992139e02 100644 --- a/src/test/ui/error-codes/E0030-teach.rs +++ b/src/test/ui/error-codes/E0030-teach.rs @@ -11,7 +11,7 @@ // compile-flags: -Z teach fn main() { - match 5u32 { + match 5u32 { //~ERROR non-exhaustive patterns: `_` not covered 1000 ... 5 => {} //~^ ERROR lower range bound must be less than or equal to upper } From b63c6bbfee45f90fb1b99016fd90cb5c22847a95 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 19 Feb 2018 10:26:53 +0100 Subject: [PATCH 092/110] Add regression test --- src/test/run-pass/ctfe/ice-48279.rs | 34 +++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/test/run-pass/ctfe/ice-48279.rs diff --git a/src/test/run-pass/ctfe/ice-48279.rs b/src/test/run-pass/ctfe/ice-48279.rs new file mode 100644 index 0000000000000..c435e5fdaab4a --- /dev/null +++ b/src/test/run-pass/ctfe/ice-48279.rs @@ -0,0 +1,34 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// https://github.com/rust-lang/rust/issues/48279 + +#![feature(const_fn)] + +#[derive(PartialEq, Eq)] +pub struct NonZeroU32 { + value: u32 +} + +impl NonZeroU32 { + const unsafe fn new_unchecked(value: u32) -> Self { + NonZeroU32 { value } + } +} + +//pub const FOO_ATOM: NonZeroU32 = unsafe { NonZeroU32::new_unchecked(7) }; +pub const FOO_ATOM: NonZeroU32 = unsafe { NonZeroU32 { value: 7 } }; + +fn main() { + match None { + Some(FOO_ATOM) => {} + _ => {} + } +} From 0ca4b45a0ccf9e29a17f0613fb280411910b3dbd Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 19 Feb 2018 12:00:15 +0100 Subject: [PATCH 093/110] Step limit is now terminator limit --- src/librustc/mir/interpret/error.rs | 2 +- src/librustc_mir/interpret/eval_context.rs | 2 +- src/librustc_mir/interpret/step.rs | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs index bb27628fa9c73..d6df340e2f68f 100644 --- a/src/librustc/mir/interpret/error.rs +++ b/src/librustc/mir/interpret/error.rs @@ -196,7 +196,7 @@ impl<'tcx> Error for EvalError<'tcx> { OutOfMemory{..} => "could not allocate more memory", ExecutionTimeLimitReached => - "reached the configured maximum execution time", + "the expression was too complex to be evaluated or resulted in an infinite loop", StackFrameLimitReached => "reached the configured maximum number of stack frames", OutOfTls => diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index f46d563106083..64e823564671b 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -41,7 +41,7 @@ pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { /// The maximum number of stack frames allowed pub(crate) stack_limit: usize, - /// The maximum number of operations that may be executed. + /// The maximum number of terminators that may be evaluated. /// This prevents infinite loops and huge computations from freezing up const eval. /// Remove once halting problem is solved. pub(crate) steps_remaining: usize, diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs index 54fd364d3f820..4e1750caf26ba 100644 --- a/src/librustc_mir/interpret/step.rs +++ b/src/librustc_mir/interpret/step.rs @@ -19,7 +19,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { /// Returns true as long as there are more things to do. pub fn step(&mut self) -> EvalResult<'tcx, bool> { - self.inc_step_counter_and_check_limit(1)?; if self.stack.is_empty() { return Ok(false); } @@ -37,6 +36,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { return Ok(true); } + self.inc_step_counter_and_check_limit(1)?; + let terminator = basic_block.terminator(); assert_eq!(old_frames, self.cur_frame()); self.terminator(terminator)?; From 134c2910ec53016dd4319206921c623e8c9a57b7 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 20 Feb 2018 10:32:33 +0100 Subject: [PATCH 094/110] Const eval will oom together with rustc now --- src/librustc/ich/impls_ty.rs | 9 --------- src/librustc/mir/interpret/error.rs | 10 ---------- src/librustc/session/mod.rs | 3 --- src/librustc/ty/structural_impls.rs | 5 ----- src/librustc_mir/interpret/memory.rs | 17 ----------------- 5 files changed, 44 deletions(-) diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index d5476f742c896..874c7c27deaa6 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -562,15 +562,6 @@ for ::mir::interpret::EvalError<'gcx> { }, Intrinsic(ref s) => s.hash_stable(hcx, hasher), InvalidChar(c) => c.hash_stable(hcx, hasher), - OutOfMemory { - allocation_size, - memory_size, - memory_usage, - } => { - allocation_size.hash_stable(hcx, hasher); - memory_size.hash_stable(hcx, hasher); - memory_usage.hash_stable(hcx, hasher) - }, AbiViolation(ref s) => s.hash_stable(hcx, hasher), AlignmentCheckFailed { required, diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs index d6df340e2f68f..51660b180cd98 100644 --- a/src/librustc/mir/interpret/error.rs +++ b/src/librustc/mir/interpret/error.rs @@ -65,11 +65,6 @@ pub enum EvalErrorKind<'tcx> { Intrinsic(String), OverflowingMath, InvalidChar(u128), - OutOfMemory { - allocation_size: u64, - memory_size: u64, - memory_usage: u64, - }, ExecutionTimeLimitReached, StackFrameLimitReached, OutOfTls, @@ -193,8 +188,6 @@ impl<'tcx> Error for EvalError<'tcx> { "mir not found", InvalidChar(..) => "tried to interpret an invalid 32-bit value as a char", - OutOfMemory{..} => - "could not allocate more memory", ExecutionTimeLimitReached => "the expression was too complex to be evaluated or resulted in an infinite loop", StackFrameLimitReached => @@ -297,9 +290,6 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { write!(f, "{}", err), InvalidChar(c) => write!(f, "tried to interpret an invalid 32-bit value as a char: {}", c), - OutOfMemory { allocation_size, memory_size, memory_usage } => - write!(f, "tried to allocate {} more bytes, but only {} bytes are free of the {} byte memory", - allocation_size, memory_size - memory_usage, memory_size), AlignmentCheckFailed { required, has } => write!(f, "tried to access memory with alignment {}, but alignment {} is required", has, required), diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 232344a036783..defc5731f2f8a 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -107,8 +107,6 @@ pub struct Session { pub const_eval_stack_frame_limit: Cell, /// The maximum number miri steps per constant pub const_eval_step_limit: Cell, - /// The maximum number of virtual bytes per constant - pub const_eval_memory_limit: Cell, /// The metadata::creader module may inject an allocator/panic_runtime /// dependency if it didn't already find one, and this tracks what was @@ -1013,7 +1011,6 @@ pub fn build_session_(sopts: config::Options, type_length_limit: Cell::new(1048576), const_eval_stack_frame_limit: Cell::new(100), const_eval_step_limit: Cell::new(1_000_000), - const_eval_memory_limit: Cell::new(100 * 1024 * 1024), // 100 MB next_node_id: Cell::new(NodeId::new(1)), injected_allocator: Cell::new(None), allocator_kind: Cell::new(None), diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 243e17ee5c7ec..78fccaa113106 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -625,11 +625,6 @@ impl<'a, 'tcx> Lift<'tcx> for interpret::EvalError<'a> { Intrinsic(ref s) => Intrinsic(s.clone()), OverflowingMath => OverflowingMath, InvalidChar(c) => InvalidChar(c), - OutOfMemory { - allocation_size, - memory_size, - memory_usage, - } => OutOfMemory { allocation_size, memory_size, memory_usage }, ExecutionTimeLimitReached => ExecutionTimeLimitReached, StackFrameLimitReached => StackFrameLimitReached, OutOfTls => OutOfTls, diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 7dceacabb292f..72a966ab38d61 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -43,12 +43,6 @@ pub struct Memory<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { /// Stores statics while they are being processed, before they are interned and thus frozen uninitialized_statics: HashMap, - /// Number of virtual bytes allocated. - memory_usage: u64, - - /// Maximum number of virtual bytes that may be allocated. - memory_size: u64, - /// The current stack frame. Used to check accesses against locks. pub cur_frame: usize, @@ -63,8 +57,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { alloc_map: HashMap::new(), uninitialized_statics: HashMap::new(), tcx, - memory_size: tcx.sess.const_eval_memory_limit.get(), - memory_usage: 0, cur_frame: usize::max_value(), } } @@ -92,14 +84,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { align: Align, kind: Option>, ) -> EvalResult<'tcx, MemoryPointer> { - if self.memory_size - self.memory_usage < size { - return err!(OutOfMemory { - allocation_size: size, - memory_size: self.memory_size, - memory_usage: self.memory_usage, - }); - } - self.memory_usage += size; assert_eq!(size as usize as u64, size); let alloc = Allocation { bytes: vec![0; size as usize], @@ -223,7 +207,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { } } - self.memory_usage -= alloc.bytes.len() as u64; debug!("deallocated : {}", ptr.alloc_id); Ok(()) From 7218836500a9444ae5e9f66cd09c0b4e6a64377c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 20 Feb 2018 11:49:50 +0100 Subject: [PATCH 095/110] Fix mozjs crater failure --- src/librustc_mir/interpret/eval_context.rs | 14 ++++++-- src/test/run-pass/ctfe/mozjs-error.rs | 37 ++++++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 src/test/run-pass/ctfe/mozjs-error.rs diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 64e823564671b..e0ad306571d24 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -246,7 +246,10 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M } pub(super) fn resolve(&self, def_id: DefId, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, ty::Instance<'tcx>> { - let substs = self.tcx.trans_apply_param_substs(self.substs(), &substs); + trace!("resolve: {:?}, {:#?}", def_id, substs); + trace!("substs: {:#?}", self.substs()); + trace!("param_env: {:#?}", self.param_env); + let substs = self.tcx.trans_apply_param_substs_env(self.substs(), self.param_env, &substs); ty::Instance::resolve( *self.tcx, self.param_env, @@ -690,8 +693,13 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M bug!("reifying a fn ptr that requires \ const arguments"); } - let instance = self.resolve(def_id, substs)?; - let fn_ptr = self.memory.create_fn_alloc(instance); + let instance: EvalResult<'tcx, _> = ty::Instance::resolve( + *self.tcx, + self.param_env, + def_id, + substs, + ).ok_or(EvalErrorKind::TypeckError.into()); + let fn_ptr = self.memory.create_fn_alloc(instance?); let valty = ValTy { value: Value::ByVal(PrimVal::Ptr(fn_ptr)), ty: dest_ty, diff --git a/src/test/run-pass/ctfe/mozjs-error.rs b/src/test/run-pass/ctfe/mozjs-error.rs new file mode 100644 index 0000000000000..9c8a4b5ae6a2d --- /dev/null +++ b/src/test/run-pass/ctfe/mozjs-error.rs @@ -0,0 +1,37 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct CustomAutoRooterVFTable { + trace: unsafe extern "C" fn(this: *mut i32, trc: *mut u32), +} + +unsafe trait CustomAutoTraceable: Sized { + const vftable: CustomAutoRooterVFTable = CustomAutoRooterVFTable { + trace: Self::trace, + }; + + unsafe extern "C" fn trace(this: *mut i32, trc: *mut u32) { + let this = this as *const Self; + let this = this.as_ref().unwrap(); + Self::do_trace(this, trc); + } + + fn do_trace(&self, trc: *mut u32); +} + +unsafe impl CustomAutoTraceable for () { + fn do_trace(&self, _: *mut u32) { + // nop + } +} + +fn main() { + let _ = <()>::vftable; +} From df283df887431eec0f6da0f4aa85db9c88c9449b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 21 Feb 2018 22:02:52 +0100 Subject: [PATCH 096/110] Don't use the undefined bytes of PrimVal::Bytes --- src/librustc/mir/interpret/mod.rs | 2 +- src/librustc/mir/interpret/value.rs | 28 --- src/librustc/ty/mod.rs | 32 ++- src/librustc_mir/build/expr/as_rvalue.rs | 38 +-- src/librustc_mir/hair/cx/mod.rs | 35 ++- src/librustc_mir/interpret/cast.rs | 105 ++++----- src/librustc_mir/interpret/eval_context.rs | 22 +- src/librustc_mir/interpret/memory.rs | 5 +- src/librustc_mir/interpret/mod.rs | 1 - src/librustc_mir/interpret/operator.rs | 257 ++++++++++----------- src/librustc_mir/transform/const_prop.rs | 5 +- src/test/run-pass/const-negation.rs | 2 + 12 files changed, 255 insertions(+), 277 deletions(-) diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs index 91f7d79108bb8..67f30f53a6810 100644 --- a/src/librustc/mir/interpret/mod.rs +++ b/src/librustc/mir/interpret/mod.rs @@ -10,7 +10,7 @@ mod value; pub use self::error::{EvalError, EvalResult, EvalErrorKind}; -pub use self::value::{PrimVal, PrimValKind, Value, Pointer, bytes_to_f32, bytes_to_f64}; +pub use self::value::{PrimVal, PrimValKind, Value, Pointer}; use std::collections::BTreeMap; use std::fmt; diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc/mir/interpret/value.rs index c00956c0a8570..7289d74bfbb1b 100644 --- a/src/librustc/mir/interpret/value.rs +++ b/src/librustc/mir/interpret/value.rs @@ -4,22 +4,6 @@ use ty::layout::{Align, HasDataLayout}; use ty; use super::{EvalResult, MemoryPointer, PointerArithmetic}; -use syntax::ast::FloatTy; -use rustc_const_math::ConstFloat; - -pub fn bytes_to_f32(bits: u128) -> ConstFloat { - ConstFloat { - bits, - ty: FloatTy::F32, - } -} - -pub fn bytes_to_f64(bits: u128) -> ConstFloat { - ConstFloat { - bits, - ty: FloatTy::F64, - } -} /// A `Value` represents a single self-contained Rust value. /// @@ -182,10 +166,6 @@ impl<'tcx> PrimVal { PrimVal::Bytes(n as u128) } - pub fn from_float(f: ConstFloat) -> Self { - PrimVal::Bytes(f.bits) - } - pub fn from_bool(b: bool) -> Self { PrimVal::Bytes(b as u128) } @@ -260,14 +240,6 @@ impl<'tcx> PrimVal { }) } - pub fn to_f32(self) -> EvalResult<'tcx, ConstFloat> { - self.to_bytes().map(bytes_to_f32) - } - - pub fn to_f64(self) -> EvalResult<'tcx, ConstFloat> { - self.to_bytes().map(bytes_to_f64) - } - pub fn to_bool(self) -> EvalResult<'tcx, bool> { match self.to_bytes()? { 0 => Ok(false), diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index cb09687a6573c..d730c95f4dd88 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1845,10 +1845,34 @@ impl<'a, 'gcx, 'tcx> AdtDef { .. }) => { trace!("discriminants: {} ({:?})", b, repr_type); - discr = Discr { - val: b, - ty: repr_type.to_ty(tcx), - }; + let ty = repr_type.to_ty(tcx); + if ty.is_signed() { + let (ty, param_env) = tcx + .lift_to_global(&(ty, param_env)) + .unwrap_or_else(|| { + bug!("MIR: discriminants({:?}, {:?}) got \ + type with inference types/regions", + ty, param_env); + }); + let size = tcx.global_tcx() + .layout_of(param_env.and(ty)) + .expect("int layout") + .size + .bits(); + let val = b as i128; + // sign extend to i128 + let amt = 128 - size; + let val = (val << amt) >> amt; + discr = Discr { + val: val as u128, + ty, + }; + } else { + discr = Discr { + val: b, + ty, + }; + } } _ => { if !expr_did.is_local() { diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index e2cc58b1fb050..59f44fa9229a9 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -22,7 +22,6 @@ use rustc::middle::region; use rustc::ty::{self, Ty}; use rustc::mir::*; use rustc::mir::interpret::{Value, PrimVal}; -use syntax::ast; use syntax_pos::Span; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { @@ -382,9 +381,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // Helper to get a `-1` value of the appropriate type fn neg_1_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> { + let bits = self.hir.type_bit_size(ty); + let n = (!0u128) >> (128 - bits); let literal = Literal::Value { value: self.hir.tcx().mk_const(ty::Const { - val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(-1i128 as u128))), + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(n))), ty }) }; @@ -394,31 +395,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // Helper to get the minimum value of the appropriate type fn minval_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> { - let literal = match ty.sty { - ty::TyInt(ity) => { - let ity = match ity { - ast::IntTy::Isize => self.hir.tcx().sess.target.isize_ty, - other => other, - }; - let val = match ity { - ast::IntTy::I8 => i8::min_value() as i128, - ast::IntTy::I16 => i16::min_value() as i128, - ast::IntTy::I32 => i32::min_value() as i128, - ast::IntTy::I64 => i64::min_value() as i128, - ast::IntTy::I128 => i128::min_value() as i128, - ast::IntTy::Isize => unreachable!(), - }; - - Literal::Value { - value: self.hir.tcx().mk_const(ty::Const { - val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(val as u128))), - ty - }) - } - } - _ => { - span_bug!(span, "Invalid type for minval_literal: `{:?}`", ty) - } + assert!(ty.is_signed()); + let bits = self.hir.type_bit_size(ty); + let n = 1 << (bits - 1); + let literal = Literal::Value { + value: self.hir.tcx().mk_const(ty::Const { + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(n))), + ty + }) }; self.literal_operand(span, ty, literal) diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index 0b59ce9e117b8..ec484785ca2f0 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -149,6 +149,26 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { } } + pub fn type_bit_size( + &self, + ty: Ty<'tcx>, + ) -> u64 { + let tcx = self.tcx.global_tcx(); + let (ty, param_env) = self + .tcx + .lift_to_global(&(ty, self.param_env)) + .unwrap_or_else(|| { + bug!("MIR: Cx::const_eval_literal({:?}, {:?}) got \ + type with inference types/regions", + ty, self.param_env); + }); + tcx + .layout_of(param_env.and(ty)) + .expect("int layout") + .size + .bits() + } + pub fn const_eval_literal( &mut self, lit: &'tcx ast::LitKind, @@ -156,6 +176,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { sp: Span, neg: bool, ) -> Literal<'tcx> { + trace!("const_eval_literal: {:#?}, {:?}, {:?}, {:?}", lit, ty, sp, neg); let tcx = self.tcx.global_tcx(); let parse_float = |num: &str, fty| -> ConstFloat { @@ -165,6 +186,15 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { }) }; + let clamp = |n| { + let size = self.type_bit_size(ty); + trace!("clamp {} with size {} and amt {}", n, size, 128 - size); + let amt = 128 - size; + let result = (n << amt) >> amt; + trace!("clamp result: {}", result); + result + }; + use rustc::mir::interpret::*; let lit = match *lit { LitKind::Str(ref s, _) => { @@ -185,9 +215,10 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { LitKind::Int(n, _) if neg => { let n = n as i128; let n = n.overflowing_neg().0; - Value::ByVal(PrimVal::Bytes(n as u128)) + let n = clamp(n as u128); + Value::ByVal(PrimVal::Bytes(n)) }, - LitKind::Int(n, _) => Value::ByVal(PrimVal::Bytes(n)), + LitKind::Int(n, _) => Value::ByVal(PrimVal::Bytes(clamp(n))), LitKind::Float(n, fty) => { let n = n.as_str(); let mut f = parse_float(&n, fty); diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index 7d7e6ec9451ed..9b118b7fb78b6 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -14,75 +14,37 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { src_ty: Ty<'tcx>, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx, PrimVal> { + use rustc::ty::TypeVariants::*; trace!("Casting {:?}: {:?} to {:?}", val, src_ty, dest_ty); - let src_kind = self.ty_to_primval_kind(src_ty)?; match val { PrimVal::Undef => Ok(PrimVal::Undef), PrimVal::Ptr(ptr) => self.cast_from_ptr(ptr, dest_ty), - val @ PrimVal::Bytes(_) => { - use rustc::mir::interpret::PrimValKind::*; - match src_kind { - F32 => self.cast_from_float(val.to_f32()?, dest_ty), - F64 => self.cast_from_float(val.to_f64()?, dest_ty), - - I8 | I16 | I32 | I64 | I128 => { - self.cast_from_signed_int(val.to_i128()?, dest_ty) - } - - Bool | Char | U8 | U16 | U32 | U64 | U128 | FnPtr | Ptr => { - self.cast_from_int(val.to_u128()?, dest_ty, false) - } + PrimVal::Bytes(b) => { + match src_ty.sty { + TyFloat(fty) => self.cast_from_float(b, fty, dest_ty), + _ => self.cast_from_int(b, src_ty, dest_ty), } } } } - fn cast_from_signed_int(&self, val: i128, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { - self.cast_from_int(val as u128, ty, val < 0) - } - - fn int_to_int(&self, v: i128, ty: IntTy) -> u128 { - match ty { - IntTy::I8 => v as i8 as u128, - IntTy::I16 => v as i16 as u128, - IntTy::I32 => v as i32 as u128, - IntTy::I64 => v as i64 as u128, - IntTy::I128 => v as u128, - IntTy::Isize => { - let ty = self.tcx.sess.target.isize_ty; - self.int_to_int(v, ty) - } - } - } - fn int_to_uint(&self, v: u128, ty: UintTy) -> u128 { - match ty { - UintTy::U8 => v as u8 as u128, - UintTy::U16 => v as u16 as u128, - UintTy::U32 => v as u32 as u128, - UintTy::U64 => v as u64 as u128, - UintTy::U128 => v, - UintTy::Usize => { - let ty = self.tcx.sess.target.usize_ty; - self.int_to_uint(v, ty) - } - } - } - fn cast_from_int( &self, v: u128, - ty: Ty<'tcx>, - negative: bool, + src_ty: Ty<'tcx>, + dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx, PrimVal> { - trace!("cast_from_int: {}, {}, {}", v, ty, negative); + trace!("cast_from_int: {}, {}, {}", v, src_ty, dest_ty); use rustc::ty::TypeVariants::*; - match ty.sty { - // Casts to bool are not permitted by rustc, no need to handle them here. - TyInt(ty) => Ok(PrimVal::Bytes(self.int_to_int(v as i128, ty))), - TyUint(ty) => Ok(PrimVal::Bytes(self.int_to_uint(v, ty))), + match dest_ty.sty { + TyInt(_) | TyUint(_) => { + let v = self.sign_extend(v, src_ty)?; + let v = self.truncate(v, dest_ty)?; + Ok(PrimVal::Bytes(v)) + } - TyFloat(fty) if negative => Ok(PrimVal::Bytes(ConstFloat::from_i128(v as i128, fty).bits)), + TyFloat(fty) if src_ty.is_signed() => Ok(PrimVal::Bytes(ConstFloat::from_i128(v as i128, fty).bits)), TyFloat(fty) => Ok(PrimVal::Bytes(ConstFloat::from_u128(v, fty).bits)), TyChar if v as u8 as u128 == v => Ok(PrimVal::Bytes(v)), @@ -91,31 +53,42 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { // No alignment check needed for raw pointers. But we have to truncate to target ptr size. TyRawPtr(_) => Ok(PrimVal::Bytes(self.memory.truncate_to_ptr(v).0 as u128)), - _ => err!(Unimplemented(format!("int to {:?} cast", ty))), + // Casts to bool are not permitted by rustc, no need to handle them here. + _ => err!(Unimplemented(format!("int to {:?} cast", dest_ty))), } } - fn cast_from_float(&self, val: ConstFloat, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + fn cast_from_float(&self, bits: u128, fty: FloatTy, dest_ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { use rustc::ty::TypeVariants::*; - match ty.sty { + use rustc_apfloat::FloatConvert; + match dest_ty.sty { + // float -> uint TyUint(t) => { let width = t.bit_width().unwrap_or(self.memory.pointer_size() as usize * 8); - match val.ty { - FloatTy::F32 => Ok(PrimVal::Bytes(Single::from_bits(val.bits).to_u128(width).value)), - FloatTy::F64 => Ok(PrimVal::Bytes(Double::from_bits(val.bits).to_u128(width).value)), + match fty { + FloatTy::F32 => Ok(PrimVal::Bytes(Single::from_bits(bits).to_u128(width).value)), + FloatTy::F64 => Ok(PrimVal::Bytes(Double::from_bits(bits).to_u128(width).value)), } }, - + // float -> int TyInt(t) => { let width = t.bit_width().unwrap_or(self.memory.pointer_size() as usize * 8); - match val.ty { - FloatTy::F32 => Ok(PrimVal::from_i128(Single::from_bits(val.bits).to_i128(width).value)), - FloatTy::F64 => Ok(PrimVal::from_i128(Double::from_bits(val.bits).to_i128(width).value)), + match fty { + FloatTy::F32 => Ok(PrimVal::from_i128(Single::from_bits(bits).to_i128(width).value)), + FloatTy::F64 => Ok(PrimVal::from_i128(Double::from_bits(bits).to_i128(width).value)), } }, - - TyFloat(fty) => Ok(PrimVal::from_float(val.convert(fty))), - _ => err!(Unimplemented(format!("float to {:?} cast", ty))), + // f64 -> f32 + TyFloat(FloatTy::F32) if fty == FloatTy::F64 => { + Ok(PrimVal::Bytes(Single::to_bits(Double::from_bits(bits).convert(&mut false).value))) + }, + // f32 -> f64 + TyFloat(FloatTy::F64) if fty == FloatTy::F32 => { + Ok(PrimVal::Bytes(Double::to_bits(Single::from_bits(bits).convert(&mut false).value))) + }, + // identity cast + TyFloat(_) => Ok(PrimVal::Bytes(bits)), + _ => err!(Unimplemented(format!("float to {:?} cast", dest_ty))), } } diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index e0ad306571d24..e38969e45d2fc 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -19,7 +19,7 @@ use rustc::mir::interpret::{ }; use super::{Place, PlaceExtra, Memory, - HasMemory, MemoryKind, operator, + HasMemory, MemoryKind, Machine}; pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { @@ -536,10 +536,10 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M UnaryOp(un_op, ref operand) => { let val = self.eval_operand_to_primval(operand)?; - let kind = self.ty_to_primval_kind(dest_ty)?; + let val = self.unary_op(un_op, val, dest_ty)?; self.write_primval( dest, - operator::unary_op(un_op, val, kind)?, + val, dest_ty, )?; } @@ -1677,6 +1677,22 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M self.tcx.sess.err(&e.to_string()); } } + + pub fn sign_extend(&self, value: u128, ty: Ty<'tcx>) -> EvalResult<'tcx, u128> { + let size = self.layout_of(ty)?.size.bits(); + // sign extend + let amt = 128 - size; + // shift the unsigned value to the left + // and back to the right as signed (essentially fills with FF on the left) + Ok((((value << amt) as i128) >> amt) as u128) + } + + pub fn truncate(&self, value: u128, ty: Ty<'tcx>) -> EvalResult<'tcx, u128> { + let size = self.layout_of(ty)?.size.bits(); + let amt = 128 - size; + // truncate (shift left to drop out leftover values, shift right to fill with zeroes) + Ok((value << amt) >> amt) + } } impl<'mir, 'tcx> Frame<'mir, 'tcx> { diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 72a966ab38d61..010ec8b9bc03f 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -666,7 +666,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { } // Now we do the actual reading let bytes = if signed { - read_target_int(endianness, bytes).unwrap() as u128 + let bytes = read_target_int(endianness, bytes).unwrap() as u128; + let amt = 128 - (size * 8); + // truncate (shift left to drop out leftover values, shift right to fill with zeroes) + (bytes << amt) >> amt } else { read_target_uint(endianness, bytes).unwrap() }; diff --git a/src/librustc_mir/interpret/mod.rs b/src/librustc_mir/interpret/mod.rs index 8e0158569a867..b1ee3d568fd12 100644 --- a/src/librustc_mir/interpret/mod.rs +++ b/src/librustc_mir/interpret/mod.rs @@ -30,5 +30,4 @@ pub use self::const_eval::{ pub use self::machine::Machine; -pub use self::operator::unary_op; pub use self::memory::{write_target_uint, write_target_int, read_target_uint, read_target_int}; diff --git a/src/librustc_mir/interpret/operator.rs b/src/librustc_mir/interpret/operator.rs index bad744194d5e3..13087cfd473bc 100644 --- a/src/librustc_mir/interpret/operator.rs +++ b/src/librustc_mir/interpret/operator.rs @@ -1,12 +1,13 @@ use rustc::mir; -use rustc::ty::Ty; +use rustc::ty::{self, Ty}; use rustc_const_math::ConstFloat; use syntax::ast::FloatTy; use std::cmp::Ordering; +use rustc::ty::layout::LayoutOf; use super::{EvalContext, Place, Machine, ValTy}; -use rustc::mir::interpret::{EvalResult, PrimVal, PrimValKind, Value, bytes_to_f32, bytes_to_f64}; +use rustc::mir::interpret::{EvalResult, PrimVal, Value}; impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { fn binop_with_overflow( @@ -55,74 +56,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { } } -macro_rules! overflow { - (overflowing_div, $l:expr, $r:expr) => ({ - let (val, overflowed) = if $r == 0 { - ($l, true) - } else { - $l.overflowing_div($r) - }; - let primval = PrimVal::Bytes(val as u128); - Ok((primval, overflowed)) - }); - (overflowing_rem, $l:expr, $r:expr) => ({ - let (val, overflowed) = if $r == 0 { - ($l, true) - } else { - $l.overflowing_rem($r) - }; - let primval = PrimVal::Bytes(val as u128); - Ok((primval, overflowed)) - }); - ($op:ident, $l:expr, $r:expr) => ({ - let (val, overflowed) = $l.$op($r); - let primval = PrimVal::Bytes(val as u128); - Ok((primval, overflowed)) - }) -} - -macro_rules! int_arithmetic { - ($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({ - let l = $l; - let r = $r; - use rustc::mir::interpret::PrimValKind::*; - match $kind { - I8 => overflow!($int_op, l as i8, r as i8), - I16 => overflow!($int_op, l as i16, r as i16), - I32 => overflow!($int_op, l as i32, r as i32), - I64 => overflow!($int_op, l as i64, r as i64), - I128 => overflow!($int_op, l as i128, r as i128), - U8 => overflow!($int_op, l as u8, r as u8), - U16 => overflow!($int_op, l as u16, r as u16), - U32 => overflow!($int_op, l as u32, r as u32), - U64 => overflow!($int_op, l as u64, r as u64), - U128 => overflow!($int_op, l as u128, r as u128), - _ => bug!("int_arithmetic should only be called on int primvals"), - } - }) -} - -macro_rules! int_shift { - ($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({ - let l = $l; - let r = $r; - let r_wrapped = r as u32; - match $kind { - I8 => overflow!($int_op, l as i8, r_wrapped), - I16 => overflow!($int_op, l as i16, r_wrapped), - I32 => overflow!($int_op, l as i32, r_wrapped), - I64 => overflow!($int_op, l as i64, r_wrapped), - I128 => overflow!($int_op, l as i128, r_wrapped), - U8 => overflow!($int_op, l as u8, r_wrapped), - U16 => overflow!($int_op, l as u16, r_wrapped), - U32 => overflow!($int_op, l as u32, r_wrapped), - U64 => overflow!($int_op, l as u64, r_wrapped), - U128 => overflow!($int_op, l as u128, r_wrapped), - _ => bug!("int_shift should only be called on int primvals"), - }.map(|(val, over)| (val, over || r != r_wrapped as u128)) - }) -} - impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { /// Returns the result of the specified operation and whether it overflowed. pub fn binary_op( @@ -134,11 +67,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { right_ty: Ty<'tcx>, ) -> EvalResult<'tcx, (PrimVal, bool)> { use rustc::mir::BinOp::*; - use rustc::mir::interpret::PrimValKind::*; let left_kind = self.ty_to_primval_kind(left_ty)?; let right_kind = self.ty_to_primval_kind(right_ty)?; - //trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind); + trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind); // I: Handle operations that support pointers if !left_kind.is_float() && !right_kind.is_float() { @@ -153,11 +85,19 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { // These ops can have an RHS with a different numeric type. if right_kind.is_int() && (bin_op == Shl || bin_op == Shr) { - return match bin_op { - Shl => int_shift!(left_kind, overflowing_shl, l, r), - Shr => int_shift!(left_kind, overflowing_shr, l, r), + let op: fn(u128, u32) -> (u128, bool) = match bin_op { + Shl => u128::overflowing_shl, + Shr => u128::overflowing_shr, _ => bug!("it has already been checked that this is a shift op"), }; + let l = if left_ty.is_signed() { + self.sign_extend(l, left_ty)? + } else { + l + }; + let (result, oflo) = op(l, r as u32); + let truncated = self.truncate(result, left_ty)?; + return Ok((PrimVal::Bytes(truncated), oflo || truncated != result)); } if left_kind != right_kind { @@ -197,41 +137,95 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { } }; - let val = match (bin_op, left_kind) { - (_, F32) => float_op(bin_op, l, r, FloatTy::F32), - (_, F64) => float_op(bin_op, l, r, FloatTy::F64), - - - (Eq, _) => PrimVal::from_bool(l == r), - (Ne, _) => PrimVal::from_bool(l != r), - - (Lt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) < (r as i128)), - (Lt, _) => PrimVal::from_bool(l < r), - (Le, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) <= (r as i128)), - (Le, _) => PrimVal::from_bool(l <= r), - (Gt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) > (r as i128)), - (Gt, _) => PrimVal::from_bool(l > r), - (Ge, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) >= (r as i128)), - (Ge, _) => PrimVal::from_bool(l >= r), + if left_ty.is_signed() { + let op: Option bool> = match bin_op { + Lt => Some(i128::lt), + Le => Some(i128::le), + Gt => Some(i128::gt), + Ge => Some(i128::ge), + _ => None, + }; + if let Some(op) = op { + let l = self.sign_extend(l, left_ty)? as i128; + let r = self.sign_extend(r, right_ty)? as i128; + return Ok((PrimVal::from_bool(op(&l, &r)), false)); + } + let op: Option (i128, bool)> = match bin_op { + Rem | Div if r == 0 => return Ok((PrimVal::Bytes(l), true)), + Div => Some(i128::overflowing_div), + Rem => Some(i128::overflowing_rem), + Add => Some(i128::overflowing_add), + Sub => Some(i128::overflowing_sub), + Mul => Some(i128::overflowing_mul), + _ => None, + }; + if let Some(op) = op { + let l128 = self.sign_extend(l, left_ty)? as i128; + let r = self.sign_extend(r, right_ty)? as i128; + let size = self.layout_of(left_ty)?.size.bits(); + match bin_op { + Rem | Div => { + // int_min / -1 + if r == -1 && l == (1 << (size - 1)) { + return Ok((PrimVal::Bytes(l), true)); + } + }, + _ => {}, + } + trace!("{}, {}, {}", l, l128, r); + let (result, mut oflo) = op(l128, r); + trace!("{}, {}", result, oflo); + if !oflo && size != 128 { + let max = 1 << (size - 1); + oflo = result >= max || result < -max; + } + let result = result as u128; + let truncated = self.truncate(result, left_ty)?; + return Ok((PrimVal::Bytes(truncated), oflo)); + } + } - (BitOr, _) => PrimVal::Bytes(l | r), - (BitAnd, _) => PrimVal::Bytes(l & r), - (BitXor, _) => PrimVal::Bytes(l ^ r), + if let ty::TyFloat(fty) = left_ty.sty { + return Ok((float_op(bin_op, l, r, fty), false)); + } - (Add, k) if k.is_int() => return int_arithmetic!(k, overflowing_add, l, r), - (Sub, k) if k.is_int() => return int_arithmetic!(k, overflowing_sub, l, r), - (Mul, k) if k.is_int() => return int_arithmetic!(k, overflowing_mul, l, r), - (Div, k) if k.is_int() => return int_arithmetic!(k, overflowing_div, l, r), - (Rem, k) if k.is_int() => return int_arithmetic!(k, overflowing_rem, l, r), + // only ints left + let val = match bin_op { + Eq => PrimVal::from_bool(l == r), + Ne => PrimVal::from_bool(l != r), + + Lt => PrimVal::from_bool(l < r), + Le => PrimVal::from_bool(l <= r), + Gt => PrimVal::from_bool(l > r), + Ge => PrimVal::from_bool(l >= r), + + BitOr => PrimVal::Bytes(l | r), + BitAnd => PrimVal::Bytes(l & r), + BitXor => PrimVal::Bytes(l ^ r), + + Add | Sub | Mul | Rem | Div => { + let op: fn(u128, u128) -> (u128, bool) = match bin_op { + Add => u128::overflowing_add, + Sub => u128::overflowing_sub, + Mul => u128::overflowing_mul, + Rem | Div if r == 0 => return Ok((PrimVal::Bytes(l), true)), + Div => u128::overflowing_div, + Rem => u128::overflowing_rem, + _ => bug!(), + }; + let (result, oflo) = op(l, r); + let truncated = self.truncate(result, left_ty)?; + return Ok((PrimVal::Bytes(truncated), oflo || truncated != result)); + } _ => { let msg = format!( "unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, - left_kind, + left_ty, right, - right_kind + right_ty, ); return err!(Unimplemented(msg)); } @@ -239,52 +233,33 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { Ok((val, false)) } -} -pub fn unary_op<'tcx>( - un_op: mir::UnOp, - val: PrimVal, - val_kind: PrimValKind, -) -> EvalResult<'tcx, PrimVal> { - use rustc::mir::UnOp::*; - use rustc::mir::interpret::PrimValKind::*; - - let bytes = val.to_bytes()?; + pub fn unary_op( + &self, + un_op: mir::UnOp, + val: PrimVal, + ty: Ty<'tcx>, + ) -> EvalResult<'tcx, PrimVal> { + use rustc::mir::UnOp::*; + use rustc_apfloat::ieee::{Single, Double}; + use rustc_apfloat::Float; - let result_bytes = match (un_op, val_kind) { - (Not, Bool) => !val.to_bool()? as u128, + let bytes = val.to_bytes()?; + let size = self.layout_of(ty)?.size.bits(); - (Not, U8) => !(bytes as u8) as u128, - (Not, U16) => !(bytes as u16) as u128, - (Not, U32) => !(bytes as u32) as u128, - (Not, U64) => !(bytes as u64) as u128, - (Not, U128) => !bytes, + let result_bytes = match (un_op, &ty.sty) { - (Not, I8) => !(bytes as i8) as u128, - (Not, I16) => !(bytes as i16) as u128, - (Not, I32) => !(bytes as i32) as u128, - (Not, I64) => !(bytes as i64) as u128, - (Not, I128) => !(bytes as i128) as u128, + (Not, ty::TyBool) => !val.to_bool()? as u128, - (Neg, I8) if bytes == i8::min_value() as u128 => return err!(OverflowingMath), - (Neg, I8) => -(bytes as i8) as u128, - (Neg, I16) if bytes == i16::min_value() as u128 => return err!(OverflowingMath), - (Neg, I16) => -(bytes as i16) as u128, - (Neg, I32) if bytes == i32::min_value() as u128 => return err!(OverflowingMath), - (Neg, I32) => -(bytes as i32) as u128, - (Neg, I64) if bytes == i64::min_value() as u128 => return err!(OverflowingMath), - (Neg, I64) => -(bytes as i64) as u128, - (Neg, I128) if bytes == i128::min_value() as u128 => return err!(OverflowingMath), - (Neg, I128) => -(bytes as i128) as u128, + (Not, _) => !bytes, - (Neg, F32) => (-bytes_to_f32(bytes)).bits, - (Neg, F64) => (-bytes_to_f64(bytes)).bits, + (Neg, ty::TyFloat(FloatTy::F32)) => Single::to_bits(-Single::from_bits(bytes)), + (Neg, ty::TyFloat(FloatTy::F64)) => Double::to_bits(-Double::from_bits(bytes)), - _ => { - let msg = format!("unimplemented unary op: {:?}, {:?}", un_op, val); - return err!(Unimplemented(msg)); - } - }; + (Neg, _) if bytes == (1 << (size - 1)) => return err!(OverflowingMath), + (Neg, _) => (-(bytes as i128)) as u128, + }; - Ok(PrimVal::Bytes(result_bytes)) + Ok(PrimVal::Bytes(self.truncate(result_bytes, ty)?)) + } } diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index de17872e96ff2..1b64178477e16 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -20,7 +20,7 @@ use rustc::mir::visit::{Visitor, PlaceContext}; use rustc::middle::const_val::ConstVal; use rustc::ty::{TyCtxt, self, Instance}; use rustc::mir::interpret::{Value, PrimVal, GlobalId}; -use interpret::{eval_body_with_mir, mk_borrowck_eval_cx, unary_op, ValTy}; +use interpret::{eval_body_with_mir, mk_borrowck_eval_cx, ValTy}; use transform::{MirPass, MirSource}; use syntax::codemap::Span; use rustc::ty::subst::Substs; @@ -205,8 +205,7 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { let val = self.eval_operand(arg)?; let prim = ecx.value_to_primval(ValTy { value: val.0, ty: val.1 }).ok()?; - let kind = ecx.ty_to_primval_kind(val.1).ok()?; - match unary_op(op, prim, kind) { + match ecx.unary_op(op, prim, val.1) { Ok(val) => Some((Value::ByVal(val), place_ty, span)), Err(mut err) => { ecx.report(&mut err, false, Some(span)); diff --git a/src/test/run-pass/const-negation.rs b/src/test/run-pass/const-negation.rs index 012fe0d95ec49..5c633eb6112ff 100644 --- a/src/test/run-pass/const-negation.rs +++ b/src/test/run-pass/const-negation.rs @@ -17,11 +17,13 @@ fn main() { const I: isize = -9223372036854775808isize; assert_eq!(::std::i32::MIN as u64, 0xffffffff80000000); assert_eq!(-2147483648isize as u64, 0xffffffff80000000); + assert_eq!(-2147483648i32 as u64, 0xffffffff80000000); assert_eq!(::std::i64::MIN as u64, 0x8000000000000000); #[cfg(target_pointer_width = "64")] assert_eq!(-9223372036854775808isize as u64, 0x8000000000000000); #[cfg(target_pointer_width = "32")] assert_eq!(-9223372036854775808isize as u64, 0); + assert_eq!(-9223372036854775808i32 as u64, 0); const J: usize = ::std::i32::MAX as usize; const K: usize = -1i32 as u32 as usize; const L: usize = ::std::i32::MIN as usize; From 889a4ebfa98977583e4f319154080c4594e6e16e Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 22 Feb 2018 09:37:20 +0100 Subject: [PATCH 097/110] Deduplicate some code --- src/librustc/ty/mod.rs | 140 ++++++++++++++++++++--------------------- 1 file changed, 67 insertions(+), 73 deletions(-) diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index d730c95f4dd88..4e3a0bcb0d22c 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1823,64 +1823,77 @@ impl<'a, 'gcx, 'tcx> AdtDef { } } + #[inline] + fn eval_explicit_discr( + &self, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + expr_did: DefId, + ) -> Option> { + let param_env = ParamEnv::empty(traits::Reveal::UserFacing); + let repr_type = self.repr.discr_type(); + let substs = Substs::identity_for_item(tcx.global_tcx(), expr_did); + let instance = ty::Instance::new(expr_did, substs); + let cid = GlobalId { + instance, + promoted: None + }; + match tcx.const_eval(param_env.and(cid)) { + Ok(&ty::Const { + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))), + .. + }) => { + trace!("discriminants: {} ({:?})", b, repr_type); + let ty = repr_type.to_ty(tcx); + if ty.is_signed() { + let (ty, param_env) = tcx + .lift_to_global(&(ty, param_env)) + .unwrap_or_else(|| { + bug!("MIR: discriminants({:?}, {:?}) got \ + type with inference types/regions", + ty, param_env); + }); + let size = tcx.global_tcx() + .layout_of(param_env.and(ty)) + .expect("int layout") + .size + .bits(); + let val = b as i128; + // sign extend to i128 + let amt = 128 - size; + let val = (val << amt) >> amt; + Some(Discr { + val: val as u128, + ty, + }) + } else { + Some(Discr { + val: b, + ty, + }) + } + } + _ => { + if !expr_did.is_local() { + span_bug!(tcx.def_span(expr_did), + "variant discriminant evaluation succeeded \ + in its crate but failed locally"); + } + None + } + } + } + #[inline] pub fn discriminants(&'a self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> impl Iterator> + 'a { - let param_env = ParamEnv::empty(traits::Reveal::UserFacing); let repr_type = self.repr.discr_type(); let initial = repr_type.initial_discriminant(tcx.global_tcx()); let mut prev_discr = None::>; self.variants.iter().map(move |v| { let mut discr = prev_discr.map_or(initial, |d| d.wrap_incr(tcx)); if let VariantDiscr::Explicit(expr_did) = v.discr { - let substs = Substs::identity_for_item(tcx.global_tcx(), expr_did); - let instance = ty::Instance::new(expr_did, substs); - let cid = GlobalId { - instance, - promoted: None - }; - match tcx.const_eval(param_env.and(cid)) { - Ok(&ty::Const { - val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))), - .. - }) => { - trace!("discriminants: {} ({:?})", b, repr_type); - let ty = repr_type.to_ty(tcx); - if ty.is_signed() { - let (ty, param_env) = tcx - .lift_to_global(&(ty, param_env)) - .unwrap_or_else(|| { - bug!("MIR: discriminants({:?}, {:?}) got \ - type with inference types/regions", - ty, param_env); - }); - let size = tcx.global_tcx() - .layout_of(param_env.and(ty)) - .expect("int layout") - .size - .bits(); - let val = b as i128; - // sign extend to i128 - let amt = 128 - size; - let val = (val << amt) >> amt; - discr = Discr { - val: val as u128, - ty, - }; - } else { - discr = Discr { - val: b, - ty, - }; - } - } - _ => { - if !expr_did.is_local() { - span_bug!(tcx.def_span(expr_did), - "variant discriminant evaluation succeeded \ - in its crate but failed locally"); - } - } + if let Some(new_discr) = self.eval_explicit_discr(tcx, expr_did) { + discr = new_discr; } } prev_discr = Some(discr); @@ -1898,7 +1911,6 @@ impl<'a, 'gcx, 'tcx> AdtDef { tcx: TyCtxt<'a, 'gcx, 'tcx>, variant_index: usize) -> Discr<'tcx> { - let param_env = ParamEnv::empty(traits::Reveal::UserFacing); let repr_type = self.repr.discr_type(); let mut explicit_value = repr_type.initial_discriminant(tcx.global_tcx()); let mut explicit_index = variant_index; @@ -1909,30 +1921,12 @@ impl<'a, 'gcx, 'tcx> AdtDef { explicit_index -= distance; } ty::VariantDiscr::Explicit(expr_did) => { - let substs = Substs::identity_for_item(tcx.global_tcx(), expr_did); - let instance = ty::Instance::new(expr_did, substs); - let cid = GlobalId { - instance, - promoted: None - }; - match tcx.const_eval(param_env.and(cid)) { - Ok(&ty::Const { - val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))), - .. - }) => { - trace!("discriminants: {} ({:?})", b, repr_type); - explicit_value = Discr { - val: b, - ty: repr_type.to_ty(tcx), - }; + match self.eval_explicit_discr(tcx, expr_did) { + Some(discr) => { + explicit_value = discr; break; - } - _ => { - if !expr_did.is_local() { - span_bug!(tcx.def_span(expr_did), - "variant discriminant evaluation succeeded \ - in its crate but failed locally"); - } + }, + None => { if explicit_index == 0 { break; } From e8d357f070e609d7feed402d7ee28c73d6c0fd70 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 22 Feb 2018 14:59:18 +0100 Subject: [PATCH 098/110] Decide signdedness on the layout instead of the type --- src/librustc/ty/layout.rs | 11 +++++++ src/librustc/ty/mod.rs | 18 +++++++++-- src/librustc_mir/interpret/cast.rs | 14 ++++++-- src/librustc_mir/interpret/eval_context.rs | 20 +++++++----- src/librustc_mir/interpret/memory.rs | 14 +------- src/librustc_mir/interpret/operator.rs | 37 +++++++++++++++------- src/librustc_typeck/collect.rs | 32 +------------------ 7 files changed, 77 insertions(+), 69 deletions(-) diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 382e99f26943f..1b919ad68d047 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -794,6 +794,17 @@ impl Abi { Abi::Aggregate { sized } => !sized } } + + /// Returns true if this is a single signed integer scalar + pub fn is_signed(&self) -> bool { + match *self { + Abi::Scalar(ref scal) => match scal.value { + Primitive::Int(_, signed) => signed, + _ => false, + }, + _ => false, + } + } } #[derive(PartialEq, Eq, Hash, Debug)] diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 4e3a0bcb0d22c..8614f7bd58e2b 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1824,7 +1824,7 @@ impl<'a, 'gcx, 'tcx> AdtDef { } #[inline] - fn eval_explicit_discr( + pub fn eval_explicit_discr( &self, tcx: TyCtxt<'a, 'gcx, 'tcx>, expr_did: DefId, @@ -1871,8 +1871,21 @@ impl<'a, 'gcx, 'tcx> AdtDef { ty, }) } + }, + Ok(&ty::Const { + val: ConstVal::Value(other), + .. + }) => { + info!("invalid enum discriminant: {:#?}", other); + ::middle::const_val::struct_error( + tcx, + tcx.def_span(expr_did), + "constant evaluation of enum discriminant resulted in non-integer", + ).emit(); + None } - _ => { + Err(err) => { + err.report(tcx, tcx.def_span(expr_did), "enum discriminant"); if !expr_did.is_local() { span_bug!(tcx.def_span(expr_did), "variant discriminant evaluation succeeded \ @@ -1880,6 +1893,7 @@ impl<'a, 'gcx, 'tcx> AdtDef { } None } + _ => span_bug!(tcx.def_span(expr_did), "const eval "), } } diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index 9b118b7fb78b6..e654142d2164c 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -1,4 +1,5 @@ use rustc::ty::Ty; +use rustc::ty::layout::LayoutOf; use syntax::ast::{FloatTy, IntTy, UintTy}; use rustc_const_math::ConstFloat; @@ -35,23 +36,30 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { src_ty: Ty<'tcx>, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx, PrimVal> { + let signed = self.layout_of(src_ty)?.abi.is_signed(); + let v = if signed { + self.sign_extend(v, src_ty)? + } else { + v + }; trace!("cast_from_int: {}, {}, {}", v, src_ty, dest_ty); use rustc::ty::TypeVariants::*; match dest_ty.sty { TyInt(_) | TyUint(_) => { - let v = self.sign_extend(v, src_ty)?; let v = self.truncate(v, dest_ty)?; Ok(PrimVal::Bytes(v)) } - TyFloat(fty) if src_ty.is_signed() => Ok(PrimVal::Bytes(ConstFloat::from_i128(v as i128, fty).bits)), + TyFloat(fty) if signed => Ok(PrimVal::Bytes(ConstFloat::from_i128(v as i128, fty).bits)), TyFloat(fty) => Ok(PrimVal::Bytes(ConstFloat::from_u128(v, fty).bits)), TyChar if v as u8 as u128 == v => Ok(PrimVal::Bytes(v)), TyChar => err!(InvalidChar(v)), // No alignment check needed for raw pointers. But we have to truncate to target ptr size. - TyRawPtr(_) => Ok(PrimVal::Bytes(self.memory.truncate_to_ptr(v).0 as u128)), + TyRawPtr(_) => { + Ok(PrimVal::Bytes(self.memory.truncate_to_ptr(v).0 as u128)) + }, // Casts to bool are not permitted by rustc, no need to handle them here. _ => err!(Unimplemented(format!("int to {:?} cast", dest_ty))), diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index e38969e45d2fc..97056dd66bbba 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -1128,20 +1128,22 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M dest_align: Align, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx> { - trace!("write_value_to_ptr: {:#?}", value); let layout = self.layout_of(dest_ty)?; + trace!("write_value_to_ptr: {:#?}, {}, {:#?}", value, dest_ty, layout); match value { Value::ByRef(ptr, align) => { self.memory.copy(ptr, align.min(layout.align), dest, dest_align.min(layout.align), layout.size.bytes(), false) } Value::ByVal(primval) => { - match layout.abi { - layout::Abi::Scalar(_) => {} - _ if primval.is_undef() => {} + let signed = match layout.abi { + layout::Abi::Scalar(ref scal) => match scal.value { + layout::Primitive::Int(_, signed) => signed, + _ => false, + }, + _ if primval.is_undef() => false, _ => bug!("write_value_to_ptr: invalid ByVal layout: {:#?}", layout) - } - // TODO: Do we need signedness? - self.memory.write_primval(dest.to_ptr()?, dest_align, primval, layout.size.bytes(), false) + }; + self.memory.write_primval(dest.to_ptr()?, dest_align, primval, layout.size.bytes(), signed) } Value::ByValPair(a_val, b_val) => { let ptr = dest.to_ptr()?; @@ -1679,7 +1681,9 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M } pub fn sign_extend(&self, value: u128, ty: Ty<'tcx>) -> EvalResult<'tcx, u128> { - let size = self.layout_of(ty)?.size.bits(); + let layout = self.layout_of(ty)?; + let size = layout.size.bits(); + assert!(layout.abi.is_signed()); // sign extend let amt = 128 - size; // shift the unsigned value to the left diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 010ec8b9bc03f..4e5bf25ca3b40 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -702,19 +702,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { val.offset as u128 } - PrimVal::Bytes(bytes) => { - // We need to mask here, or the byteorder crate can die when given a u64 larger - // than fits in an integer of the requested size. - let mask = match size { - 1 => !0u8 as u128, - 2 => !0u16 as u128, - 4 => !0u32 as u128, - 8 => !0u64 as u128, - 16 => !0, - n => bug!("unexpected PrimVal::Bytes size: {}", n), - }; - bytes & mask - } + PrimVal::Bytes(bytes) => bytes, PrimVal::Undef => { self.mark_definedness(PrimVal::Ptr(ptr).into(), size, false)?; diff --git a/src/librustc_mir/interpret/operator.rs b/src/librustc_mir/interpret/operator.rs index 13087cfd473bc..a74fe63e53c86 100644 --- a/src/librustc_mir/interpret/operator.rs +++ b/src/librustc_mir/interpret/operator.rs @@ -83,21 +83,34 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { let l = left.to_bytes()?; let r = right.to_bytes()?; + let left_layout = self.layout_of(left_ty)?; + // These ops can have an RHS with a different numeric type. if right_kind.is_int() && (bin_op == Shl || bin_op == Shr) { - let op: fn(u128, u32) -> (u128, bool) = match bin_op { - Shl => u128::overflowing_shl, - Shr => u128::overflowing_shr, - _ => bug!("it has already been checked that this is a shift op"), - }; - let l = if left_ty.is_signed() { - self.sign_extend(l, left_ty)? + let signed = left_layout.abi.is_signed(); + let mut r = r as u32; + let size = left_layout.size.bits() as u32; + let oflo = r > size; + if oflo { + r %= size; + } + let result = if signed { + let l = self.sign_extend(l, left_ty)? as i128; + let result = match bin_op { + Shl => l << r, + Shr => l >> r, + _ => bug!("it has already been checked that this is a shift op"), + }; + result as u128 } else { - l + match bin_op { + Shl => l << r, + Shr => l >> r, + _ => bug!("it has already been checked that this is a shift op"), + } }; - let (result, oflo) = op(l, r as u32); let truncated = self.truncate(result, left_ty)?; - return Ok((PrimVal::Bytes(truncated), oflo || truncated != result)); + return Ok((PrimVal::Bytes(truncated), oflo)); } if left_kind != right_kind { @@ -137,7 +150,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { } }; - if left_ty.is_signed() { + if left_layout.abi.is_signed() { let op: Option bool> = match bin_op { Lt => Some(i128::lt), Le => Some(i128::le), @@ -162,7 +175,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { if let Some(op) = op { let l128 = self.sign_extend(l, left_ty)? as i128; let r = self.sign_extend(r, right_ty)? as i128; - let size = self.layout_of(left_ty)?.size.bits(); + let size = left_layout.size.bits(); match bin_op { Rem | Div => { // int_min / -1 diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index b040bd014e30f..858d9fb5b7492 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -28,17 +28,14 @@ use astconv::{AstConv, Bounds}; use lint; use constrained_type_params as ctp; use middle::lang_items::SizedTraitLangItem; -use middle::const_val::ConstVal; use middle::resolve_lifetime as rl; use rustc::mir::mono::Linkage; -use rustc::traits::Reveal; use rustc::ty::subst::Substs; use rustc::ty::{ToPredicate, ReprOptions}; use rustc::ty::{self, AdtKind, ToPolyTraitRef, Ty, TyCtxt}; use rustc::ty::maps::Providers; use rustc::ty::util::IntTypeExt; use rustc::util::nodemap::{FxHashSet, FxHashMap}; -use rustc::mir::interpret::{GlobalId, Value, PrimVal}; use rustc::ty::util::Discr; use syntax::{abi, ast}; @@ -511,7 +508,6 @@ fn convert_variant_ctor<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, fn convert_enum_variant_types<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, variants: &[hir::Variant]) { - let param_env = ty::ParamEnv::empty(Reveal::UserFacing); let def = tcx.adt_def(def_id); let repr_type = def.repr.discr_type(); let initial = repr_type.initial_discriminant(tcx); @@ -522,33 +518,7 @@ fn convert_enum_variant_types<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let wrapped_discr = prev_discr.map_or(initial, |d| d.wrap_incr(tcx)); prev_discr = Some(if let Some(e) = variant.node.disr_expr { let expr_did = tcx.hir.local_def_id(e.node_id); - let substs = Substs::identity_for_item(tcx, expr_did); - let instance = ty::Instance::new(expr_did, substs); - let global_id = GlobalId { - instance, - promoted: None - }; - let result = tcx.at(variant.span).const_eval(param_env.and(global_id)); - - // enum variant evaluation happens before the global constant check - // so we need to report the real error - if let Err(ref err) = result { - err.report(tcx, variant.span, "enum discriminant"); - } - - match result { - // FIXME: just use `to_raw_bits` here? - Ok(&ty::Const { - val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))), - .. - }) => { - Some(Discr { - val: b, - ty: initial.ty, - }) - } - _ => None - } + def.eval_explicit_discr(tcx, expr_did) } else if let Some(discr) = repr_type.disr_incr(tcx, prev_discr) { Some(discr) } else { From 208d7648331874fcdbdb144025a516f03db1eb69 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 22 Feb 2018 16:56:14 +0100 Subject: [PATCH 099/110] Adjust test which differs between 32 bit and 64 bit The differences are not part of what the test is testing, so they were simply removed. --- src/test/codegen/consts.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/codegen/consts.rs b/src/test/codegen/consts.rs index 13c037e96cbe3..007fb7f459662 100644 --- a/src/test/codegen/consts.rs +++ b/src/test/codegen/consts.rs @@ -54,7 +54,7 @@ pub fn inline_enum_const() -> E { #[no_mangle] pub fn low_align_const() -> E { // Check that low_align_const and high_align_const use the same constant -// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %1, i8* getelementptr inbounds (<{ [8 x i8] }>, <{ [8 x i8] }>* [[LOW_HIGH]], i32 0, i32 0, i32 0), i64 8, i32 2, i1 false) +// CHECK: i8* getelementptr inbounds (<{ [8 x i8] }>, <{ [8 x i8] }>* [[LOW_HIGH]], i32 0, i32 0, i32 0), *&E::A(0) } @@ -62,6 +62,6 @@ pub fn low_align_const() -> E { #[no_mangle] pub fn high_align_const() -> E { // Check that low_align_const and high_align_const use the same constant -// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %1, i8* getelementptr inbounds (<{ [8 x i8] }>, <{ [8 x i8] }>* [[LOW_HIGH]], i32 0, i32 0, i32 0), i64 8, i32 4, i1 false) +// CHECK: i8* getelementptr inbounds (<{ [8 x i8] }>, <{ [8 x i8] }>* [[LOW_HIGH]], i32 0, i32 0, i32 0), *&E::A(0) } From 47913ee8f488e2661a06337e060fb90895680c8f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 22 Feb 2018 17:29:39 +0100 Subject: [PATCH 100/110] Address review comments --- src/librustc/ty/mod.rs | 17 ++--------- src/librustc_mir/hair/cx/mod.rs | 21 ++++--------- src/librustc_mir/interpret/eval_context.rs | 26 +++++++--------- src/librustc_mir/interpret/memory.rs | 32 ++++++-------------- src/librustc_mir/interpret/mod.rs | 2 +- src/librustc_mir/interpret/terminator/mod.rs | 2 +- src/librustc_mir/interpret/traits.rs | 4 +-- 7 files changed, 33 insertions(+), 71 deletions(-) diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 8614f7bd58e2b..63494438f7d82 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1831,6 +1831,7 @@ impl<'a, 'gcx, 'tcx> AdtDef { ) -> Option> { let param_env = ParamEnv::empty(traits::Reveal::UserFacing); let repr_type = self.repr.discr_type(); + let bit_size = layout::Integer::from_attr(tcx, repr_type).size().bits(); let substs = Substs::identity_for_item(tcx.global_tcx(), expr_did); let instance = ty::Instance::new(expr_did, substs); let cid = GlobalId { @@ -1844,22 +1845,10 @@ impl<'a, 'gcx, 'tcx> AdtDef { }) => { trace!("discriminants: {} ({:?})", b, repr_type); let ty = repr_type.to_ty(tcx); - if ty.is_signed() { - let (ty, param_env) = tcx - .lift_to_global(&(ty, param_env)) - .unwrap_or_else(|| { - bug!("MIR: discriminants({:?}, {:?}) got \ - type with inference types/regions", - ty, param_env); - }); - let size = tcx.global_tcx() - .layout_of(param_env.and(ty)) - .expect("int layout") - .size - .bits(); + if repr_type.is_signed() { let val = b as i128; // sign extend to i128 - let amt = 128 - size; + let amt = 128 - bit_size; let val = (val << amt) >> amt; Some(Discr { val: val as u128, diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index ec484785ca2f0..28eef70679b95 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -151,22 +151,13 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { pub fn type_bit_size( &self, - ty: Ty<'tcx>, + ty: Ty, ) -> u64 { - let tcx = self.tcx.global_tcx(); - let (ty, param_env) = self - .tcx - .lift_to_global(&(ty, self.param_env)) - .unwrap_or_else(|| { - bug!("MIR: Cx::const_eval_literal({:?}, {:?}) got \ - type with inference types/regions", - ty, self.param_env); - }); - tcx - .layout_of(param_env.and(ty)) - .expect("int layout") - .size - .bits() + match ty.sty { + ty::TyInt(ity) => ity.bit_width(), + ty::TyUint(uty) => uty.bit_width(), + _ => bug!("{} is not an integer", ty), + }.map_or(self.tcx.data_layout.pointer_size.bits(), |n| n as u64) } pub fn const_eval_literal( diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 97056dd66bbba..13090ca53302b 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -1256,7 +1256,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M pointee_ty: Ty<'tcx>, ) -> EvalResult<'tcx, Value> { let ptr_size = self.memory.pointer_size(); - let p: Pointer = self.memory.read_ptr_sized_unsigned(ptr, ptr_align)?.into(); + let p: Pointer = self.memory.read_ptr_sized(ptr, ptr_align)?.into(); if self.type_is_sized(pointee_ty) { Ok(p.to_value()) } else { @@ -1264,12 +1264,12 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M let extra = ptr.offset(ptr_size, self)?; match self.tcx.struct_tail(pointee_ty).sty { ty::TyDynamic(..) => Ok(p.to_value_with_vtable( - self.memory.read_ptr_sized_unsigned(extra, ptr_align)?.to_ptr()?, + self.memory.read_ptr_sized(extra, ptr_align)?.to_ptr()?, )), ty::TySlice(..) | ty::TyStr => { let len = self .memory - .read_ptr_sized_unsigned(extra, ptr_align)? + .read_ptr_sized(extra, ptr_align)? .to_bytes()?; Ok(p.to_value_with_len(len as u64)) }, @@ -1284,7 +1284,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M let ptr = ptr.to_ptr()?; let val = match ty.sty { ty::TyBool => { - let val = self.memory.read_primval(ptr, ptr_align, 1, false)?; + let val = self.memory.read_primval(ptr, ptr_align, 1)?; let val = match val { PrimVal::Bytes(0) => false, PrimVal::Bytes(1) => true, @@ -1294,7 +1294,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M PrimVal::from_bool(val) } ty::TyChar => { - let c = self.memory.read_primval(ptr, ptr_align, 4, false)?.to_bytes()? as u32; + let c = self.memory.read_primval(ptr, ptr_align, 4)?.to_bytes()? as u32; match ::std::char::from_u32(c) { Some(ch) => PrimVal::from_char(ch), None => return err!(InvalidChar(c as u128)), @@ -1311,7 +1311,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M I128 => 16, Isize => self.memory.pointer_size(), }; - self.memory.read_primval(ptr, ptr_align, size, true)? + self.memory.read_primval(ptr, ptr_align, size)? } ty::TyUint(uint_ty) => { @@ -1324,17 +1324,17 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M U128 => 16, Usize => self.memory.pointer_size(), }; - self.memory.read_primval(ptr, ptr_align, size, false)? + self.memory.read_primval(ptr, ptr_align, size)? } ty::TyFloat(FloatTy::F32) => { - PrimVal::Bytes(self.memory.read_primval(ptr, ptr_align, 4, false)?.to_bytes()?) + PrimVal::Bytes(self.memory.read_primval(ptr, ptr_align, 4)?.to_bytes()?) } ty::TyFloat(FloatTy::F64) => { - PrimVal::Bytes(self.memory.read_primval(ptr, ptr_align, 8, false)?.to_bytes()?) + PrimVal::Bytes(self.memory.read_primval(ptr, ptr_align, 8)?.to_bytes()?) } - ty::TyFnPtr(_) => self.memory.read_ptr_sized_unsigned(ptr, ptr_align)?, + ty::TyFnPtr(_) => self.memory.read_ptr_sized(ptr, ptr_align)?, ty::TyRef(_, ref tam) | ty::TyRawPtr(ref tam) => return self.read_ptr(ptr, ptr_align, tam.ty).map(Some), @@ -1344,12 +1344,8 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M } if let layout::Abi::Scalar(ref scalar) = self.layout_of(ty)?.abi { - let mut signed = false; - if let layout::Int(_, s) = scalar.value { - signed = s; - } let size = scalar.value.size(self).bytes(); - self.memory.read_primval(ptr, ptr_align, size, signed)? + self.memory.read_primval(ptr, ptr_align, size)? } else { return Ok(None); } diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 4e5bf25ca3b40..b369f80e849b0 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -655,7 +655,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { Ok(()) } - pub fn read_primval(&self, ptr: MemoryPointer, ptr_align: Align, size: u64, signed: bool) -> EvalResult<'tcx, PrimVal> { + pub fn read_primval(&self, ptr: MemoryPointer, ptr_align: Align, size: u64) -> EvalResult<'tcx, PrimVal> { self.check_relocation_edges(ptr, size)?; // Make sure we don't read part of a pointer as a pointer let endianness = self.endianness(); let bytes = self.get_bytes_unchecked(ptr, size, ptr_align.min(self.int_align(size)))?; @@ -665,14 +665,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { return Ok(PrimVal::Undef.into()); } // Now we do the actual reading - let bytes = if signed { - let bytes = read_target_int(endianness, bytes).unwrap() as u128; - let amt = 128 - (size * 8); - // truncate (shift left to drop out leftover values, shift right to fill with zeroes) - (bytes << amt) >> amt - } else { - read_target_uint(endianness, bytes).unwrap() - }; + let bytes = read_target_uint(endianness, bytes).unwrap(); // See if we got a pointer if size != self.pointer_size() { if self.relocations(ptr, size)?.count() != 0 { @@ -689,8 +682,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { Ok(PrimVal::Bytes(bytes)) } - pub fn read_ptr_sized_unsigned(&self, ptr: MemoryPointer, ptr_align: Align) -> EvalResult<'tcx, PrimVal> { - self.read_primval(ptr, ptr_align, self.pointer_size(), false) + pub fn read_ptr_sized(&self, ptr: MemoryPointer, ptr_align: Align) -> EvalResult<'tcx, PrimVal> { + self.read_primval(ptr, ptr_align, self.pointer_size()) } pub fn write_primval(&mut self, ptr: MemoryPointer, ptr_align: Align, val: PrimVal, size: u64, signed: bool) -> EvalResult<'tcx> { @@ -901,13 +894,6 @@ pub fn read_target_uint(endianness: layout::Endian, mut source: &[u8]) -> Result } } -pub fn read_target_int(endianness: layout::Endian, mut source: &[u8]) -> Result { - match endianness { - layout::Endian::Little => source.read_int128::(source.len()), - layout::Endian::Big => source.read_int128::(source.len()), - } -} - //////////////////////////////////////////////////////////////////////////////// // Unaligned accesses //////////////////////////////////////////////////////////////////////////////// @@ -924,7 +910,7 @@ pub trait HasMemory<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { ) -> EvalResult<'tcx, Pointer> { Ok(match value { Value::ByRef(ptr, align) => { - self.memory().read_ptr_sized_unsigned(ptr.to_ptr()?, align)? + self.memory().read_ptr_sized(ptr.to_ptr()?, align)? } Value::ByVal(ptr) | Value::ByValPair(ptr, _) => ptr, @@ -938,8 +924,8 @@ pub trait HasMemory<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { match value { Value::ByRef(ref_ptr, align) => { let mem = self.memory(); - let ptr = mem.read_ptr_sized_unsigned(ref_ptr.to_ptr()?, align)?.into(); - let vtable = mem.read_ptr_sized_unsigned( + let ptr = mem.read_ptr_sized(ref_ptr.to_ptr()?, align)?.into(); + let vtable = mem.read_ptr_sized( ref_ptr.offset(mem.pointer_size(), &mem.tcx.data_layout)?.to_ptr()?, align )?.to_ptr()?; @@ -960,8 +946,8 @@ pub trait HasMemory<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { match value { Value::ByRef(ref_ptr, align) => { let mem = self.memory(); - let ptr = mem.read_ptr_sized_unsigned(ref_ptr.to_ptr()?, align)?.into(); - let len = mem.read_ptr_sized_unsigned( + let ptr = mem.read_ptr_sized(ref_ptr.to_ptr()?, align)?.into(); + let len = mem.read_ptr_sized( ref_ptr.offset(mem.pointer_size(), &mem.tcx.data_layout)?.to_ptr()?, align )?.to_bytes()? as u64; diff --git a/src/librustc_mir/interpret/mod.rs b/src/librustc_mir/interpret/mod.rs index b1ee3d568fd12..ae6337d82c3e3 100644 --- a/src/librustc_mir/interpret/mod.rs +++ b/src/librustc_mir/interpret/mod.rs @@ -30,4 +30,4 @@ pub use self::const_eval::{ pub use self::machine::Machine; -pub use self::memory::{write_target_uint, write_target_int, read_target_uint, read_target_int}; +pub use self::memory::{write_target_uint, write_target_int, read_target_uint}; diff --git a/src/librustc_mir/interpret/terminator/mod.rs b/src/librustc_mir/interpret/terminator/mod.rs index 51dee0aaff47c..babc784701475 100644 --- a/src/librustc_mir/interpret/terminator/mod.rs +++ b/src/librustc_mir/interpret/terminator/mod.rs @@ -401,7 +401,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { let ptr_size = self.memory.pointer_size(); let ptr_align = self.tcx.data_layout.pointer_align; let (ptr, vtable) = self.into_ptr_vtable_pair(args[0].value)?; - let fn_ptr = self.memory.read_ptr_sized_unsigned( + let fn_ptr = self.memory.read_ptr_sized( vtable.offset(ptr_size * (idx as u64 + 3), &self)?, ptr_align )?.to_ptr()?; diff --git a/src/librustc_mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs index c67cf1c84bf0b..ded27108e716a 100644 --- a/src/librustc_mir/interpret/traits.rs +++ b/src/librustc_mir/interpret/traits.rs @@ -79,8 +79,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { ) -> EvalResult<'tcx, (Size, Align)> { let pointer_size = self.memory.pointer_size(); let pointer_align = self.tcx.data_layout.pointer_align; - let size = self.memory.read_ptr_sized_unsigned(vtable.offset(pointer_size, self)?, pointer_align)?.to_bytes()? as u64; - let align = self.memory.read_ptr_sized_unsigned( + let size = self.memory.read_ptr_sized(vtable.offset(pointer_size, self)?, pointer_align)?.to_bytes()? as u64; + let align = self.memory.read_ptr_sized( vtable.offset(pointer_size * 2, self)?, pointer_align )?.to_bytes()? as u64; From 3dc7e93db632ccbd4616808a61c07aa048d6b814 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 23 Feb 2018 09:21:31 +0100 Subject: [PATCH 101/110] Generator assertions are unconditionally triggered --- src/librustc_mir/transform/const_prop.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 1b64178477e16..565e5de85c1e8 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -404,10 +404,12 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> { ); use rustc::mir::AssertMessage::*; match msg { - GeneratorResumedAfterReturn => - lint.span_label(span, "generator resumed after completion"), - GeneratorResumedAfterPanic => - lint.span_label(span, "generator resumed after panicking"), + // Need proper const propagator for these + GeneratorResumedAfterReturn | + GeneratorResumedAfterPanic => { + lint.cancel(); + return; + }, Math(ref err) => lint.span_label(span, err.description()), BoundsCheck { ref len, ref index } => { let len = self.eval_operand(len).expect("len must be const"); From 273025675d5c526ae5a0b212b4a46a7fb9938d01 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 23 Feb 2018 11:20:51 +0100 Subject: [PATCH 102/110] Remove unused error code --- src/librustc_typeck/diagnostics.rs | 35 ------------------------------ 1 file changed, 35 deletions(-) diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 96b2ec745f132..bd2267a46010b 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -1020,41 +1020,6 @@ Here `X` will have already been specified the discriminant 0 by the time `Y` is encountered, so a conflict occurs. "##, -E0082: r##" -#### Note: this error code is no longer emitted by the compiler. - -When you specify enum discriminants with `=`, the compiler expects `isize` -values by default. Or you can add the `repr` attibute to the enum declaration -for an explicit choice of the discriminant type. In either cases, the -discriminant values must fall within a valid range for the expected type; -otherwise this error is raised. For example: - -```compile_fail -# #![deny(overflowing_literals)] -#[repr(u8)] -enum Thing { - A = 1024, - B = 5, -} -``` - -Here, 1024 lies outside the valid range for `u8`, so the discriminant for `A` is -invalid. Here is another, more subtle example which depends on target word size: - -```compile_fail,E0080 -# #[repr(i32)] -enum DependsOnPointerSize { - A = 1 << 32, -} -``` - -Here, `1 << 32` is interpreted as an `isize` value. So it is invalid for 32 bit -target (`target_pointer_width = "32"`) but valid for 64 bit target. - -You may want to change representation types to fix this, or else change invalid -discriminant values so that they fit within the existing type. -"##, - E0084: r##" An unsupported representation was attempted on a zero-variant enum. From eb18d39a04dc524232471d1011bfd7c1f1fcad4d Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 23 Feb 2018 13:39:41 +0100 Subject: [PATCH 103/110] Reuse `Integer::from_attr` --- src/librustc_mir/hair/cx/mod.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index 28eef70679b95..57aa7bd4afeef 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -23,7 +23,7 @@ use rustc::hir::map::blocks::FnLikeNode; use rustc::middle::region; use rustc::infer::InferCtxt; use rustc::ty::subst::Subst; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::{self, Ty, TyCtxt, layout}; use rustc::ty::subst::Substs; use syntax::ast::{self, LitKind}; use syntax::attr; @@ -153,11 +153,12 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { &self, ty: Ty, ) -> u64 { - match ty.sty { - ty::TyInt(ity) => ity.bit_width(), - ty::TyUint(uty) => uty.bit_width(), + let ty = match ty.sty { + ty::TyInt(ity) => attr::IntType::SignedInt(ity), + ty::TyUint(uty) => attr::IntType::UnsignedInt(uty), _ => bug!("{} is not an integer", ty), - }.map_or(self.tcx.data_layout.pointer_size.bits(), |n| n as u64) + }; + layout::Integer::from_attr(self.tcx, ty).size().bits() } pub fn const_eval_literal( From 6cfda078bf36e081323b118082af822bacd8aef5 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 23 Feb 2018 13:52:33 +0100 Subject: [PATCH 104/110] Use clearer name for integer bit width helper function --- src/librustc_mir/build/expr/as_rvalue.rs | 4 ++-- src/librustc_mir/hair/cx/mod.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index 59f44fa9229a9..1a9064aab1b2f 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -381,7 +381,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // Helper to get a `-1` value of the appropriate type fn neg_1_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> { - let bits = self.hir.type_bit_size(ty); + let bits = self.hir.integer_bit_width(ty); let n = (!0u128) >> (128 - bits); let literal = Literal::Value { value: self.hir.tcx().mk_const(ty::Const { @@ -396,7 +396,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // Helper to get the minimum value of the appropriate type fn minval_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> { assert!(ty.is_signed()); - let bits = self.hir.type_bit_size(ty); + let bits = self.hir.integer_bit_width(ty); let n = 1 << (bits - 1); let literal = Literal::Value { value: self.hir.tcx().mk_const(ty::Const { diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index 57aa7bd4afeef..d8d5f5073abc1 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -149,7 +149,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { } } - pub fn type_bit_size( + pub fn integer_bit_width( &self, ty: Ty, ) -> u64 { @@ -179,7 +179,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { }; let clamp = |n| { - let size = self.type_bit_size(ty); + let size = self.integer_bit_width(ty); trace!("clamp {} with size {} and amt {}", n, size, 128 - size); let amt = 128 - size; let result = (n << amt) >> amt; From aedd4c61ea372022e04be183da2f5768953ea2fe Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 5 Mar 2018 10:21:11 +0100 Subject: [PATCH 105/110] Regenerate tests --- src/Cargo.lock | 21 +----- src/librustc/ich/impls_ty.rs | 4 +- src/librustc/ty/maps/on_disk_cache.rs | 1 + src/librustc_driver/driver.rs | 2 +- src/librustc_mir/hair/pattern/mod.rs | 22 +++--- src/librustc_mir/monomorphize/collector.rs | 3 +- .../transform/uniform_array_move_out.rs | 2 +- src/librustc_passes/rvalue_promotion.rs | 13 +--- src/test/ui/const-eval-overflow-2.stderr | 2 +- src/test/ui/const-eval-overflow-4.stderr | 8 --- .../conditional_array_execution.stderr | 25 ++----- .../ui/const-eval/index_out_of_bound.stderr | 9 +++ src/test/ui/const-eval/issue-43197.stderr | 28 ++++---- src/test/ui/const-expr-addr-operator.stderr | 15 ---- src/test/ui/const-fn-error.stderr | 47 +++---------- .../const-len-underflow-separate-spans.stderr | 18 ++--- .../const-pattern-not-const-evaluable.stderr | 15 ---- src/test/ui/discrim-overflow-2.stderr | 32 ++++----- src/test/ui/discrim-overflow.stderr | 32 ++++----- src/test/ui/error-codes/E0030-teach.rs | 2 +- src/test/ui/error-codes/E0030.rs | 2 +- src/test/ui/error-codes/E0080.stderr | 22 ++++-- src/test/ui/error-codes/E0081.stderr | 6 +- src/test/ui/error-codes/E0370.stderr | 4 +- .../ui/feature-gate-const-indexing.stderr | 9 --- .../ui/infinite-recursion-const-fn.stderr | 70 ++++++++++++++++++- src/test/ui/issue-15524.stderr | 18 ++--- src/test/ui/issue-23302-1.stderr | 9 ++- src/test/ui/issue-23302-2.stderr | 9 ++- src/test/ui/issue-23302-3.rs | 4 +- src/test/ui/issue-23302-3.stderr | 28 +++++--- src/test/ui/issue-36163.stderr | 16 ++++- src/test/ui/issue-38875/issue_38875.stderr | 15 ---- src/test/ui/union/union-const-eval.stderr | 17 ----- 34 files changed, 247 insertions(+), 283 deletions(-) create mode 100644 src/test/ui/const-eval/index_out_of_bound.stderr diff --git a/src/Cargo.lock b/src/Cargo.lock index b70a01ebb3c18..34c077a6f7a03 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -1858,20 +1858,6 @@ dependencies = [ "syntax_pos 0.0.0", ] -[[package]] -name = "rustc_const_eval" -version = "0.0.0" -dependencies = [ - "arena 0.0.0", - "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc 0.0.0", - "rustc_const_math 0.0.0", - "rustc_data_structures 0.0.0", - "rustc_errors 0.0.0", - "syntax 0.0.0", - "syntax_pos 0.0.0", -] - [[package]] name = "rustc_const_math" version = "0.0.0" @@ -1914,7 +1900,6 @@ dependencies = [ "rustc_allocator 0.0.0", "rustc_back 0.0.0", "rustc_borrowck 0.0.0", - "rustc_const_eval 0.0.0", "rustc_data_structures 0.0.0", "rustc_errors 0.0.0", "rustc_incremental 0.0.0", @@ -1964,7 +1949,7 @@ version = "0.0.0" dependencies = [ "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", - "rustc_const_eval 0.0.0", + "rustc_mir 0.0.0", "syntax 0.0.0", "syntax_pos 0.0.0", ] @@ -2012,6 +1997,7 @@ dependencies = [ name = "rustc_mir" version = "0.0.0" dependencies = [ + "arena 0.0.0", "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "graphviz 0.0.0", @@ -2020,7 +2006,6 @@ dependencies = [ "rustc 0.0.0", "rustc_apfloat 0.0.0", "rustc_back 0.0.0", - "rustc_const_eval 0.0.0", "rustc_const_math 0.0.0", "rustc_data_structures 0.0.0", "rustc_errors 0.0.0", @@ -2046,10 +2031,10 @@ version = "0.0.0" dependencies = [ "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", - "rustc_const_eval 0.0.0", "rustc_const_math 0.0.0", "rustc_data_structures 0.0.0", "rustc_errors 0.0.0", + "rustc_mir 0.0.0", "syntax 0.0.0", "syntax_pos 0.0.0", ] diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 874c7c27deaa6..d927a151610ef 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -61,10 +61,10 @@ for ty::subst::Kind<'gcx> { } } -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for ty::subst::UnpackedKind<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { match self { ty::subst::UnpackedKind::Lifetime(lt) => lt.hash_stable(hcx, hasher), diff --git a/src/librustc/ty/maps/on_disk_cache.rs b/src/librustc/ty/maps/on_disk_cache.rs index 5a24b1067b1ba..49c4b8bc49d8c 100644 --- a/src/librustc/ty/maps/on_disk_cache.rs +++ b/src/librustc/ty/maps/on_disk_cache.rs @@ -362,6 +362,7 @@ impl<'sess> OnDiskCache<'sess> { cnum_map: cnum_map.as_ref().unwrap(), file_index_to_file: &self.file_index_to_file, file_index_to_stable_id: &self.file_index_to_stable_id, + synthetic_expansion_infos: &self.synthetic_expansion_infos, interpret_alloc_cache: FxHashMap::default(), }; diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 06108a72869f1..c9cf3f3b81fe6 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -36,7 +36,7 @@ use rustc_typeck as typeck; use rustc_privacy; use rustc_plugin::registry::Registry; use rustc_plugin as plugin; -use rustc_passes::{self, ast_validation, loops, consts, hir_stats}; +use rustc_passes::{self, ast_validation, loops, rvalue_promotion, hir_stats}; use super::Compilation; use serialize::json; diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs index 42e095a0b5beb..1774c95af0f05 100644 --- a/src/librustc_mir/hair/pattern/mod.rs +++ b/src/librustc_mir/hair/pattern/mod.rs @@ -373,13 +373,17 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { 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::Excluded, Ordering::Less) => + PatternKind::Range { lo, hi, end }, + (RangeEnd::Excluded, _) => { + span_err!( + self.tcx.sess, + lo_expr.span, + E0579, + "lower range bound must be less than upper", + ); + PatternKind::Wild + }, (RangeEnd::Included, Ordering::Greater) => { let mut err = struct_span_err!( self.tcx.sess, @@ -399,10 +403,10 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { to be less than or equal to the end of the range."); } err.emit(); + PatternKind::Wild }, - (RangeEnd::Included, _) => {} + (RangeEnd::Included, _) => PatternKind::Range { lo, hi, end }, } - PatternKind::Range { lo, hi, end } } _ => PatternKind::Wild } diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index a6df0465789b2..f6b47efca31cd 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -1090,8 +1090,7 @@ fn collect_miri<'a, 'tcx>( let instance = Instance::mono(tcx, did); if should_monomorphize_locally(tcx, &instance) { trace!("collecting static {:?}", did); - let node_id = tcx.hir.as_local_node_id(did).unwrap(); - output.push(MonoItem::Static(node_id)); + output.push(MonoItem::Static(did)); } } else if let Some(alloc) = tcx.interpret_interner.get_alloc(alloc_id) { trace!("collecting {:?} with {:#?}", alloc_id, alloc); diff --git a/src/librustc_mir/transform/uniform_array_move_out.rs b/src/librustc_mir/transform/uniform_array_move_out.rs index 983a44920ea6d..9cc3ffb30638f 100644 --- a/src/librustc_mir/transform/uniform_array_move_out.rs +++ b/src/librustc_mir/transform/uniform_array_move_out.rs @@ -203,7 +203,7 @@ impl MirPass for RestoreSubsliceArrayMoveOut { let opt_size = opt_src_place.and_then(|src_place| { let src_ty = src_place.ty(mir, tcx).to_ty(tcx); if let ty::TyArray(_, ref size_o) = src_ty.sty { - size_o.val.to_const_int().and_then(|v| v.to_u64()) + size_o.val.to_raw_bits().map(|n| n as u64) } else { None } diff --git a/src/librustc_passes/rvalue_promotion.rs b/src/librustc_passes/rvalue_promotion.rs index bda351eed40b2..16278c37a0cc0 100644 --- a/src/librustc_passes/rvalue_promotion.rs +++ b/src/librustc_passes/rvalue_promotion.rs @@ -32,7 +32,7 @@ use rustc::middle::expr_use_visitor as euv; use rustc::middle::mem_categorization as mc; use rustc::middle::mem_categorization::Categorization; use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::maps::{queries, Providers}; +use rustc::ty::maps::Providers; use rustc::ty::subst::Substs; use rustc::traits::Reveal; use rustc::util::nodemap::{ItemLocalSet, NodeSet}; @@ -325,16 +325,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node // Don't peek inside trait associated constants. false } else { - queries::const_is_rvalue_promotable_to_static::try_get(v.tcx, e.span, did) - .unwrap_or_else(|mut err| { - // A cycle between constants ought to be reported elsewhere. - err.cancel(); - v.tcx.sess.delay_span_bug( - e.span, - &format!("cycle encountered during const qualification: {:?}", - did)); - false - }) + v.tcx.at(e.span).const_is_rvalue_promotable_to_static(did) }; // Just in case the type is more specific than the definition, diff --git a/src/test/ui/const-eval-overflow-2.stderr b/src/test/ui/const-eval-overflow-2.stderr index 05a286d4e7eb3..f376de7cc4c17 100644 --- a/src/test/ui/const-eval-overflow-2.stderr +++ b/src/test/ui/const-eval-overflow-2.stderr @@ -5,7 +5,7 @@ LL | const NEG_NEG_128: i8 = -NEG_128; | ^^^^^^^^ attempt to negate with overflow | note: for pattern here - --> $DIR/const-eval-overflow-2.rs:27:9 + --> $DIR/const-eval-overflow-2.rs:26:9 | LL | NEG_NEG_128 => println!("A"), | ^^^^^^^^^^^ diff --git a/src/test/ui/const-eval-overflow-4.stderr b/src/test/ui/const-eval-overflow-4.stderr index b907a47afb963..e967f024ae3d7 100644 --- a/src/test/ui/const-eval-overflow-4.stderr +++ b/src/test/ui/const-eval-overflow-4.stderr @@ -1,11 +1,3 @@ -warning: constant evaluation error: attempt to add with overflow - --> $DIR/const-eval-overflow-4.rs:23:13 - | -LL | : [u32; (i8::MAX as i8 + 1i8) as usize] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: #[warn(const_err)] on by default - error[E0080]: constant evaluation error --> $DIR/const-eval-overflow-4.rs:23:13 | diff --git a/src/test/ui/const-eval/conditional_array_execution.stderr b/src/test/ui/const-eval/conditional_array_execution.stderr index 0e26db7ac0858..8d421f401caa5 100644 --- a/src/test/ui/const-eval/conditional_array_execution.stderr +++ b/src/test/ui/const-eval/conditional_array_execution.stderr @@ -1,26 +1,15 @@ error[E0080]: constant evaluation error - --> $DIR/conditional_array_execution.rs:13:19 - | -13 | const FOO: u32 = [X - Y, Y - X][(X < Y) as usize]; //~ E0080 - | ^^^^^ attempt to subtract with overflow - | -note: inside call to FOO - --> $DIR/conditional_array_execution.rs:13:1 + --> $DIR/conditional_array_execution.rs:16:20 | -13 | const FOO: u32 = [X - Y, Y - X][(X < Y) as usize]; //~ E0080 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | println!("{}", FOO); //~ E0080 + | ^^^ referenced constant has errors error[E0080]: constant evaluation error - --> $DIR/conditional_array_execution.rs:16:20 - | -16 | println!("{}", FOO); //~ E0080 - | ^^^ attempt to subtract with overflow - | -note: inside call to main - --> $DIR/conditional_array_execution.rs:16:20 + --> $DIR/conditional_array_execution.rs:13:19 | -16 | println!("{}", FOO); //~ E0080 - | ^^^ +LL | const FOO: u32 = [X - Y, Y - X][(X < Y) as usize]; //~ E0080 + | ^^^^^ attempt to subtract with overflow error: aborting due to 2 previous errors +If you want more information on this error, try using "rustc --explain E0080" diff --git a/src/test/ui/const-eval/index_out_of_bound.stderr b/src/test/ui/const-eval/index_out_of_bound.stderr new file mode 100644 index 0000000000000..7651fb257e3ce --- /dev/null +++ b/src/test/ui/const-eval/index_out_of_bound.stderr @@ -0,0 +1,9 @@ +error[E0080]: constant evaluation error + --> $DIR/index_out_of_bound.rs:11:19 + | +LL | static FOO: i32 = [][0]; + | ^^^^^ index out of bounds: the len is 0 but the index is 0 at $DIR/index_out_of_bound.rs:11:19: 11:24 + +error: aborting due to previous error + +If you want more information on this error, try using "rustc --explain E0080" diff --git a/src/test/ui/const-eval/issue-43197.stderr b/src/test/ui/const-eval/issue-43197.stderr index c523b749d376f..21b9a282c485e 100644 --- a/src/test/ui/const-eval/issue-43197.stderr +++ b/src/test/ui/const-eval/issue-43197.stderr @@ -1,16 +1,20 @@ -warning: constant evaluation error: attempt to subtract with overflow - --> $DIR/issue-43197.rs:18:20 +error[E0080]: constant evaluation error + --> $DIR/issue-43197.rs:20:23 | -LL | const X: u32 = 0-1; //~ ERROR constant evaluation error - | ^^^ +LL | println!("{} {}", X, Y); + | ^ referenced constant has errors + +error[E0080]: constant evaluation error + --> $DIR/issue-43197.rs:20:26 | - = note: #[warn(const_err)] on by default +LL | println!("{} {}", X, Y); + | ^ referenced constant has errors -warning: constant evaluation error: attempt to subtract with overflow - --> $DIR/issue-43197.rs:20:20 +error[E0080]: constant evaluation error + --> $DIR/issue-43197.rs:19:24 | LL | const Y: u32 = foo(0-1); //~ ERROR constant evaluation error - | ^^^^^^^^ + | ^^^ attempt to subtract with overflow error[E0080]: constant evaluation error --> $DIR/issue-43197.rs:18:20 @@ -18,12 +22,6 @@ error[E0080]: constant evaluation error LL | const X: u32 = 0-1; //~ ERROR constant evaluation error | ^^^ attempt to subtract with overflow -error[E0080]: constant evaluation error - --> $DIR/issue-43197.rs:20:24 - | -LL | const Y: u32 = foo(0-1); //~ ERROR constant evaluation error - | ^^^ attempt to subtract with overflow - -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors If you want more information on this error, try using "rustc --explain E0080" diff --git a/src/test/ui/const-expr-addr-operator.stderr b/src/test/ui/const-expr-addr-operator.stderr index bd86f270f319c..e69de29bb2d1d 100644 --- a/src/test/ui/const-expr-addr-operator.stderr +++ b/src/test/ui/const-expr-addr-operator.stderr @@ -1,15 +0,0 @@ -error[E0080]: constant evaluation error - --> $DIR/const-expr-addr-operator.rs:15:29 - | -LL | const X: &'static u32 = &22; //~ ERROR constant evaluation error - | ^^^ unimplemented constant expression: address operator - | -note: for pattern here - --> $DIR/const-expr-addr-operator.rs:17:9 - | -LL | X => 0, - | ^ - -error: aborting due to previous error - -If you want more information on this error, try using "rustc --explain E0080" diff --git a/src/test/ui/const-fn-error.stderr b/src/test/ui/const-fn-error.stderr index f7ce0565c85ca..d57efce0dffc0 100644 --- a/src/test/ui/const-fn-error.stderr +++ b/src/test/ui/const-fn-error.stderr @@ -1,62 +1,35 @@ -<<<<<<< HEAD -warning: constant evaluation error: non-constant path in constant expression - --> $DIR/const-fn-error.rs:27:19 - | -LL | let a : [i32; f(X)]; - | ^^^^ - | - = note: #[warn(const_err)] on by default - -error[E0016]: blocks in constant functions are limited to items and tail expressions - --> $DIR/const-fn-error.rs:16:19 - | -LL | let mut sum = 0; //~ ERROR blocks in constant functions are limited -======= error[E0016]: blocks in constant functions are limited to items and tail expressions --> $DIR/const-fn-error.rs:16:19 | -16 | let mut sum = 0; ->>>>>>> Produce instead of pointers +LL | let mut sum = 0; | ^ error[E0015]: calls in constant functions are limited to constant functions, struct and enum constructors --> $DIR/const-fn-error.rs:18:14 | -<<<<<<< HEAD -LL | for i in 0..x { //~ ERROR calls in constant functions -======= -18 | for i in 0..x { ->>>>>>> Report errors in statics during collecting instead of translating +LL | for i in 0..x { | ^^^^ error[E0019]: constant function contains unimplemented expression type --> $DIR/const-fn-error.rs:18:14 | -<<<<<<< HEAD -LL | for i in 0..x { //~ ERROR calls in constant functions -======= -18 | for i in 0..x { ->>>>>>> Report errors in statics during collecting instead of translating +LL | for i in 0..x { | ^^^^ error[E0080]: constant evaluation error -<<<<<<< HEAD - --> $DIR/const-fn-error.rs:21:5 + --> $DIR/const-fn-error.rs:18:14 | -LL | sum //~ ERROR E0080 - | ^^^ non-constant path in constant expression +LL | for i in 0..x { + | ^^^^ calling non-const fn `>::into_iter` +... +LL | let a : [i32; f(X)]; + | ---- inside call to `f` | note: for constant expression here - --> $DIR/const-fn-error.rs:27:13 + --> $DIR/const-fn-error.rs:29:13 | LL | let a : [i32; f(X)]; | ^^^^^^^^^^^ -======= - --> $DIR/const-fn-error.rs:28:19 - | -28 | let a : [i32; f(X)]; - | ^^^^ miri failed: machine error: Cannot evaluate within constants: "calling non-const fn `>::into_iter`" ->>>>>>> Produce instead of pointers error: aborting due to 4 previous errors diff --git a/src/test/ui/const-len-underflow-separate-spans.stderr b/src/test/ui/const-len-underflow-separate-spans.stderr index 6391433ebaf1e..301b063885c19 100644 --- a/src/test/ui/const-len-underflow-separate-spans.stderr +++ b/src/test/ui/const-len-underflow-separate-spans.stderr @@ -1,23 +1,15 @@ -warning: constant evaluation error: attempt to subtract with overflow - --> $DIR/const-len-underflow-separate-spans.rs:17:20 - | -LL | const LEN: usize = ONE - TWO; - | ^^^^^^^^^ - | - = note: #[warn(const_err)] on by default - error[E0080]: constant evaluation error --> $DIR/const-len-underflow-separate-spans.rs:17:20 | LL | const LEN: usize = ONE - TWO; | ^^^^^^^^^ attempt to subtract with overflow - | -note: for constant expression here - --> $DIR/const-len-underflow-separate-spans.rs:22:12 + +error[E0080]: constant evaluation error + --> $DIR/const-len-underflow-separate-spans.rs:21:17 | LL | let a: [i8; LEN] = unimplemented!(); - | ^^^^^^^^^ + | ^^^ referenced constant has errors -error: aborting due to previous error +error: aborting due to 2 previous errors If you want more information on this error, try using "rustc --explain E0080" diff --git a/src/test/ui/const-pattern-not-const-evaluable.stderr b/src/test/ui/const-pattern-not-const-evaluable.stderr index 74677c7f117a8..e69de29bb2d1d 100644 --- a/src/test/ui/const-pattern-not-const-evaluable.stderr +++ b/src/test/ui/const-pattern-not-const-evaluable.stderr @@ -1,15 +0,0 @@ -error[E0080]: constant evaluation error - --> $DIR/const-pattern-not-const-evaluable.rs:22:31 - | -LL | const BOO: Pair = Pair(Marmor, BlackForest); - | ^^^^ unimplemented constant expression: tuple struct constructors - | -note: for pattern here - --> $DIR/const-pattern-not-const-evaluable.rs:37:9 - | -LL | FOO => println!("hi"), - | ^^^ - -error: aborting due to previous error - -If you want more information on this error, try using "rustc --explain E0080" diff --git a/src/test/ui/discrim-overflow-2.stderr b/src/test/ui/discrim-overflow-2.stderr index 1facda94cd6e6..6162766b58705 100644 --- a/src/test/ui/discrim-overflow-2.stderr +++ b/src/test/ui/discrim-overflow-2.stderr @@ -2,65 +2,65 @@ error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow-2.rs:27:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 127i8 + | ^^^^ overflowed on value after 127 | - = note: explicitly set `OhNo = -128i8` if that is desired outcome + = note: explicitly set `OhNo = -128` if that is desired outcome error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow-2.rs:36:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 255u8 + | ^^^^ overflowed on value after 255 | - = note: explicitly set `OhNo = 0u8` if that is desired outcome + = note: explicitly set `OhNo = 0` if that is desired outcome error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow-2.rs:45:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 32767i16 + | ^^^^ overflowed on value after 32767 | - = note: explicitly set `OhNo = -32768i16` if that is desired outcome + = note: explicitly set `OhNo = -32768` if that is desired outcome error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow-2.rs:54:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 65535u16 + | ^^^^ overflowed on value after 65535 | - = note: explicitly set `OhNo = 0u16` if that is desired outcome + = note: explicitly set `OhNo = 0` if that is desired outcome error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow-2.rs:63:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 2147483647i32 + | ^^^^ overflowed on value after 2147483647 | - = note: explicitly set `OhNo = -2147483648i32` if that is desired outcome + = note: explicitly set `OhNo = -2147483648` if that is desired outcome error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow-2.rs:72:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 4294967295u32 + | ^^^^ overflowed on value after 4294967295 | - = note: explicitly set `OhNo = 0u32` if that is desired outcome + = note: explicitly set `OhNo = 0` if that is desired outcome error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow-2.rs:81:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 9223372036854775807i64 + | ^^^^ overflowed on value after 9223372036854775807 | - = note: explicitly set `OhNo = -9223372036854775808i64` if that is desired outcome + = note: explicitly set `OhNo = -9223372036854775808` if that is desired outcome error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow-2.rs:90:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 18446744073709551615u64 + | ^^^^ overflowed on value after 18446744073709551615 | - = note: explicitly set `OhNo = 0u64` if that is desired outcome + = note: explicitly set `OhNo = 0` if that is desired outcome error: aborting due to 8 previous errors diff --git a/src/test/ui/discrim-overflow.stderr b/src/test/ui/discrim-overflow.stderr index 43c032b12a6c4..a713aea1b21ef 100644 --- a/src/test/ui/discrim-overflow.stderr +++ b/src/test/ui/discrim-overflow.stderr @@ -2,65 +2,65 @@ error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow.rs:25:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 127i8 + | ^^^^ overflowed on value after 127 | - = note: explicitly set `OhNo = -128i8` if that is desired outcome + = note: explicitly set `OhNo = -128` if that is desired outcome error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow.rs:36:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 255u8 + | ^^^^ overflowed on value after 255 | - = note: explicitly set `OhNo = 0u8` if that is desired outcome + = note: explicitly set `OhNo = 0` if that is desired outcome error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow.rs:47:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 32767i16 + | ^^^^ overflowed on value after 32767 | - = note: explicitly set `OhNo = -32768i16` if that is desired outcome + = note: explicitly set `OhNo = -32768` if that is desired outcome error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow.rs:58:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 65535u16 + | ^^^^ overflowed on value after 65535 | - = note: explicitly set `OhNo = 0u16` if that is desired outcome + = note: explicitly set `OhNo = 0` if that is desired outcome error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow.rs:70:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 2147483647i32 + | ^^^^ overflowed on value after 2147483647 | - = note: explicitly set `OhNo = -2147483648i32` if that is desired outcome + = note: explicitly set `OhNo = -2147483648` if that is desired outcome error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow.rs:82:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 4294967295u32 + | ^^^^ overflowed on value after 4294967295 | - = note: explicitly set `OhNo = 0u32` if that is desired outcome + = note: explicitly set `OhNo = 0` if that is desired outcome error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow.rs:94:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 9223372036854775807i64 + | ^^^^ overflowed on value after 9223372036854775807 | - = note: explicitly set `OhNo = -9223372036854775808i64` if that is desired outcome + = note: explicitly set `OhNo = -9223372036854775808` if that is desired outcome error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow.rs:106:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 18446744073709551615u64 + | ^^^^ overflowed on value after 18446744073709551615 | - = note: explicitly set `OhNo = 0u64` if that is desired outcome + = note: explicitly set `OhNo = 0` if that is desired outcome error: aborting due to 8 previous errors diff --git a/src/test/ui/error-codes/E0030-teach.rs b/src/test/ui/error-codes/E0030-teach.rs index e49b992139e02..2af32eda62be9 100644 --- a/src/test/ui/error-codes/E0030-teach.rs +++ b/src/test/ui/error-codes/E0030-teach.rs @@ -11,7 +11,7 @@ // compile-flags: -Z teach fn main() { - match 5u32 { //~ERROR non-exhaustive patterns: `_` not covered + match 5u32 { 1000 ... 5 => {} //~^ ERROR lower range bound must be less than or equal to upper } diff --git a/src/test/ui/error-codes/E0030.rs b/src/test/ui/error-codes/E0030.rs index 896dd2599bf53..ef3bded4beffb 100644 --- a/src/test/ui/error-codes/E0030.rs +++ b/src/test/ui/error-codes/E0030.rs @@ -10,7 +10,7 @@ fn main() { - match 5u32 { //~ ERROR non-exhaustive patterns + match 5u32 { 1000 ... 5 => {} //~^ ERROR lower range bound must be less than or equal to upper } diff --git a/src/test/ui/error-codes/E0080.stderr b/src/test/ui/error-codes/E0080.stderr index 01cfa0c375bb1..501051b9fe617 100644 --- a/src/test/ui/error-codes/E0080.stderr +++ b/src/test/ui/error-codes/E0080.stderr @@ -1,10 +1,10 @@ -warning: constant evaluation error: attempt to shift left with overflow +error: bitshift exceeds the type's number of bits --> $DIR/E0080.rs:12:9 | LL | X = (1 << 500), //~ ERROR E0080 | ^^^^^^^^^^ | - = note: #[warn(const_err)] on by default + = note: #[deny(exceeding_bitshifts)] on by default error[E0080]: constant evaluation error --> $DIR/E0080.rs:12:9 @@ -12,18 +12,26 @@ error[E0080]: constant evaluation error LL | X = (1 << 500), //~ ERROR E0080 | ^^^^^^^^^^ attempt to shift left with overflow -warning: constant evaluation error: attempt to divide by zero - --> $DIR/E0080.rs:14:9 +warning: constant evaluation error + --> $DIR/E0080.rs:15:9 + | +LL | Y = (1 / 0) //~ ERROR E0080 + | ^^^^^^^ attempt to divide by zero + | + = note: #[warn(const_err)] on by default + +warning: constant evaluation error + --> $DIR/E0080.rs:15:9 | LL | Y = (1 / 0) //~ ERROR E0080 - | ^^^^^^^ + | ^^^^^^^ attempted to do overflowing math error[E0080]: constant evaluation error - --> $DIR/E0080.rs:14:9 + --> $DIR/E0080.rs:15:9 | LL | Y = (1 / 0) //~ ERROR E0080 | ^^^^^^^ attempt to divide by zero -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors If you want more information on this error, try using "rustc --explain E0080" diff --git a/src/test/ui/error-codes/E0081.stderr b/src/test/ui/error-codes/E0081.stderr index 42d0769bb5701..e1133c1fc587e 100644 --- a/src/test/ui/error-codes/E0081.stderr +++ b/src/test/ui/error-codes/E0081.stderr @@ -1,10 +1,10 @@ -error[E0081]: discriminant value `3isize` already exists +error[E0081]: discriminant value `3` already exists --> $DIR/E0081.rs:13:9 | LL | P = 3, - | - first use of `3isize` + | - first use of `3` LL | X = 3, - | ^ enum already has `3isize` + | ^ enum already has `3` error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0370.stderr b/src/test/ui/error-codes/E0370.stderr index 1c7a18bd6d867..60e077e063e09 100644 --- a/src/test/ui/error-codes/E0370.stderr +++ b/src/test/ui/error-codes/E0370.stderr @@ -2,9 +2,9 @@ error[E0370]: enum discriminant overflowed --> $DIR/E0370.rs:17:5 | LL | Y, //~ ERROR E0370 - | ^ overflowed on value after 9223372036854775807i64 + | ^ overflowed on value after 9223372036854775807 | - = note: explicitly set `Y = -9223372036854775808i64` if that is desired outcome + = note: explicitly set `Y = -9223372036854775808` if that is desired outcome error: aborting due to previous error diff --git a/src/test/ui/feature-gate-const-indexing.stderr b/src/test/ui/feature-gate-const-indexing.stderr index 419400bb0ac0c..e69de29bb2d1d 100644 --- a/src/test/ui/feature-gate-const-indexing.stderr +++ b/src/test/ui/feature-gate-const-indexing.stderr @@ -1,9 +0,0 @@ -error[E0080]: constant evaluation error - --> $DIR/feature-gate-const-indexing.rs:16:24 - | -LL | const BLUB: [i32; (ARR[0] - 41) as usize] = [5]; //~ ERROR constant evaluation error - | ^^^^^^ the index operation on const values is unstable - -error: aborting due to previous error - -If you want more information on this error, try using "rustc --explain E0080" diff --git a/src/test/ui/infinite-recursion-const-fn.stderr b/src/test/ui/infinite-recursion-const-fn.stderr index ce1b35aac7ecc..bc062ecce2587 100644 --- a/src/test/ui/infinite-recursion-const-fn.stderr +++ b/src/test/ui/infinite-recursion-const-fn.stderr @@ -1,8 +1,72 @@ error[E0080]: constant evaluation error - --> $DIR/infinite-recursion-const-fn.rs:16:18 + --> $DIR/infinite-recursion-const-fn.rs:14:25 | -16 | const ARR: [i32; a()] = [5; 6]; //~ ERROR constant evaluation error - | ^^^ miri failed: reached the configured maximum number of stack frames +LL | const fn a() -> usize { b() } //~ ERROR constant evaluation error + | ^^^ + | | + | reached the configured maximum number of stack frames + | inside call to `b` +LL | const fn b() -> usize { a() } + | --- + | | + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` +LL | const ARR: [i32; a()] = [5; 6]; + | --- inside call to `a` + | +note: for constant expression here + --> $DIR/infinite-recursion-const-fn.rs:16:1 + | +LL | const ARR: [i32; a()] = [5; 6]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error +If you want more information on this error, try using "rustc --explain E0080" diff --git a/src/test/ui/issue-15524.stderr b/src/test/ui/issue-15524.stderr index 1cb16dfe19a58..e26766eca373d 100644 --- a/src/test/ui/issue-15524.stderr +++ b/src/test/ui/issue-15524.stderr @@ -1,28 +1,28 @@ -error[E0081]: discriminant value `1isize` already exists +error[E0081]: discriminant value `1` already exists --> $DIR/issue-15524.rs:15:9 | LL | A = 1, - | - first use of `1isize` + | - first use of `1` LL | B = 1, - | ^ enum already has `1isize` + | ^ enum already has `1` -error[E0081]: discriminant value `1isize` already exists +error[E0081]: discriminant value `1` already exists --> $DIR/issue-15524.rs:18:5 | LL | A = 1, - | - first use of `1isize` + | - first use of `1` ... LL | D, - | ^ enum already has `1isize` + | ^ enum already has `1` -error[E0081]: discriminant value `1isize` already exists +error[E0081]: discriminant value `1` already exists --> $DIR/issue-15524.rs:21:9 | LL | A = 1, - | - first use of `1isize` + | - first use of `1` ... LL | E = N, - | ^ enum already has `1isize` + | ^ enum already has `1` error: aborting due to 3 previous errors diff --git a/src/test/ui/issue-23302-1.stderr b/src/test/ui/issue-23302-1.stderr index 13a2a23cc527d..087eae43a2994 100644 --- a/src/test/ui/issue-23302-1.stderr +++ b/src/test/ui/issue-23302-1.stderr @@ -5,10 +5,15 @@ LL | A = X::A as isize, //~ ERROR E0391 | ^^^^^^^^^^^^^ cyclic reference | note: the cycle begins when const-evaluating `X::A::{{initializer}}`... - --> $DIR/issue-23302-1.rs:14:5 + --> $DIR/issue-23302-1.rs:14:9 + | +LL | A = X::A as isize, //~ ERROR E0391 + | ^^^^^^^^^^^^^ +note: ...which then requires computing layout of `X`... + --> $DIR/issue-23302-1.rs:14:9 | LL | A = X::A as isize, //~ ERROR E0391 - | ^^^^^^^^^^^^^^^^^ + | ^^^^ = note: ...which then again requires const-evaluating `X::A::{{initializer}}`, completing the cycle. error: aborting due to previous error diff --git a/src/test/ui/issue-23302-2.stderr b/src/test/ui/issue-23302-2.stderr index f303fa7c6718e..66ba5c3258211 100644 --- a/src/test/ui/issue-23302-2.stderr +++ b/src/test/ui/issue-23302-2.stderr @@ -5,10 +5,15 @@ LL | A = Y::B as isize, //~ ERROR E0391 | ^^^^^^^^^^^^^ cyclic reference | note: the cycle begins when const-evaluating `Y::A::{{initializer}}`... - --> $DIR/issue-23302-2.rs:14:5 + --> $DIR/issue-23302-2.rs:14:9 + | +LL | A = Y::B as isize, //~ ERROR E0391 + | ^^^^^^^^^^^^^ +note: ...which then requires computing layout of `Y`... + --> $DIR/issue-23302-2.rs:14:9 | LL | A = Y::B as isize, //~ ERROR E0391 - | ^^^^^^^^^^^^^^^^^ + | ^^^^ = note: ...which then again requires const-evaluating `Y::A::{{initializer}}`, completing the cycle. error: aborting due to previous error diff --git a/src/test/ui/issue-23302-3.rs b/src/test/ui/issue-23302-3.rs index 1d750b09025b9..5903acc8b7a6d 100644 --- a/src/test/ui/issue-23302-3.rs +++ b/src/test/ui/issue-23302-3.rs @@ -8,8 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -const A: i32 = B; //~ ERROR E0391 +const A: i32 = B; -const B: i32 = A; +const B: i32 = A; //~ ERROR cyclic dependency detected fn main() { } diff --git a/src/test/ui/issue-23302-3.stderr b/src/test/ui/issue-23302-3.stderr index ec615809749ae..31168579394d3 100644 --- a/src/test/ui/issue-23302-3.stderr +++ b/src/test/ui/issue-23302-3.stderr @@ -1,20 +1,30 @@ error[E0391]: cyclic dependency detected - --> $DIR/issue-23302-3.rs:11:16 + --> $DIR/issue-23302-3.rs:13:16 | -LL | const A: i32 = B; //~ ERROR E0391 +LL | const B: i32 = A; //~ ERROR cyclic dependency detected | ^ cyclic reference | -note: the cycle begins when processing `B`... - --> $DIR/issue-23302-3.rs:13:1 +note: the cycle begins when const checking if rvalue is promotable to static `A`... + --> $DIR/issue-23302-3.rs:11:1 | -LL | const B: i32 = A; +LL | const A: i32 = B; | ^^^^^^^^^^^^^^^^^ -note: ...which then requires processing `A`... - --> $DIR/issue-23302-3.rs:13:16 +note: ...which then requires checking which parts of `A` are promotable to static... + --> $DIR/issue-23302-3.rs:11:1 + | +LL | const A: i32 = B; + | ^^^^^^^^^^^^^^^^^ +note: ...which then requires const checking if rvalue is promotable to static `B`... + --> $DIR/issue-23302-3.rs:11:16 | -LL | const B: i32 = A; +LL | const A: i32 = B; | ^ - = note: ...which then again requires processing `B`, completing the cycle. +note: ...which then requires checking which parts of `B` are promotable to static... + --> $DIR/issue-23302-3.rs:13:1 + | +LL | const B: i32 = A; //~ ERROR cyclic dependency detected + | ^^^^^^^^^^^^^^^^^ + = note: ...which then again requires const checking if rvalue is promotable to static `A`, completing the cycle. error: aborting due to previous error diff --git a/src/test/ui/issue-36163.stderr b/src/test/ui/issue-36163.stderr index c511b576a2184..4323eb4858f40 100644 --- a/src/test/ui/issue-36163.stderr +++ b/src/test/ui/issue-36163.stderr @@ -5,15 +5,25 @@ LL | B = A, //~ ERROR E0391 | ^ cyclic reference | note: the cycle begins when const-evaluating `Foo::B::{{initializer}}`... - --> $DIR/issue-36163.rs:14:5 + --> $DIR/issue-36163.rs:14:9 | LL | B = A, //~ ERROR E0391 - | ^^^^^ -note: ...which then requires const-evaluating `A`... + | ^ +note: ...which then requires processing `Foo::B::{{initializer}}`... --> $DIR/issue-36163.rs:14:9 | LL | B = A, //~ ERROR E0391 | ^ +note: ...which then requires const-evaluating `A`... + --> $DIR/issue-36163.rs:11:1 + | +LL | const A: isize = Foo::B as isize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which then requires computing layout of `Foo`... + --> $DIR/issue-36163.rs:11:18 + | +LL | const A: isize = Foo::B as isize; + | ^^^^^^ = note: ...which then again requires const-evaluating `Foo::B::{{initializer}}`, completing the cycle. error: aborting due to previous error diff --git a/src/test/ui/issue-38875/issue_38875.stderr b/src/test/ui/issue-38875/issue_38875.stderr index 9a412be6a674e..e69de29bb2d1d 100644 --- a/src/test/ui/issue-38875/issue_38875.stderr +++ b/src/test/ui/issue-38875/issue_38875.stderr @@ -1,15 +0,0 @@ -error[E0080]: constant evaluation error - --> $DIR/auxiliary/issue_38875_b.rs:11:24 - | -LL | pub const FOO: usize = *&0; - | ^^^ unimplemented constant expression: deref operation - | -note: for constant expression here - --> $DIR/issue_38875.rs:16:22 - | -LL | let test_x = [0; issue_38875_b::FOO]; - | ^^^^^^^^^^^^^^^^^^ - -error: aborting due to previous error - -If you want more information on this error, try using "rustc --explain E0080" diff --git a/src/test/ui/union/union-const-eval.stderr b/src/test/ui/union/union-const-eval.stderr index afd661337c6c4..e69de29bb2d1d 100644 --- a/src/test/ui/union/union-const-eval.stderr +++ b/src/test/ui/union/union-const-eval.stderr @@ -1,17 +0,0 @@ -warning: constant evaluation error: nonexistent struct field - --> $DIR/union-const-eval.rs:21:21 - | -LL | let b: [u8; C.b]; //~ ERROR constant evaluation error - | ^^^ - | - = note: #[warn(const_err)] on by default - -error[E0080]: constant evaluation error - --> $DIR/union-const-eval.rs:21:21 - | -LL | let b: [u8; C.b]; //~ ERROR constant evaluation error - | ^^^ nonexistent struct field - -error: aborting due to previous error - -If you want more information on this error, try using "rustc --explain E0080" From edc5f7343338ddbbc2bae94be585cd821dbe8118 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 6 Mar 2018 10:26:23 +0100 Subject: [PATCH 106/110] Correct the shift overflow check in miri --- src/librustc_mir/interpret/operator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_mir/interpret/operator.rs b/src/librustc_mir/interpret/operator.rs index a74fe63e53c86..dfc0c4a824a84 100644 --- a/src/librustc_mir/interpret/operator.rs +++ b/src/librustc_mir/interpret/operator.rs @@ -90,7 +90,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { let signed = left_layout.abi.is_signed(); let mut r = r as u32; let size = left_layout.size.bits() as u32; - let oflo = r > size; + let oflo = r >= size; if oflo { r %= size; } From b5ace9a906d0a445420a0ff16f77ce967722ddc6 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 6 Mar 2018 12:43:02 +0100 Subject: [PATCH 107/110] Unify the const folding errors before they differed depending on whether optimizations were on or not --- src/librustc_mir/transform/const_prop.rs | 142 ++++++++++++++---- src/test/compile-fail/const-err-early.rs | 7 +- src/test/compile-fail/const-err-multi.rs | 4 +- src/test/compile-fail/const-err2.rs | 5 + src/test/compile-fail/const-eval-overflow2.rs | 8 + .../compile-fail/const-eval-overflow2b.rs | 8 + .../compile-fail/const-eval-overflow2c.rs | 8 + src/test/compile-fail/eval-enum.rs | 2 - src/test/compile-fail/issue-44578.rs | 2 +- src/test/compile-fail/issue-8460-const.rs | 81 +++------- .../compile-fail/lint-exceeding-bitshifts.rs | 40 ++--- .../compile-fail/lint-exceeding-bitshifts2.rs | 6 +- src/test/ui/const-eval-overflow-4.rs | 1 + src/test/ui/const-eval-overflow-4.stderr | 8 + .../const-eval/conditional_array_execution.rs | 3 +- .../conditional_array_execution.stderr | 18 ++- src/test/ui/const-eval/issue-43197.rs | 6 +- src/test/ui/const-eval/issue-43197.stderr | 30 ++-- src/test/ui/const-eval/pub_const_err.rs | 16 ++ src/test/ui/const-eval/pub_const_err.stderr | 14 ++ .../ui/const-len-underflow-separate-spans.rs | 1 + .../const-len-underflow-separate-spans.stderr | 10 +- src/test/ui/error-codes/E0080.rs | 1 - src/test/ui/error-codes/E0080.stderr | 12 +- 24 files changed, 278 insertions(+), 155 deletions(-) create mode 100644 src/test/ui/const-eval/pub_const_err.rs create mode 100644 src/test/ui/const-eval/pub_const_err.stderr diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 565e5de85c1e8..c8e1a93cdda2c 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -12,10 +12,10 @@ //! assertion failures - +use rustc::hir::def::Def; use rustc::mir::{Constant, Literal, Location, Place, Mir, Operand, Rvalue, Local}; use rustc::mir::{NullOp, StatementKind, Statement, BasicBlock, LocalKind}; -use rustc::mir::{TerminatorKind, ClearCrossCrate, SourceInfo, BinOp}; +use rustc::mir::{TerminatorKind, ClearCrossCrate, SourceInfo, BinOp, ProjectionElem}; use rustc::mir::visit::{Visitor, PlaceContext}; use rustc::middle::const_val::ConstVal; use rustc::ty::{TyCtxt, self, Instance}; @@ -26,6 +26,10 @@ use syntax::codemap::Span; use rustc::ty::subst::Substs; use rustc_data_structures::indexed_vec::IndexVec; use rustc::ty::ParamEnv; +use rustc::ty::layout::{ + LayoutOf, TyLayout, LayoutError, + HasTyCtxt, TargetDataLayout, HasDataLayout, +}; pub struct ConstProp; @@ -34,6 +38,15 @@ impl MirPass for ConstProp { tcx: TyCtxt<'a, 'tcx, 'tcx>, source: MirSource, mir: &mut Mir<'tcx>) { + // will be evaluated by miri and produce its errors there + if source.promoted.is_some() { + return; + } + match tcx.describe_def(source.def_id) { + // skip statics because they'll be evaluated by miri anyway + Some(Def::Static(..)) => return, + _ => {}, + } trace!("ConstProp starting for {:?}", source.def_id); // FIXME(oli-obk, eddyb) Optimize locals (or even local paths) to hold @@ -59,6 +72,28 @@ struct ConstPropagator<'b, 'a, 'tcx:'a+'b> { param_env: ParamEnv<'tcx>, } +impl<'a, 'b, 'tcx> LayoutOf> for &'a ConstPropagator<'a, 'b, 'tcx> { + type TyLayout = Result, LayoutError<'tcx>>; + + fn layout_of(self, ty: ty::Ty<'tcx>) -> Self::TyLayout { + self.tcx.layout_of(self.param_env.and(ty)) + } +} + +impl<'a, 'b, 'tcx> HasDataLayout for &'a ConstPropagator<'a, 'b, 'tcx> { + #[inline] + fn data_layout(&self) -> &TargetDataLayout { + &self.tcx.data_layout + } +} + +impl<'a, 'b, 'tcx> HasTyCtxt<'tcx> for &'a ConstPropagator<'a, 'b, 'tcx> { + #[inline] + fn tcx<'c>(&'c self) -> TyCtxt<'c, 'tcx, 'tcx> { + self.tcx + } +} + impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { fn new( mir: &'b Mir<'tcx>, @@ -134,15 +169,43 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { } } + fn eval_place(&mut self, place: &Place<'tcx>) -> Option> { + match *place { + Place::Local(loc) => self.places[loc].clone(), + Place::Projection(ref proj) => match proj.elem { + ProjectionElem::Field(field, _) => { + trace!("field proj on {:?}", proj.base); + let (base, ty, span) = self.eval_place(&proj.base)?; + match base { + Value::ByValPair(a, b) => { + trace!("by val pair: {:?}, {:?}", a, b); + let base_layout = self.tcx.layout_of(self.param_env.and(ty)).ok()?; + trace!("layout computed"); + use rustc_data_structures::indexed_vec::Idx; + let field_index = field.index(); + let val = if field_index == 0 { + a + } else { + assert_eq!(field_index, 1); + b + }; + let field = base_layout.field(&*self, field_index).ok()?; + trace!("projection resulted in: {:?}", val); + Some((Value::ByVal(val), field.ty, span)) + }, + _ => None, + } + }, + _ => None, + }, + _ => None, + } + } + fn eval_operand(&mut self, op: &Operand<'tcx>) -> Option> { match *op { Operand::Constant(ref c) => self.eval_constant(c), - Operand::Move(ref place) | Operand::Copy(ref place) => match *place { - Place::Local(loc) => self.places[loc].clone(), - // FIXME(oli-obk): field and index projections - Place::Projection(_) => None, - _ => None, - }, + Operand::Move(ref place) | Operand::Copy(ref place) => self.eval_place(place), } } @@ -235,18 +298,24 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { let r = ecx.value_to_primval(ValTy { value: right.0, ty: right.1 }).ok()?; if op == BinOp::Shr || op == BinOp::Shl { let param_env = self.tcx.param_env(self.source.def_id); - let bits = self.tcx.layout_of(param_env.and(place_ty)).unwrap().size.bits(); + let left_ty = left.ty(self.mir, self.tcx); + let bits = self.tcx.layout_of(param_env.and(left_ty)).unwrap().size.bits(); if r.to_bytes().ok().map_or(false, |b| b >= bits as u128) { let scope_info = match self.mir.visibility_scope_info { ClearCrossCrate::Set(ref data) => data, ClearCrossCrate::Clear => return None, }; + let dir = if op == BinOp::Shr { + "right" + } else { + "left" + }; 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"); + &format!("attempt to shift {} with overflow", dir)); return None; } } @@ -334,6 +403,7 @@ impl<'tcx> Visitor<'tcx> for CanConstProp { Copy | Move | StorageDead | StorageLive | Validate | + Projection(_) | Inspect => {}, _ => self.can_const_prop[local] = false, } @@ -364,6 +434,7 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> { .to_ty(self.tcx); if let Some(value) = self.const_prop(rval, place_ty, statement.source_info) { if let Place::Local(local) = *place { + trace!("checking whether {:?} can be stored to {:?}", value, local); if self.can_const_prop[local] { trace!("storing {:?} to {:?}", value, local); assert!(self.places[local].is_none()); @@ -384,7 +455,22 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> { self.super_terminator_kind(block, kind, location); if let TerminatorKind::Assert { expected, msg, cond, .. } = kind { if let Some(value) = self.eval_operand(cond) { + trace!("assertion on {:?} should be {:?}", value, expected); if Value::ByVal(PrimVal::from_bool(*expected)) != value.0 { + // poison all places this operand references so that further code + // doesn't use the invalid value + match cond { + Operand::Move(ref place) | Operand::Copy(ref place) => { + let mut place = place; + while let Place::Projection(ref proj) = *place { + place = &proj.base; + } + if let Place::Local(local) = *place { + self.places[local] = None; + } + }, + Operand::Constant(_) => {} + } let span = self.mir[block] .terminator .as_ref() @@ -396,21 +482,12 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> { .hir .as_local_node_id(self.source.def_id) .expect("some part of a failing const eval must be local"); - let mut lint = self.tcx.struct_span_lint_node( - ::rustc::lint::builtin::CONST_ERR, - node_id, - span, - "constant evaluation error", - ); use rustc::mir::AssertMessage::*; - match msg { + let msg = match msg { // Need proper const propagator for these GeneratorResumedAfterReturn | - GeneratorResumedAfterPanic => { - lint.cancel(); - return; - }, - Math(ref err) => lint.span_label(span, err.description()), + GeneratorResumedAfterPanic => return, + Math(ref err) => err.description().to_owned(), BoundsCheck { ref len, ref index } => { let len = self.eval_operand(len).expect("len must be const"); let len = match len.0 { @@ -424,17 +501,20 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> { Value::ByVal(PrimVal::Bytes(n)) => n, _ => bug!("const index not primitive: {:?}", index), }; - lint.span_label( - span, - format!( - "index out of bounds: \ - the len is {} but the index is {}", - len, - index, - ), + format!( + "index out of bounds: \ + the len is {} but the index is {}", + len, + index, ) }, - }.emit(); + }; + self.tcx.lint_node( + ::rustc::lint::builtin::CONST_ERR, + node_id, + span, + &msg, + ); } } } diff --git a/src/test/compile-fail/const-err-early.rs b/src/test/compile-fail/const-err-early.rs index 53077d74ce50d..3de0f1ff61e1c 100644 --- a/src/test/compile-fail/const-err-early.rs +++ b/src/test/compile-fail/const-err-early.rs @@ -11,11 +11,14 @@ #![deny(const_err)] pub const A: i8 = -std::i8::MIN; //~ ERROR E0080 -//~| ERROR const_err -//~| ERROR const_err +//~^ ERROR attempt to negate with overflow +//~| ERROR constant evaluation error pub const B: u8 = 200u8 + 200u8; //~ ERROR E0080 +//~^ ERROR attempt to add with overflow pub const C: u8 = 200u8 * 4; //~ ERROR E0080 +//~^ ERROR attempt to multiply with overflow pub const D: u8 = 42u8 - (42u8 + 1); //~ ERROR E0080 +//~^ ERROR attempt to subtract with overflow pub const E: u8 = [5u8][1]; //~^ ERROR E0080 diff --git a/src/test/compile-fail/const-err-multi.rs b/src/test/compile-fail/const-err-multi.rs index d72097848feaa..d2355f57f1729 100644 --- a/src/test/compile-fail/const-err-multi.rs +++ b/src/test/compile-fail/const-err-multi.rs @@ -12,8 +12,8 @@ pub const A: i8 = -std::i8::MIN; //~^ ERROR E0080 -//~| ERROR const_err -//~| ERROR const_err +//~| ERROR attempt to negate with overflow +//~| ERROR constant evaluation error pub const B: i8 = A; //~^ ERROR E0080 pub const C: u8 = A as u8; diff --git a/src/test/compile-fail/const-err2.rs b/src/test/compile-fail/const-err2.rs index 09983f179266d..46b73371e56cf 100644 --- a/src/test/compile-fail/const-err2.rs +++ b/src/test/compile-fail/const-err2.rs @@ -8,6 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// needed because negating int::MIN will behave differently between +// optimized compilation and unoptimized compilation and thus would +// lead to different lints being emitted +// compile-flags: -O + #![feature(rustc_attrs)] #![allow(exceeding_bitshifts)] #![deny(const_err)] diff --git a/src/test/compile-fail/const-eval-overflow2.rs b/src/test/compile-fail/const-eval-overflow2.rs index 25787b7bb4b92..a0d8f9672c05e 100644 --- a/src/test/compile-fail/const-eval-overflow2.rs +++ b/src/test/compile-fail/const-eval-overflow2.rs @@ -25,46 +25,54 @@ const VALS_I8: (i8,) = ( i8::MIN - 1, //~^ ERROR constant evaluation error + //~| ERROR attempt to subtract with overflow ); const VALS_I16: (i16,) = ( i16::MIN - 1, //~^ ERROR constant evaluation error + //~| ERROR attempt to subtract with overflow ); const VALS_I32: (i32,) = ( i32::MIN - 1, //~^ ERROR constant evaluation error + //~| ERROR attempt to subtract with overflow ); const VALS_I64: (i64,) = ( i64::MIN - 1, //~^ ERROR constant evaluation error + //~| ERROR attempt to subtract with overflow ); const VALS_U8: (u8,) = ( u8::MIN - 1, //~^ ERROR constant evaluation error + //~| ERROR attempt to subtract with overflow ); const VALS_U16: (u16,) = ( u16::MIN - 1, //~^ ERROR constant evaluation error + //~| ERROR attempt to subtract with overflow ); const VALS_U32: (u32,) = ( u32::MIN - 1, //~^ ERROR constant evaluation error + //~| ERROR attempt to subtract with overflow ); const VALS_U64: (u64,) = ( u64::MIN - 1, //~^ ERROR constant evaluation error + //~| ERROR attempt to subtract with overflow ); fn main() { diff --git a/src/test/compile-fail/const-eval-overflow2b.rs b/src/test/compile-fail/const-eval-overflow2b.rs index d8332757b642b..08128f90e532f 100644 --- a/src/test/compile-fail/const-eval-overflow2b.rs +++ b/src/test/compile-fail/const-eval-overflow2b.rs @@ -25,46 +25,54 @@ const VALS_I8: (i8,) = ( i8::MAX + 1, //~^ ERROR constant evaluation error + //~| ERROR attempt to add with overflow ); const VALS_I16: (i16,) = ( i16::MAX + 1, //~^ ERROR constant evaluation error + //~| ERROR attempt to add with overflow ); const VALS_I32: (i32,) = ( i32::MAX + 1, //~^ ERROR constant evaluation error + //~| ERROR attempt to add with overflow ); const VALS_I64: (i64,) = ( i64::MAX + 1, //~^ ERROR constant evaluation error + //~| ERROR attempt to add with overflow ); const VALS_U8: (u8,) = ( u8::MAX + 1, //~^ ERROR constant evaluation error + //~| ERROR attempt to add with overflow ); const VALS_U16: (u16,) = ( u16::MAX + 1, //~^ ERROR constant evaluation error + //~| ERROR attempt to add with overflow ); const VALS_U32: (u32,) = ( u32::MAX + 1, //~^ ERROR constant evaluation error + //~| ERROR attempt to add with overflow ); const VALS_U64: (u64,) = ( u64::MAX + 1, //~^ ERROR constant evaluation error + //~| ERROR attempt to add with overflow ); fn main() { diff --git a/src/test/compile-fail/const-eval-overflow2c.rs b/src/test/compile-fail/const-eval-overflow2c.rs index 2a8e4dc2c34fe..31a1638cade17 100644 --- a/src/test/compile-fail/const-eval-overflow2c.rs +++ b/src/test/compile-fail/const-eval-overflow2c.rs @@ -25,46 +25,54 @@ const VALS_I8: (i8,) = ( i8::MIN * 2, //~^ ERROR constant evaluation error + //~| ERROR attempt to multiply with overflow ); const VALS_I16: (i16,) = ( i16::MIN * 2, //~^ ERROR constant evaluation error + //~| ERROR attempt to multiply with overflow ); const VALS_I32: (i32,) = ( i32::MIN * 2, //~^ ERROR constant evaluation error + //~| ERROR attempt to multiply with overflow ); const VALS_I64: (i64,) = ( i64::MIN * 2, //~^ ERROR constant evaluation error + //~| ERROR attempt to multiply with overflow ); const VALS_U8: (u8,) = ( u8::MAX * 2, //~^ ERROR constant evaluation error + //~| ERROR attempt to multiply with overflow ); const VALS_U16: (u16,) = ( u16::MAX * 2, //~^ ERROR constant evaluation error + //~| ERROR attempt to multiply with overflow ); const VALS_U32: (u32,) = ( u32::MAX * 2, //~^ ERROR constant evaluation error + //~| ERROR attempt to multiply with overflow ); const VALS_U64: (u64,) = ( u64::MAX * 2, //~^ ERROR constant evaluation error + //~| ERROR attempt to multiply with overflow ); fn main() { diff --git a/src/test/compile-fail/eval-enum.rs b/src/test/compile-fail/eval-enum.rs index 469618407d8f9..49f76c532df54 100644 --- a/src/test/compile-fail/eval-enum.rs +++ b/src/test/compile-fail/eval-enum.rs @@ -13,12 +13,10 @@ enum Test { //~^ attempt to divide by zero //~| ERROR constant evaluation error //~| WARN constant evaluation error - //~| WARN constant evaluation error RemZero = 1%0, //~^ attempt to calculate the remainder with a divisor of zero //~| ERROR constant evaluation error //~| WARN constant evaluation error - //~| WARN constant evaluation error } fn main() {} diff --git a/src/test/compile-fail/issue-44578.rs b/src/test/compile-fail/issue-44578.rs index 9efe3d90a93c5..18cfb35113dc6 100644 --- a/src/test/compile-fail/issue-44578.rs +++ b/src/test/compile-fail/issue-44578.rs @@ -18,7 +18,7 @@ enum Bar { } impl Foo for Bar { - const AMT: usize = [A::AMT][(A::AMT > B::AMT) as usize]; //~ E0080 + const AMT: usize = [A::AMT][(A::AMT > B::AMT) as usize]; } impl Foo for u8 { diff --git a/src/test/compile-fail/issue-8460-const.rs b/src/test/compile-fail/issue-8460-const.rs index 024aca2e66d36..1d59e75a0f0f0 100644 --- a/src/test/compile-fail/issue-8460-const.rs +++ b/src/test/compile-fail/issue-8460-const.rs @@ -9,110 +9,69 @@ // except according to those terms. #![deny(const_err)] -//~^ NOTE lint level defined here use std::{isize, i8, i16, i32, i64}; use std::thread; fn main() { assert!(thread::spawn(move|| { isize::MIN / -1; }).join().is_err()); - //~^ NOTE attempt to divide with overflow - //~| NOTE attempted to do overflowing math - //~| ERROR constant evaluation error + //~^ ERROR attempt to divide with overflow //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i8::MIN / -1; }).join().is_err()); - //~^ NOTE attempt to divide with overflow - //~| NOTE attempted to do overflowing math - //~| ERROR constant evaluation error + //~^ ERROR attempt to divide with overflow //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i16::MIN / -1; }).join().is_err()); - //~^ NOTE attempt to divide with overflow - //~| NOTE attempted to do overflowing math - //~| ERROR constant evaluation error + //~^ ERROR attempt to divide with overflow //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i32::MIN / -1; }).join().is_err()); - //~^ NOTE attempt to divide with overflow - //~| NOTE attempted to do overflowing math - //~| ERROR constant evaluation error + //~^ ERROR attempt to divide with overflow //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i64::MIN / -1; }).join().is_err()); - //~^ NOTE attempt to divide with overflow - //~| NOTE attempted to do overflowing math - //~| ERROR constant evaluation error + //~^ ERROR attempt to divide with overflow //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1isize / 0; }).join().is_err()); - //~^ NOTE attempt to divide by zero - //~| NOTE attempted to do overflowing math - //~| ERROR constant evaluation error + //~^ ERROR attempt to divide by zero //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i8 / 0; }).join().is_err()); - //~^ NOTE attempt to divide by zero - //~| NOTE attempted to do overflowing math - //~| ERROR constant evaluation error + //~^ ERROR attempt to divide by zero //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i16 / 0; }).join().is_err()); - //~^ NOTE attempt to divide by zero - //~| NOTE attempted to do overflowing math - //~| ERROR constant evaluation error + //~^ ERROR attempt to divide by zero //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i32 / 0; }).join().is_err()); - //~^ NOTE attempt to divide by zero - //~| NOTE attempted to do overflowing math - //~| ERROR constant evaluation error + //~^ ERROR attempt to divide by zero //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i64 / 0; }).join().is_err()); - //~^ NOTE attempt to divide by zero - //~| NOTE attempted to do overflowing math - //~| ERROR constant evaluation error + //~^ ERROR attempt to divide by zero //~| ERROR constant evaluation error assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err()); - //~^ NOTE attempt to calculate the remainder with overflow - //~| NOTE attempted to do overflowing math - //~| ERROR constant evaluation error + //~^ ERROR attempt to calculate the remainder with overflow //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err()); - //~^ NOTE attempt to calculate the remainder with overflow - //~| NOTE attempted to do overflowing math - //~| ERROR constant evaluation error + //~^ ERROR attempt to calculate the remainder with overflow //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err()); - //~^ NOTE attempt to calculate the remainder with overflow - //~| NOTE attempted to do overflowing math - //~| ERROR constant evaluation error + //~^ ERROR attempt to calculate the remainder with overflow //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err()); - //~^ NOTE attempt to calculate the remainder with overflow - //~| NOTE attempted to do overflowing math - //~| ERROR constant evaluation error + //~^ ERROR attempt to calculate the remainder with overflow //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err()); - //~^ NOTE attempt to calculate the remainder with overflow - //~| NOTE attempted to do overflowing math - //~| ERROR constant evaluation error + //~^ ERROR attempt to calculate the remainder with overflow //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1isize % 0; }).join().is_err()); - //~^ NOTE attempt to calculate the remainder with a divisor of zero - //~| NOTE attempted to do overflowing math - //~| ERROR constant evaluation error + //~^ ERROR attempt to calculate the remainder with a divisor of zero //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i8 % 0; }).join().is_err()); - //~^ NOTE attempt to calculate the remainder with a divisor of zero - //~| NOTE attempted to do overflowing math - //~| ERROR constant evaluation error + //~^ ERROR attempt to calculate the remainder with a divisor of zero //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i16 % 0; }).join().is_err()); - //~^ NOTE attempt to calculate the remainder with a divisor of zero - //~| NOTE attempted to do overflowing math - //~| ERROR constant evaluation error + //~^ ERROR attempt to calculate the remainder with a divisor of zero //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i32 % 0; }).join().is_err()); - //~^ NOTE attempt to calculate the remainder with a divisor of zero - //~| NOTE attempted to do overflowing math - //~| ERROR constant evaluation error + //~^ ERROR attempt to calculate the remainder with a divisor of zero //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i64 % 0; }).join().is_err()); - //~^ NOTE attempt to calculate the remainder with a divisor of zero - //~| NOTE attempted to do overflowing math - //~| ERROR constant evaluation error + //~^ ERROR attempt to calculate the remainder with a divisor of zero //~| ERROR constant evaluation error } diff --git a/src/test/compile-fail/lint-exceeding-bitshifts.rs b/src/test/compile-fail/lint-exceeding-bitshifts.rs index 34b43c5badb51..5ebfcc4926baa 100644 --- a/src/test/compile-fail/lint-exceeding-bitshifts.rs +++ b/src/test/compile-fail/lint-exceeding-bitshifts.rs @@ -8,50 +8,50 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![deny(exceeding_bitshifts)] +#![deny(exceeding_bitshifts, const_err)] #![allow(unused_variables)] -#![allow(dead_code, const_err)] +#![allow(dead_code)] fn main() { let n = 1u8 << 7; - let n = 1u8 << 8; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u8 << 8; //~ ERROR: attempt to shift left with overflow let n = 1u16 << 15; - let n = 1u16 << 16; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u16 << 16; //~ ERROR: attempt to shift left with overflow let n = 1u32 << 31; - let n = 1u32 << 32; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u32 << 32; //~ ERROR: attempt to shift left with overflow let n = 1u64 << 63; - let n = 1u64 << 64; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u64 << 64; //~ ERROR: attempt to shift left with overflow let n = 1i8 << 7; - let n = 1i8 << 8; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i8 << 8; //~ ERROR: attempt to shift left with overflow let n = 1i16 << 15; - let n = 1i16 << 16; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i16 << 16; //~ ERROR: attempt to shift left with overflow let n = 1i32 << 31; - let n = 1i32 << 32; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i32 << 32; //~ ERROR: attempt to shift left with overflow let n = 1i64 << 63; - let n = 1i64 << 64; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i64 << 64; //~ ERROR: attempt to shift left with overflow let n = 1u8 >> 7; - let n = 1u8 >> 8; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u8 >> 8; //~ ERROR: attempt to shift right with overflow let n = 1u16 >> 15; - let n = 1u16 >> 16; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u16 >> 16; //~ ERROR: attempt to shift right with overflow let n = 1u32 >> 31; - let n = 1u32 >> 32; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u32 >> 32; //~ ERROR: attempt to shift right with overflow let n = 1u64 >> 63; - let n = 1u64 >> 64; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u64 >> 64; //~ ERROR: attempt to shift right with overflow let n = 1i8 >> 7; - let n = 1i8 >> 8; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i8 >> 8; //~ ERROR: attempt to shift right with overflow let n = 1i16 >> 15; - let n = 1i16 >> 16; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i16 >> 16; //~ ERROR: attempt to shift right with overflow let n = 1i32 >> 31; - let n = 1i32 >> 32; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i32 >> 32; //~ ERROR: attempt to shift right with overflow let n = 1i64 >> 63; - let n = 1i64 >> 64; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i64 >> 64; //~ ERROR: attempt to shift right with overflow let n = 1u8; let n = n << 7; - let n = n << 8; //~ ERROR: bitshift exceeds the type's number of bits + let n = n << 8; //~ ERROR: attempt to shift left with overflow - let n = 1u8 << -8; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u8 << -8; //~ ERROR: attempt to shift left with overflow let n = 1i8<<(1isize+-1); } diff --git a/src/test/compile-fail/lint-exceeding-bitshifts2.rs b/src/test/compile-fail/lint-exceeding-bitshifts2.rs index a8027034fbee8..3ba300eb7c4ab 100644 --- a/src/test/compile-fail/lint-exceeding-bitshifts2.rs +++ b/src/test/compile-fail/lint-exceeding-bitshifts2.rs @@ -14,7 +14,7 @@ fn main() { let n = 1u8 << (4+3); - let n = 1u8 << (4+4); //~ ERROR: bitshift exceeds + let n = 1u8 << (4+4); //~ ERROR: attempt to shift left with overflow let n = 1i64 >> [63][0]; let n = 1i64 >> [64][0]; // should be linting, needs to wait for const propagation @@ -22,6 +22,6 @@ fn main() { const BITS: usize = 32; #[cfg(target_pointer_width = "64")] const BITS: usize = 64; - let n = 1_isize << BITS; //~ ERROR: bitshift exceeds - let n = 1_usize << BITS; //~ ERROR: bitshift exceeds + let n = 1_isize << BITS; //~ ERROR: attempt to shift left with overflow + let n = 1_usize << BITS; //~ ERROR: attempt to shift left with overflow } diff --git a/src/test/ui/const-eval-overflow-4.rs b/src/test/ui/const-eval-overflow-4.rs index a1b90f623cdae..24e178152eef0 100644 --- a/src/test/ui/const-eval-overflow-4.rs +++ b/src/test/ui/const-eval-overflow-4.rs @@ -22,6 +22,7 @@ use std::{u8, u16, u32, u64, usize}; const A_I8_T : [u32; (i8::MAX as i8 + 1i8) as usize] //~^ ERROR E0080 + //~| WARN attempt to add with overflow = [0; (i8::MAX as usize) + 1]; fn main() { diff --git a/src/test/ui/const-eval-overflow-4.stderr b/src/test/ui/const-eval-overflow-4.stderr index e967f024ae3d7..14753038fef04 100644 --- a/src/test/ui/const-eval-overflow-4.stderr +++ b/src/test/ui/const-eval-overflow-4.stderr @@ -1,3 +1,11 @@ +warning: attempt to add with overflow + --> $DIR/const-eval-overflow-4.rs:23:13 + | +LL | : [u32; (i8::MAX as i8 + 1i8) as usize] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: #[warn(const_err)] on by default + error[E0080]: constant evaluation error --> $DIR/const-eval-overflow-4.rs:23:13 | diff --git a/src/test/ui/const-eval/conditional_array_execution.rs b/src/test/ui/const-eval/conditional_array_execution.rs index 41ceb609df5bd..324bcf60e8ff6 100644 --- a/src/test/ui/const-eval/conditional_array_execution.rs +++ b/src/test/ui/const-eval/conditional_array_execution.rs @@ -10,7 +10,8 @@ const X: u32 = 5; const Y: u32 = 6; -const FOO: u32 = [X - Y, Y - X][(X < Y) as usize]; //~ E0080 +const FOO: u32 = [X - Y, Y - X][(X < Y) as usize]; +//~^ WARN attempt to subtract with overflow fn main() { println!("{}", FOO); //~ E0080 diff --git a/src/test/ui/const-eval/conditional_array_execution.stderr b/src/test/ui/const-eval/conditional_array_execution.stderr index 8d421f401caa5..4cf12e222831c 100644 --- a/src/test/ui/const-eval/conditional_array_execution.stderr +++ b/src/test/ui/const-eval/conditional_array_execution.stderr @@ -1,15 +1,17 @@ -error[E0080]: constant evaluation error - --> $DIR/conditional_array_execution.rs:16:20 +warning: attempt to subtract with overflow + --> $DIR/conditional_array_execution.rs:13:19 | -LL | println!("{}", FOO); //~ E0080 - | ^^^ referenced constant has errors +LL | const FOO: u32 = [X - Y, Y - X][(X < Y) as usize]; + | ^^^^^ + | + = note: #[warn(const_err)] on by default error[E0080]: constant evaluation error - --> $DIR/conditional_array_execution.rs:13:19 + --> $DIR/conditional_array_execution.rs:17:20 | -LL | const FOO: u32 = [X - Y, Y - X][(X < Y) as usize]; //~ E0080 - | ^^^^^ attempt to subtract with overflow +LL | println!("{}", FOO); //~ E0080 + | ^^^ referenced constant has errors -error: aborting due to 2 previous errors +error: aborting due to previous error If you want more information on this error, try using "rustc --explain E0080" diff --git a/src/test/ui/const-eval/issue-43197.rs b/src/test/ui/const-eval/issue-43197.rs index e87b2caa49f90..d5c4796d0b497 100644 --- a/src/test/ui/const-eval/issue-43197.rs +++ b/src/test/ui/const-eval/issue-43197.rs @@ -15,8 +15,10 @@ const fn foo(x: u32) -> u32 { } fn main() { - const X: u32 = 0-1; //~ ERROR constant evaluation error - const Y: u32 = foo(0-1); //~ ERROR constant evaluation error + const X: u32 = 0-1; + //~^ WARN attempt to subtract with overflow + const Y: u32 = foo(0-1); + //~^ WARN attempt to subtract with overflow println!("{} {}", X, Y); //~^ ERROR constant evaluation error //~| ERROR constant evaluation error diff --git a/src/test/ui/const-eval/issue-43197.stderr b/src/test/ui/const-eval/issue-43197.stderr index 21b9a282c485e..3f10826874754 100644 --- a/src/test/ui/const-eval/issue-43197.stderr +++ b/src/test/ui/const-eval/issue-43197.stderr @@ -1,27 +1,29 @@ -error[E0080]: constant evaluation error - --> $DIR/issue-43197.rs:20:23 +warning: attempt to subtract with overflow + --> $DIR/issue-43197.rs:18:20 | -LL | println!("{} {}", X, Y); - | ^ referenced constant has errors +LL | const X: u32 = 0-1; + | ^^^ + | + = note: #[warn(const_err)] on by default error[E0080]: constant evaluation error - --> $DIR/issue-43197.rs:20:26 + --> $DIR/issue-43197.rs:22:23 | LL | println!("{} {}", X, Y); - | ^ referenced constant has errors + | ^ referenced constant has errors -error[E0080]: constant evaluation error - --> $DIR/issue-43197.rs:19:24 +warning: attempt to subtract with overflow + --> $DIR/issue-43197.rs:20:24 | -LL | const Y: u32 = foo(0-1); //~ ERROR constant evaluation error - | ^^^ attempt to subtract with overflow +LL | const Y: u32 = foo(0-1); + | ^^^ error[E0080]: constant evaluation error - --> $DIR/issue-43197.rs:18:20 + --> $DIR/issue-43197.rs:22:26 | -LL | const X: u32 = 0-1; //~ ERROR constant evaluation error - | ^^^ attempt to subtract with overflow +LL | println!("{} {}", X, Y); + | ^ referenced constant has errors -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors If you want more information on this error, try using "rustc --explain E0080" diff --git a/src/test/ui/const-eval/pub_const_err.rs b/src/test/ui/const-eval/pub_const_err.rs new file mode 100644 index 0000000000000..bdb9f5b19a885 --- /dev/null +++ b/src/test/ui/const-eval/pub_const_err.rs @@ -0,0 +1,16 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![deny(const_err)] + +#![crate_type = "lib"] + +pub const Z: u32 = 0 - 1; +//~^ ERROR attempt to subtract with overflow diff --git a/src/test/ui/const-eval/pub_const_err.stderr b/src/test/ui/const-eval/pub_const_err.stderr new file mode 100644 index 0000000000000..b77ec38ca1679 --- /dev/null +++ b/src/test/ui/const-eval/pub_const_err.stderr @@ -0,0 +1,14 @@ +error: attempt to subtract with overflow + --> $DIR/pub_const_err.rs:15:20 + | +LL | pub const Z: u32 = 0 - 1; + | ^^^^^ + | +note: lint level defined here + --> $DIR/pub_const_err.rs:11:9 + | +LL | #![deny(const_err)] + | ^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/const-len-underflow-separate-spans.rs b/src/test/ui/const-len-underflow-separate-spans.rs index 35e3ba9ce0945..ee07dabab1fc8 100644 --- a/src/test/ui/const-len-underflow-separate-spans.rs +++ b/src/test/ui/const-len-underflow-separate-spans.rs @@ -16,6 +16,7 @@ const ONE: usize = 1; const TWO: usize = 2; const LEN: usize = ONE - TWO; //~^ ERROR E0080 +//~| WARN attempt to subtract with overflow fn main() { let a: [i8; LEN] = unimplemented!(); diff --git a/src/test/ui/const-len-underflow-separate-spans.stderr b/src/test/ui/const-len-underflow-separate-spans.stderr index 301b063885c19..98f4ac9e83fb8 100644 --- a/src/test/ui/const-len-underflow-separate-spans.stderr +++ b/src/test/ui/const-len-underflow-separate-spans.stderr @@ -1,3 +1,11 @@ +warning: attempt to subtract with overflow + --> $DIR/const-len-underflow-separate-spans.rs:17:20 + | +LL | const LEN: usize = ONE - TWO; + | ^^^^^^^^^ + | + = note: #[warn(const_err)] on by default + error[E0080]: constant evaluation error --> $DIR/const-len-underflow-separate-spans.rs:17:20 | @@ -5,7 +13,7 @@ LL | const LEN: usize = ONE - TWO; | ^^^^^^^^^ attempt to subtract with overflow error[E0080]: constant evaluation error - --> $DIR/const-len-underflow-separate-spans.rs:21:17 + --> $DIR/const-len-underflow-separate-spans.rs:22:17 | LL | let a: [i8; LEN] = unimplemented!(); | ^^^ referenced constant has errors diff --git a/src/test/ui/error-codes/E0080.rs b/src/test/ui/error-codes/E0080.rs index cca63f23b7776..c8e425711284e 100644 --- a/src/test/ui/error-codes/E0080.rs +++ b/src/test/ui/error-codes/E0080.rs @@ -10,7 +10,6 @@ enum Enum { X = (1 << 500), //~ ERROR E0080 - //~| ERROR bitshift exceeds //~| shift left with overflow Y = (1 / 0) //~ ERROR E0080 //~| const_err diff --git a/src/test/ui/error-codes/E0080.stderr b/src/test/ui/error-codes/E0080.stderr index 501051b9fe617..6db53acd6b982 100644 --- a/src/test/ui/error-codes/E0080.stderr +++ b/src/test/ui/error-codes/E0080.stderr @@ -1,4 +1,4 @@ -error: bitshift exceeds the type's number of bits +error: attempt to shift left with overflow --> $DIR/E0080.rs:12:9 | LL | X = (1 << 500), //~ ERROR E0080 @@ -12,22 +12,22 @@ error[E0080]: constant evaluation error LL | X = (1 << 500), //~ ERROR E0080 | ^^^^^^^^^^ attempt to shift left with overflow -warning: constant evaluation error - --> $DIR/E0080.rs:15:9 +warning: attempt to divide by zero + --> $DIR/E0080.rs:14:9 | LL | Y = (1 / 0) //~ ERROR E0080 - | ^^^^^^^ attempt to divide by zero + | ^^^^^^^ | = note: #[warn(const_err)] on by default warning: constant evaluation error - --> $DIR/E0080.rs:15:9 + --> $DIR/E0080.rs:14:9 | LL | Y = (1 / 0) //~ ERROR E0080 | ^^^^^^^ attempted to do overflowing math error[E0080]: constant evaluation error - --> $DIR/E0080.rs:15:9 + --> $DIR/E0080.rs:14:9 | LL | Y = (1 / 0) //~ ERROR E0080 | ^^^^^^^ attempt to divide by zero From c0fc6a8b6b5bba6eed003fdc08e3af1967ab349d Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 6 Mar 2018 14:07:52 +0100 Subject: [PATCH 108/110] simplify constant value pair field access --- src/librustc_mir/transform/const_prop.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index c8e1a93cdda2c..f133d6e9c6dee 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -183,12 +183,7 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { trace!("layout computed"); use rustc_data_structures::indexed_vec::Idx; let field_index = field.index(); - let val = if field_index == 0 { - a - } else { - assert_eq!(field_index, 1); - b - }; + let val = [a, b][field_index]; let field = base_layout.field(&*self, field_index).ok()?; trace!("projection resulted in: {:?}", val); Some((Value::ByVal(val), field.ty, span)) From 47e0bb59dbfe1010ed19bfd49c21fa7da4ab4f42 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 7 Mar 2018 08:17:03 +0100 Subject: [PATCH 109/110] Clean up the binary hex lint --- src/librustc_lint/types.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index be7f1152ea4a0..266f322e39777 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -21,7 +21,7 @@ use lint::{LintPass, LateLintPass}; use std::cmp; use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64}; -use syntax::ast; +use syntax::{ast, attr}; use syntax::abi::Abi; use syntax_pos::Span; use syntax::codemap; @@ -365,12 +365,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { ) { let (t, actually) = match ty { ty::TyInt(t) => { - let bits = int_ty_bits(t, cx.sess().target.isize_ty); + let ity = attr::IntType::SignedInt(t); + let bits = layout::Integer::from_attr(cx.tcx, ity).size().bits(); let actually = (val << (128 - bits)) as i128 >> (128 - bits); (format!("{:?}", t), actually.to_string()) } ty::TyUint(t) => { - let bits = uint_ty_bits(t, cx.sess().target.usize_ty); + let ity = attr::IntType::UnsignedInt(t); + let bits = layout::Integer::from_attr(cx.tcx, ity).size().bits(); let actually = (val << (128 - bits)) >> (128 - bits); (format!("{:?}", t), actually.to_string()) } From 52dec0e1c9e0fe5bbbe81385531e69c2c46ef56e Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 7 Mar 2018 12:56:48 +0100 Subject: [PATCH 110/110] Don't derive traits on packed structs --- src/libsyntax_pos/span_encoding.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/libsyntax_pos/span_encoding.rs b/src/libsyntax_pos/span_encoding.rs index b23e40ce7a932..bf9a832519ad9 100644 --- a/src/libsyntax_pos/span_encoding.rs +++ b/src/libsyntax_pos/span_encoding.rs @@ -19,16 +19,37 @@ use hygiene::SyntaxContext; use rustc_data_structures::fx::FxHashMap; use std::cell::RefCell; +use std::hash::{Hash, Hasher}; /// A compressed span. /// Contains either fields of `SpanData` inline if they are small, or index into span interner. /// The primary goal of `Span` is to be as small as possible and fit into other structures /// (that's why it uses `packed` as well). Decoding speed is the second priority. /// See `SpanData` for the info on span fields in decoded representation. -#[derive(Clone, Copy, PartialEq, Eq, Hash)] #[repr(packed)] pub struct Span(u32); +impl Copy for Span {} +impl Clone for Span { + fn clone(&self) -> Span { + *self + } +} +impl PartialEq for Span { + fn eq(&self, other: &Span) -> bool { + let a = self.0; + let b = other.0; + a == b + } +} +impl Eq for Span {} +impl Hash for Span { + fn hash(&self, state: &mut H) { + let a = self.0; + a.hash(state) + } +} + /// Dummy span, both position and length are zero, syntax context is zero as well. /// This span is kept inline and encoded with format 0. pub const DUMMY_SP: Span = Span(0);