diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index dd99305809c..35c51c0c206 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 6386de5952f..977e617968a 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 8b4f56e1dba..394b3d96a4f 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 d8994351418..9e9ba516203 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 ce23cb23496..2e554aff13e 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 2eeef70a14e..5c082b0610f 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 8ac69c4b528..e0e85600b90 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 ebd78467c3b..bf75afcfa1a 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 f41bdf61d9b..0ded759fec7 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 02bf409f33f..f9071cff78f 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 6ae0d520e54..cdf619a1dfa 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 e71bef512cf..0ec5700f5f3 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 d991fb0f67b..14984e8a6d7 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 04ac32af7cc..71be3f28759 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 1ff0ffaaa68..028fc337967 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 a9508defdcd..9827dd58cd6 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 3621d80df1d..9fbf5539698 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 370b8681ba6..25a9e522367 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 a70ee2d1d64..4ecf0d6e638 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 533bad18c38..a150335a1ae 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 dc8bbdc60e0..db44d870acb 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 6fda4703d1c..24bf9445d45 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 b476ea56852..7d7e6ec9451 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 48f123b1f57..2e2246f9ab9 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_with_mir<'a, 'mir, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + cid: GlobalId<'tcx>, + mir: &'mir mir::Mir<'tcx>, + param_env: ty::ParamEnv<'tcx>, +) -> Option<(Value, Pointer, Ty<'tcx>)> { + let (res, ecx) = eval_body_and_ecx(tcx, cid, Some(mir), param_env); + match res { + Ok(val) => Some(val), + Err(mut err) => { + ecx.report(&mut err, true); + None + } + } +} + pub fn eval_body<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, cid: GlobalId<'tcx>, param_env: ty::ParamEnv<'tcx>, -) -> EvalResult<'tcx, (Value, Pointer, Ty<'tcx>)> { - eval_body_and_ecx(tcx, cid, param_env).0 -} - -pub fn check_body<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - cid: GlobalId<'tcx>, - param_env: ty::ParamEnv<'tcx>, -) { - let (res, ecx) = eval_body_and_ecx(tcx, cid, param_env); - if let Err(mut err) = res { - ecx.report(&mut err); +) -> 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(), - )?; + 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()? {} - } - let alloc = tcx.interpret_interner.borrow().get_cached(cid).expect("global not cached"); + 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 63939f7e038..7d2b83e9217 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 c2989dbaaf1..d5e57d3317c 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 7cc4ba84895..5ee84e0e02c 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)?; - } + // The machine handled it + if M::mark_static_initialized(self, alloc_id, mutability)? { + return Ok(()) + } + 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) => {}, + } + 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)?; } - return Ok(()); } - // We are marking the static as initialized, so move it out of the uninit map - if let Some(uninit) = self.uninitialized_statics.remove(&alloc_id) { - self.alloc_map.insert(alloc_id, uninit); - } - // do not use `self.get_mut(alloc_id)` here, because we might have already marked a - // sub-element or have circular pointers (e.g. `Rc`-cycles) - let relocations = match self.alloc_map.get_mut(&alloc_id) { - Some(&mut Allocation { - ref mut relocations, - .. - }) => { - match self.alloc_kind.get(&alloc_id) { - // const eval results can refer to "locals". - // E.g. `const Foo: &u32 = &1;` refers to the temp local that stores the `1` - None | - Some(&MemoryKind::Stack) => {}, - Some(&MemoryKind::Machine(m)) => M::mark_static_initialized(m)?, - Some(&MemoryKind::MutableStatic) => { - trace!("mark_static_initalized: skipping already initialized static referred to by static currently being initialized"); - return Ok(()); - }, - } - // overwrite or insert - self.alloc_kind.insert(alloc_id, MemoryKind::MutableStatic); - // take out the relocations vector to free the borrow on self, so we can call - // mark recursively - mem::replace(relocations, Default::default()) - } - None => return err!(DanglingPointerDeref), - }; - // recurse into inner allocations - for &alloc in relocations.values() { - self.mark_inner_allocation_initialized(alloc, mutability)?; - } - // put back the relocations - self.alloc_map - .get_mut(&alloc_id) - .expect("checked above") - .relocations = relocations; Ok(()) } @@ -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 ba894a1728a..f23ba90fd4c 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 b20540b00ce..bad744194d5 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 4a2b4547cb0..349ac630559 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 2b0f9041d51..21e81ff668e 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 c5942712b87..fbc0c499e59 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 c18cf6d9f96..b1b5f77a2e6 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 22417201f0d..4dc0879c85d 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 0f512569adf..0a8fd022dd1 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 0bef9f0602d..86d08dec2b9 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 8856d263864..e27e7c72473 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 0ce3f729305..122b51dbbb7 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 239300c1ecf..6d2f9c6c97f 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 66173cfef92..5050628b024 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))?; - if let layout::Abi::Uninhabited = l.abi { - return Const::new(C_undef(l.llvm_type(cx)), t); - } + let alloc_id = ccx + .tcx() + .interpret_interner + .borrow() + .get_cached(def_id) + .expect("global not cached"); - 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 alloc = ccx + .tcx() + .interpret_interner + .borrow() + .get_alloc(alloc_id) + .expect("miri allocation never successfully created"); + Ok(global_initializer(ccx, alloc)) +} - 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)) } - } - _ => {} + mir::Literal::Value { value } => { + Ok(self.monomorphize(&value)) + } + }.and_then(|c| self.const_to_miri_value(bcx, c)) } - // 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; + // 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) + }) } - - 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; - } - - // 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 99e3a59e2c4..a1044ac87e4 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 e1b906646aa..01cf324124c 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 91c1097fc7f..06d94e8d155 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 1ad26b4cda5..f5303979f9c 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 0271ad269e1..bb41acc5410 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 d1e6f27069c..b9e4ce661d0 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 18476494300..02264228a6b 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 42fb40394fb..8c2e6233abf 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 d4f9c0fe56d..668f95f2c8d 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 e65194ab56f..8bd759b6d37 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 9889ca1392a..a0648993aac 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 00000000000..636537d1df2 --- /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 058a8d0a1bd..00000000000 --- 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 00000000000..61a653589ff --- /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 00000000000..b2e84486101 --- /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 00000000000..c4a8beaf0f0 --- /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 29bc665a22e..3065122af6a 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 7f2229b5a65..85cc893aa13 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 b1b4bfe2d1c..179ea9e853f 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 b42c440f87d..7596881ef9b 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 86cc2c144ac..49f76c532df 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 1f07422e21b..00000000000 --- 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 ca8d5a1f704..be76796c5c4 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 823ece2bdca..ac85e43cf4f 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 a6ae21c3b54..9efe3d90a93 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 2a97945f266..9191dfa155c 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 d8ab48d1ec3..1d59e75a0f0 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 e94d1ff7793..2634157c806 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 00000000000..d95e1b370aa --- /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 f7cf8a68d56..9178d8d2f6e 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 737f80372de..fe88ab4e117 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 fd888b659f1..ca9af78dd99 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 4f92770df28..2e60634d15e 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 00000000000..80ab12482cb --- /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 86c5e873df8..e87b2caa49f 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 7582d0efa81..35e3ba9ce09 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 ef3bded4bef..896dd2599bf 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 2f199c48e46..6e525d4ea9e 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() {