diff --git a/src/Cargo.lock b/src/Cargo.lock index b70a01ebb3c..34c077a6f7a 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -1858,20 +1858,6 @@ dependencies = [ "syntax_pos 0.0.0", ] -[[package]] -name = "rustc_const_eval" -version = "0.0.0" -dependencies = [ - "arena 0.0.0", - "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc 0.0.0", - "rustc_const_math 0.0.0", - "rustc_data_structures 0.0.0", - "rustc_errors 0.0.0", - "syntax 0.0.0", - "syntax_pos 0.0.0", -] - [[package]] name = "rustc_const_math" version = "0.0.0" @@ -1914,7 +1900,6 @@ dependencies = [ "rustc_allocator 0.0.0", "rustc_back 0.0.0", "rustc_borrowck 0.0.0", - "rustc_const_eval 0.0.0", "rustc_data_structures 0.0.0", "rustc_errors 0.0.0", "rustc_incremental 0.0.0", @@ -1964,7 +1949,7 @@ version = "0.0.0" dependencies = [ "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", - "rustc_const_eval 0.0.0", + "rustc_mir 0.0.0", "syntax 0.0.0", "syntax_pos 0.0.0", ] @@ -2012,6 +1997,7 @@ dependencies = [ name = "rustc_mir" version = "0.0.0" dependencies = [ + "arena 0.0.0", "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "graphviz 0.0.0", @@ -2020,7 +2006,6 @@ dependencies = [ "rustc 0.0.0", "rustc_apfloat 0.0.0", "rustc_back 0.0.0", - "rustc_const_eval 0.0.0", "rustc_const_math 0.0.0", "rustc_data_structures 0.0.0", "rustc_errors 0.0.0", @@ -2046,10 +2031,10 @@ version = "0.0.0" dependencies = [ "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", - "rustc_const_eval 0.0.0", "rustc_const_math 0.0.0", "rustc_data_structures 0.0.0", "rustc_errors 0.0.0", + "rustc_mir 0.0.0", "syntax 0.0.0", "syntax_pos 0.0.0", ] diff --git a/src/doc/rustc-ux-guidelines.md b/src/doc/rustc-ux-guidelines.md index 323d49e4691..b62762ef69e 100644 --- a/src/doc/rustc-ux-guidelines.md +++ b/src/doc/rustc-ux-guidelines.md @@ -64,7 +64,6 @@ for details on how to format and write long error codes. [librustc](https://github.com/rust-lang/rust/blob/master/src/librustc/diagnostics.rs), [libsyntax](https://github.com/rust-lang/rust/blob/master/src/libsyntax/diagnostics.rs), [librustc_borrowck](https://github.com/rust-lang/rust/blob/master/src/librustc_borrowck/diagnostics.rs), - [librustc_const_eval](https://github.com/rust-lang/rust/blob/master/src/librustc_const_eval/diagnostics.rs), [librustc_metadata](https://github.com/rust-lang/rust/blob/master/src/librustc_metadata/diagnostics.rs), [librustc_mir](https://github.com/rust-lang/rust/blob/master/src/librustc_mir/diagnostics.rs), [librustc_passes](https://github.com/rust-lang/rust/blob/master/src/librustc_passes/diagnostics.rs), diff --git a/src/doc/unstable-book/src/language-features/const-indexing.md b/src/doc/unstable-book/src/language-features/const-indexing.md deleted file mode 100644 index 42d46ce15f6..00000000000 --- a/src/doc/unstable-book/src/language-features/const-indexing.md +++ /dev/null @@ -1,19 +0,0 @@ -# `const_indexing` - -The tracking issue for this feature is: [#29947] - -[#29947]: https://github.com/rust-lang/rust/issues/29947 - ------------------------- - -The `const_indexing` feature allows the constant evaluation of index operations -on constant arrays and repeat expressions. - -## Examples - -```rust -#![feature(const_indexing)] - -const ARR: [usize; 5] = [1, 2, 3, 4, 5]; -const ARR2: [usize; ARR[1]] = [42, 99]; -``` \ No newline at end of file diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs index b9847044982..6602643dc51 100644 --- a/src/libcore/cmp.rs +++ b/src/libcore/cmp.rs @@ -427,6 +427,7 @@ impl Ord for Reverse { /// } /// } /// ``` +#[cfg_attr(not(stage0), lang = "ord")] #[stable(feature = "rust1", since = "1.0.0")] pub trait Ord: Eq + PartialOrd { /// This method returns an `Ordering` between `self` and `other`. @@ -596,7 +597,8 @@ impl PartialOrd for Ordering { /// assert_eq!(x < y, true); /// assert_eq!(x.lt(&y), true); /// ``` -#[lang = "ord"] +#[cfg_attr(stage0, lang = "ord")] +#[cfg_attr(not(stage0), lang = "partial_ord")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented = "can't compare `{Self}` with `{Rhs}`"] pub trait PartialOrd: PartialEq { diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index ed46296389d..7d8709a82f4 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -60,15 +60,15 @@ //! user of the `DepNode` API of having to know how to compute the expected //! fingerprint for a given set of node parameters. +use mir::interpret::{GlobalId}; use hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX}; use hir::map::DefPathHash; use hir::{HirId, ItemLocalId}; -use ich::Fingerprint; +use ich::{Fingerprint, StableHashingContext}; +use rustc_data_structures::stable_hasher::{StableHasher, HashStable}; use ty::{TyCtxt, Instance, InstanceDef, ParamEnv, ParamEnvAnd, PolyTraitRef, Ty}; use ty::subst::Substs; -use rustc_data_structures::stable_hasher::{StableHasher, HashStable}; -use ich::StableHashingContext; use std::fmt; use std::hash::Hash; use syntax_pos::symbol::InternedString; @@ -518,7 +518,7 @@ define_dep_nodes!( <'tcx> [] TypeckTables(DefId), [] UsedTraitImports(DefId), [] HasTypeckTables(DefId), - [] ConstEval { param_env: ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)> }, + [] ConstEval { param_env: ParamEnvAnd<'tcx, GlobalId<'tcx>> }, [] CheckMatch(DefId), [] SymbolName(DefId), [] InstanceSymbolName { instance: Instance<'tcx> }, @@ -661,7 +661,7 @@ trait DepNodeParams<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> : fmt::Debug { } impl<'a, 'gcx: 'tcx + 'a, 'tcx: 'a, T> DepNodeParams<'a, 'gcx, 'tcx> for T - where T: HashStable> + fmt::Debug + where T: HashStable> + fmt::Debug { default const CAN_RECONSTRUCT_QUERY_KEY: bool = false; diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 287516474d4..b3a904f2f5f 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -14,19 +14,6 @@ // Each message should start and end with a new line, and be wrapped to 80 characters. // In vim you can `:set tw=80` and use `gq` to wrap paragraphs. Use `:set tw=0` to disable. register_long_diagnostics! { -E0020: r##" -This error indicates that an attempt was made to divide by zero (or take the -remainder of a zero divisor) in a static or constant expression. Erroneous -code example: - -```compile_fail -#[deny(const_err)] - -const X: i32 = 42 / 0; -// error: attempt to divide by zero in a constant expression -``` -"##, - E0038: r##" Trait objects like `Box` can only be constructed when certain requirements are satisfied by the trait in question. diff --git a/src/librustc/hir/def_id.rs b/src/librustc/hir/def_id.rs index 637b156ceef..34b3aa53d6b 100644 --- a/src/librustc/hir/def_id.rs +++ b/src/librustc/hir/def_id.rs @@ -220,7 +220,6 @@ impl serialize::UseSpecializedDecodable for DefId {} pub struct LocalDefId(DefIndex); impl LocalDefId { - #[inline] pub fn from_def_id(def_id: DefId) -> LocalDefId { assert!(def_id.is_local()); diff --git a/src/librustc/hir/map/collector.rs b/src/librustc/hir/map/collector.rs index 9bbda9d7447..3c523f5633e 100644 --- a/src/librustc/hir/map/collector.rs +++ b/src/librustc/hir/map/collector.rs @@ -529,7 +529,7 @@ struct HirItemLike { hash_bodies: bool, } -impl<'hir, T> HashStable> for HirItemLike +impl<'a, 'hir, T> HashStable> for HirItemLike where T: HashStable> { fn hash_stable(&self, diff --git a/src/librustc/ich/hcx.rs b/src/librustc/ich/hcx.rs index 4dcab6a04ee..6ae588b2a07 100644 --- a/src/librustc/ich/hcx.rs +++ b/src/librustc/ich/hcx.rs @@ -46,19 +46,19 @@ pub fn compute_ignored_attr_names() -> FxHashSet { /// a reference to the TyCtxt) and it holds a few caches for speeding up various /// things (e.g. each DefId/DefPath is only hashed once). #[derive(Clone)] -pub struct StableHashingContext<'gcx> { - sess: &'gcx Session, - definitions: &'gcx Definitions, - cstore: &'gcx dyn CrateStore, - body_resolver: BodyResolver<'gcx>, +pub struct StableHashingContext<'a> { + sess: &'a Session, + definitions: &'a Definitions, + cstore: &'a dyn CrateStore, + body_resolver: BodyResolver<'a>, hash_spans: bool, hash_bodies: bool, node_id_hashing_mode: NodeIdHashingMode, // Very often, we are hashing something that does not need the // CachingCodemapView, so we initialize it lazily. - raw_codemap: &'gcx CodeMap, - caching_codemap: Option>, + raw_codemap: &'a CodeMap, + caching_codemap: Option>, } #[derive(PartialEq, Eq, Clone, Copy)] @@ -81,14 +81,14 @@ impl<'gcx> BodyResolver<'gcx> { } } -impl<'gcx> StableHashingContext<'gcx> { +impl<'a> StableHashingContext<'a> { // The `krate` here is only used for mapping BodyIds to Bodies. // Don't use it for anything else or you'll run the risk of // leaking data out of the tracking system. - pub fn new(sess: &'gcx Session, - krate: &'gcx hir::Crate, - definitions: &'gcx Definitions, - cstore: &'gcx dyn CrateStore) + pub fn new(sess: &'a Session, + krate: &'a hir::Crate, + definitions: &'a Definitions, + cstore: &'a dyn CrateStore) -> Self { let hash_spans_initial = !sess.opts.debugging_opts.incremental_ignore_spans; @@ -106,7 +106,7 @@ impl<'gcx> StableHashingContext<'gcx> { } #[inline] - pub fn sess(&self) -> &'gcx Session { + pub fn sess(&self) -> &'a Session { self.sess } @@ -165,7 +165,7 @@ impl<'gcx> StableHashingContext<'gcx> { } #[inline] - pub fn codemap(&mut self) -> &mut CachingCodemapView<'gcx> { + pub fn codemap(&mut self) -> &mut CachingCodemapView<'a> { match self.caching_codemap { Some(ref mut cm) => { cm @@ -193,27 +193,27 @@ impl<'gcx> StableHashingContext<'gcx> { } impl<'a, 'gcx, 'lcx> StableHashingContextProvider for TyCtxt<'a, 'gcx, 'lcx> { - type ContextType = StableHashingContext<'gcx>; + type ContextType = StableHashingContext<'a>; fn create_stable_hashing_context(&self) -> Self::ContextType { (*self).create_stable_hashing_context() } } -impl<'gcx> StableHashingContextProvider for StableHashingContext<'gcx> { - type ContextType = StableHashingContext<'gcx>; +impl<'a> StableHashingContextProvider for StableHashingContext<'a> { + type ContextType = StableHashingContext<'a>; fn create_stable_hashing_context(&self) -> Self::ContextType { self.clone() } } -impl<'gcx> ::dep_graph::DepGraphSafe for StableHashingContext<'gcx> { +impl<'a> ::dep_graph::DepGraphSafe for StableHashingContext<'a> { } -impl<'gcx> HashStable> for hir::BodyId { +impl<'a> HashStable> for hir::BodyId { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { if hcx.hash_bodies() { hcx.body_resolver.body(*self).hash_stable(hcx, hasher); @@ -221,10 +221,10 @@ impl<'gcx> HashStable> for hir::BodyId { } } -impl<'gcx> HashStable> for hir::HirId { +impl<'a> HashStable> for hir::HirId { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { match hcx.node_id_hashing_mode { NodeIdHashingMode::Ignore => { @@ -243,21 +243,21 @@ impl<'gcx> HashStable> for hir::HirId { } } -impl<'gcx> ToStableHashKey> for hir::HirId { +impl<'a> ToStableHashKey> for hir::HirId { type KeyType = (DefPathHash, hir::ItemLocalId); #[inline] fn to_stable_hash_key(&self, - hcx: &StableHashingContext<'gcx>) + hcx: &StableHashingContext<'a>) -> (DefPathHash, hir::ItemLocalId) { let def_path_hash = hcx.local_def_path_hash(self.owner); (def_path_hash, self.local_id) } } -impl<'gcx> HashStable> for ast::NodeId { +impl<'a> HashStable> for ast::NodeId { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { match hcx.node_id_hashing_mode { NodeIdHashingMode::Ignore => { @@ -270,18 +270,18 @@ impl<'gcx> HashStable> for ast::NodeId { } } -impl<'gcx> ToStableHashKey> for ast::NodeId { +impl<'a> ToStableHashKey> for ast::NodeId { type KeyType = (DefPathHash, hir::ItemLocalId); #[inline] fn to_stable_hash_key(&self, - hcx: &StableHashingContext<'gcx>) + hcx: &StableHashingContext<'a>) -> (DefPathHash, hir::ItemLocalId) { hcx.definitions.node_to_hir_id(*self).to_stable_hash_key(hcx) } } -impl<'gcx> HashStable> for Span { +impl<'a> HashStable> for Span { // Hash a span in a stable way. We can't directly hash the span's BytePos // fields (that would be similar to hashing pointers, since those are just @@ -293,7 +293,7 @@ impl<'gcx> HashStable> for Span { // Also, hashing filenames is expensive so we avoid doing it twice when the // span starts and ends in the same file, which is almost always the case. fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { const TAG_VALID_SPAN: u8 = 0; const TAG_INVALID_SPAN: u8 = 1; @@ -373,8 +373,8 @@ impl<'gcx> HashStable> for Span { } } -pub fn hash_stable_trait_impls<'gcx, W, R>( - hcx: &mut StableHashingContext<'gcx>, +pub fn hash_stable_trait_impls<'a, 'gcx, W, R>( + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher, blanket_impls: &Vec, non_blanket_impls: &HashMap, R>) diff --git a/src/librustc/ich/impls_const_math.rs b/src/librustc/ich/impls_const_math.rs index 6790c2ac7de..5f3ff461c0c 100644 --- a/src/librustc/ich/impls_const_math.rs +++ b/src/librustc/ich/impls_const_math.rs @@ -16,33 +16,6 @@ impl_stable_hash_for!(struct ::rustc_const_math::ConstFloat { bits }); -impl_stable_hash_for!(enum ::rustc_const_math::ConstInt { - I8(val), - I16(val), - I32(val), - I64(val), - I128(val), - Isize(val), - U8(val), - U16(val), - U32(val), - U64(val), - U128(val), - Usize(val) -}); - -impl_stable_hash_for!(enum ::rustc_const_math::ConstIsize { - Is16(i16), - Is32(i32), - Is64(i64) -}); - -impl_stable_hash_for!(enum ::rustc_const_math::ConstUsize { - Us16(i16), - Us32(i32), - Us64(i64) -}); - impl_stable_hash_for!(enum ::rustc_const_math::ConstMathErr { NotInRange, CmpBetweenUnequalTypes, diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc/ich/impls_hir.rs index faad3f35631..c085b803085 100644 --- a/src/librustc/ich/impls_hir.rs +++ b/src/librustc/ich/impls_hir.rs @@ -21,46 +21,46 @@ use std::mem; use syntax::ast; use syntax::attr; -impl<'gcx> HashStable> for DefId { +impl<'a> HashStable> for DefId { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.def_path_hash(*self).hash_stable(hcx, hasher); } } -impl<'gcx> ToStableHashKey> for DefId { +impl<'a> ToStableHashKey> for DefId { type KeyType = DefPathHash; #[inline] - fn to_stable_hash_key(&self, hcx: &StableHashingContext<'gcx>) -> DefPathHash { + fn to_stable_hash_key(&self, hcx: &StableHashingContext<'a>) -> DefPathHash { hcx.def_path_hash(*self) } } -impl<'gcx> HashStable> for LocalDefId { +impl<'a> HashStable> for LocalDefId { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.def_path_hash(self.to_def_id()).hash_stable(hcx, hasher); } } -impl<'gcx> ToStableHashKey> for LocalDefId { +impl<'a> ToStableHashKey> for LocalDefId { type KeyType = DefPathHash; #[inline] - fn to_stable_hash_key(&self, hcx: &StableHashingContext<'gcx>) -> DefPathHash { + fn to_stable_hash_key(&self, hcx: &StableHashingContext<'a>) -> DefPathHash { hcx.def_path_hash(self.to_def_id()) } } -impl<'gcx> HashStable> for CrateNum { +impl<'a> HashStable> for CrateNum { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.def_path_hash(DefId { krate: *self, @@ -69,11 +69,11 @@ impl<'gcx> HashStable> for CrateNum { } } -impl<'gcx> ToStableHashKey> for CrateNum { +impl<'a> ToStableHashKey> for CrateNum { type KeyType = DefPathHash; #[inline] - fn to_stable_hash_key(&self, hcx: &StableHashingContext<'gcx>) -> DefPathHash { + fn to_stable_hash_key(&self, hcx: &StableHashingContext<'a>) -> DefPathHash { let def_id = DefId { krate: *self, index: CRATE_DEF_INDEX }; def_id.to_stable_hash_key(hcx) } @@ -81,13 +81,13 @@ impl<'gcx> ToStableHashKey> for CrateNum { impl_stable_hash_for!(tuple_struct hir::ItemLocalId { index }); -impl<'gcx> ToStableHashKey> +impl<'a> ToStableHashKey> for hir::ItemLocalId { type KeyType = hir::ItemLocalId; #[inline] fn to_stable_hash_key(&self, - _: &StableHashingContext<'gcx>) + _: &StableHashingContext<'a>) -> hir::ItemLocalId { *self } @@ -100,9 +100,9 @@ for hir::ItemLocalId { // want to pick up on a reference changing its target, so we hash the NodeIds // in "DefPath Mode". -impl<'gcx> HashStable> for hir::ItemId { +impl<'a> HashStable> for hir::ItemId { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::ItemId { id @@ -114,9 +114,9 @@ impl<'gcx> HashStable> for hir::ItemId { } } -impl<'gcx> HashStable> for hir::TraitItemId { +impl<'a> HashStable> for hir::TraitItemId { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::TraitItemId { node_id @@ -128,9 +128,9 @@ impl<'gcx> HashStable> for hir::TraitItemId { } } -impl<'gcx> HashStable> for hir::ImplItemId { +impl<'a> HashStable> for hir::ImplItemId { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::ImplItemId { node_id @@ -271,9 +271,9 @@ impl_stable_hash_for!(struct hir::TypeBinding { span }); -impl<'gcx> HashStable> for hir::Ty { +impl<'a> HashStable> for hir::Ty { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.while_hashing_hir_bodies(true, |hcx| { let hir::Ty { @@ -339,9 +339,9 @@ impl_stable_hash_for!(enum hir::FunctionRetTy { Return(t) }); -impl<'gcx> HashStable> for hir::TraitRef { +impl<'a> HashStable> for hir::TraitRef { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::TraitRef { ref path, @@ -376,9 +376,9 @@ impl_stable_hash_for!(struct hir::MacroDef { }); -impl<'gcx> HashStable> for hir::Block { +impl<'a> HashStable> for hir::Block { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::Block { ref stmts, @@ -400,9 +400,9 @@ impl<'gcx> HashStable> for hir::Block { } } -impl<'gcx> HashStable> for hir::Pat { +impl<'a> HashStable> for hir::Pat { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::Pat { id: _, @@ -527,9 +527,9 @@ impl_stable_hash_for!(enum hir::UnsafeSource { UserProvided }); -impl<'gcx> HashStable> for hir::Expr { +impl<'a> HashStable> for hir::Expr { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.while_hashing_hir_bodies(true, |hcx| { let hir::Expr { @@ -591,9 +591,9 @@ impl_stable_hash_for!(enum hir::LoopSource { ForLoop }); -impl<'gcx> HashStable> for hir::MatchSource { +impl<'a> HashStable> for hir::MatchSource { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use hir::MatchSource; @@ -647,9 +647,9 @@ impl_stable_hash_for!(enum hir::ScopeTarget { Loop(loop_id_result) }); -impl<'gcx> HashStable> for ast::Ident { +impl<'a> HashStable> for ast::Ident { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ast::Ident { ref name, @@ -660,9 +660,9 @@ impl<'gcx> HashStable> for ast::Ident { } } -impl<'gcx> HashStable> for hir::TraitItem { +impl<'a> HashStable> for hir::TraitItem { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::TraitItem { id: _, @@ -695,9 +695,9 @@ impl_stable_hash_for!(enum hir::TraitItemKind { Type(bounds, rhs) }); -impl<'gcx> HashStable> for hir::ImplItem { +impl<'a> HashStable> for hir::ImplItem { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::ImplItem { id: _, @@ -729,9 +729,9 @@ impl_stable_hash_for!(enum hir::ImplItemKind { Type(t) }); -impl<'gcx> HashStable> for hir::Visibility { +impl<'a> HashStable> for hir::Visibility { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -750,9 +750,9 @@ impl<'gcx> HashStable> for hir::Visibility { } } -impl<'gcx> HashStable> for hir::Defaultness { +impl<'a> HashStable> for hir::Defaultness { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -771,9 +771,9 @@ impl_stable_hash_for!(enum hir::ImplPolarity { Negative }); -impl<'gcx> HashStable> for hir::Mod { +impl<'a> HashStable> for hir::Mod { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::Mod { inner, @@ -826,9 +826,9 @@ impl_stable_hash_for!(enum hir::VariantData { Unit(id) }); -impl<'gcx> HashStable> for hir::Item { +impl<'a> HashStable> for hir::Item { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::Item { name, @@ -885,10 +885,10 @@ impl_stable_hash_for!(struct hir::ImplItemRef { defaultness }); -impl<'gcx> HashStable> +impl<'a> HashStable> for hir::AssociatedItemKind { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -930,9 +930,9 @@ impl_stable_hash_for!(struct hir::Arg { hir_id }); -impl<'gcx> HashStable> for hir::Body { +impl<'a> HashStable> for hir::Body { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::Body { ref arguments, @@ -948,12 +948,12 @@ impl<'gcx> HashStable> for hir::Body { } } -impl<'gcx> ToStableHashKey> for hir::BodyId { +impl<'a> ToStableHashKey> for hir::BodyId { type KeyType = (DefPathHash, hir::ItemLocalId); #[inline] fn to_stable_hash_key(&self, - hcx: &StableHashingContext<'gcx>) + hcx: &StableHashingContext<'a>) -> (DefPathHash, hir::ItemLocalId) { let hir::BodyId { node_id } = *self; node_id.to_stable_hash_key(hcx) @@ -966,9 +966,9 @@ impl_stable_hash_for!(struct hir::InlineAsmOutput { is_indirect }); -impl<'gcx> HashStable> for hir::GlobalAsm { +impl<'a> HashStable> for hir::GlobalAsm { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::GlobalAsm { asm, @@ -979,9 +979,9 @@ impl<'gcx> HashStable> for hir::GlobalAsm { } } -impl<'gcx> HashStable> for hir::InlineAsm { +impl<'a> HashStable> for hir::InlineAsm { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::InlineAsm { asm, @@ -1062,22 +1062,22 @@ impl_stable_hash_for!(enum hir::Constness { NotConst }); -impl<'gcx> HashStable> +impl<'a> HashStable> for hir::def_id::DefIndex { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.local_def_path_hash(*self).hash_stable(hcx, hasher); } } -impl<'gcx> ToStableHashKey> +impl<'a> ToStableHashKey> for hir::def_id::DefIndex { type KeyType = DefPathHash; #[inline] - fn to_stable_hash_key(&self, hcx: &StableHashingContext<'gcx>) -> DefPathHash { + fn to_stable_hash_key(&self, hcx: &StableHashingContext<'a>) -> DefPathHash { hcx.local_def_path_hash(*self) } } @@ -1090,10 +1090,10 @@ impl_stable_hash_for!(struct hir::def::Export { is_import }); -impl<'gcx> HashStable> +impl<'a> HashStable> for ::middle::lang_items::LangItem { fn hash_stable(&self, - _: &mut StableHashingContext<'gcx>, + _: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { ::std::hash::Hash::hash(self, hasher); } @@ -1104,10 +1104,10 @@ impl_stable_hash_for!(struct ::middle::lang_items::LanguageItems { missing }); -impl<'gcx> HashStable> +impl<'a> HashStable> for hir::TraitCandidate { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { let hir::TraitCandidate { @@ -1121,11 +1121,11 @@ for hir::TraitCandidate { } } -impl<'gcx> ToStableHashKey> for hir::TraitCandidate { +impl<'a> ToStableHashKey> for hir::TraitCandidate { type KeyType = (DefPathHash, Option<(DefPathHash, hir::ItemLocalId)>); fn to_stable_hash_key(&self, - hcx: &StableHashingContext<'gcx>) + hcx: &StableHashingContext<'a>) -> Self::KeyType { let hir::TraitCandidate { def_id, diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs index 67b4cfb6fa7..1e6dadae363 100644 --- a/src/librustc/ich/impls_mir.rs +++ b/src/librustc/ich/impls_mir.rs @@ -35,11 +35,11 @@ impl_stable_hash_for!(struct mir::BasicBlockData<'tcx> { statements, terminator, impl_stable_hash_for!(struct mir::UnsafetyViolation { source_info, description, kind }); impl_stable_hash_for!(struct mir::UnsafetyCheckResult { violations, unsafe_blocks }); -impl<'gcx> HashStable> +impl<'a> HashStable> for mir::BorrowKind { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -54,11 +54,11 @@ for mir::BorrowKind { } -impl<'gcx> HashStable> +impl<'a> HashStable> for mir::UnsafetyViolationKind { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -79,12 +79,12 @@ impl_stable_hash_for!(struct mir::Terminator<'tcx> { source_info }); -impl<'gcx, T> HashStable> for mir::ClearCrossCrate - where T: HashStable> +impl<'a, 'gcx, T> HashStable> for mir::ClearCrossCrate + where T: HashStable> { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -96,61 +96,61 @@ impl<'gcx, T> HashStable> for mir::ClearCrossCrate } } -impl<'gcx> HashStable> for mir::Local { +impl<'a> HashStable> for mir::Local { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use rustc_data_structures::indexed_vec::Idx; self.index().hash_stable(hcx, hasher); } } -impl<'gcx> HashStable> for mir::BasicBlock { +impl<'a> HashStable> for mir::BasicBlock { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use rustc_data_structures::indexed_vec::Idx; self.index().hash_stable(hcx, hasher); } } -impl<'gcx> HashStable> for mir::Field { +impl<'a> HashStable> for mir::Field { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use rustc_data_structures::indexed_vec::Idx; self.index().hash_stable(hcx, hasher); } } -impl<'gcx> HashStable> +impl<'a> HashStable> for mir::VisibilityScope { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use rustc_data_structures::indexed_vec::Idx; self.index().hash_stable(hcx, hasher); } } -impl<'gcx> HashStable> for mir::Promoted { +impl<'a> HashStable> for mir::Promoted { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use rustc_data_structures::indexed_vec::Idx; self.index().hash_stable(hcx, hasher); } } -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for mir::TerminatorKind<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -227,10 +227,10 @@ for mir::TerminatorKind<'gcx> { } } -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for mir::AssertMessage<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -250,10 +250,10 @@ for mir::AssertMessage<'gcx> { impl_stable_hash_for!(struct mir::Statement<'tcx> { source_info, kind }); -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for mir::StatementKind<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -287,12 +287,12 @@ for mir::StatementKind<'gcx> { } } -impl<'gcx, T> HashStable> +impl<'a, 'gcx, T> HashStable> for mir::ValidationOperand<'gcx, T> - where T: HashStable> + where T: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.place.hash_stable(hcx, hasher); @@ -304,9 +304,9 @@ impl<'gcx, T> HashStable> impl_stable_hash_for!(enum mir::ValidationOp { Acquire, Release, Suspend(region_scope) }); -impl<'gcx> HashStable> for mir::Place<'gcx> { +impl<'a, 'gcx> HashStable> for mir::Place<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -323,14 +323,14 @@ impl<'gcx> HashStable> for mir::Place<'gcx> { } } -impl<'gcx, B, V, T> HashStable> +impl<'a, 'gcx, B, V, T> HashStable> for mir::Projection<'gcx, B, V, T> - where B: HashStable>, - V: HashStable>, - T: HashStable> + where B: HashStable>, + V: HashStable>, + T: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let mir::Projection { ref base, @@ -342,13 +342,13 @@ for mir::Projection<'gcx, B, V, T> } } -impl<'gcx, V, T> HashStable> +impl<'a, 'gcx, V, T> HashStable> for mir::ProjectionElem<'gcx, V, T> - where V: HashStable>, - T: HashStable> + where V: HashStable>, + T: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -382,9 +382,9 @@ impl_stable_hash_for!(struct mir::VisibilityScopeInfo { lint_root, safety }); -impl<'gcx> HashStable> for mir::Safety { +impl<'a> HashStable> for mir::Safety { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -399,9 +399,9 @@ impl<'gcx> HashStable> for mir::Safety { } } -impl<'gcx> HashStable> for mir::Operand<'gcx> { +impl<'a, 'gcx> HashStable> for mir::Operand<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -419,9 +419,9 @@ impl<'gcx> HashStable> for mir::Operand<'gcx> { } } -impl<'gcx> HashStable> for mir::Rvalue<'gcx> { +impl<'a, 'gcx> HashStable> for mir::Rvalue<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -479,10 +479,10 @@ impl_stable_hash_for!(enum mir::CastKind { Unsize }); -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for mir::AggregateKind<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -541,9 +541,9 @@ impl_stable_hash_for!(enum mir::NullOp { impl_stable_hash_for!(struct mir::Constant<'tcx> { span, ty, literal }); -impl<'gcx> HashStable> for mir::Literal<'gcx> { +impl<'a, 'gcx> HashStable> for mir::Literal<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -570,9 +570,9 @@ impl_stable_hash_for!(struct mir::ClosureOutlivesRequirement<'tcx> { blame_span }); -impl<'gcx> HashStable> for mir::ClosureOutlivesSubject<'gcx> { +impl<'a, 'gcx> HashStable> for mir::ClosureOutlivesSubject<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -585,3 +585,5 @@ impl<'gcx> HashStable> for mir::ClosureOutlivesSubjec } } } + +impl_stable_hash_for!(struct mir::interpret::GlobalId<'tcx> { instance, promoted }); diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc/ich/impls_syntax.rs index f935cbfcde9..52f43fbed7b 100644 --- a/src/librustc/ich/impls_syntax.rs +++ b/src/librustc/ich/impls_syntax.rs @@ -29,42 +29,42 @@ use rustc_data_structures::stable_hasher::{HashStable, ToStableHashKey, StableHasher, StableHasherResult}; use rustc_data_structures::accumulate_vec::AccumulateVec; -impl<'gcx> HashStable> for InternedString { +impl<'a> HashStable> for InternedString { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let s: &str = &**self; s.hash_stable(hcx, hasher); } } -impl<'gcx> ToStableHashKey> for InternedString { +impl<'a> ToStableHashKey> for InternedString { type KeyType = InternedString; #[inline] fn to_stable_hash_key(&self, - _: &StableHashingContext<'gcx>) + _: &StableHashingContext<'a>) -> InternedString { self.clone() } } -impl<'gcx> HashStable> for ast::Name { +impl<'a> HashStable> for ast::Name { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.as_str().hash_stable(hcx, hasher); } } -impl<'gcx> ToStableHashKey> for ast::Name { +impl<'a> ToStableHashKey> for ast::Name { type KeyType = InternedString; #[inline] fn to_stable_hash_key(&self, - _: &StableHashingContext<'gcx>) + _: &StableHashingContext<'a>) -> InternedString { self.as_str() } @@ -111,10 +111,10 @@ impl_stable_hash_for!(struct ::syntax::attr::Stability { rustc_const_unstable }); -impl<'gcx> HashStable> +impl<'a> HashStable> for ::syntax::attr::StabilityLevel { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -166,9 +166,9 @@ impl_stable_hash_for!(struct ::syntax::ast::Lifetime { id, span, ident }); impl_stable_hash_for!(enum ::syntax::ast::StrStyle { Cooked, Raw(pounds) }); impl_stable_hash_for!(enum ::syntax::ast::AttrStyle { Outer, Inner }); -impl<'gcx> HashStable> for [ast::Attribute] { +impl<'a> HashStable> for [ast::Attribute] { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { if self.len() == 0 { self.len().hash_stable(hcx, hasher); @@ -191,9 +191,9 @@ impl<'gcx> HashStable> for [ast::Attribute] { } } -impl<'gcx> HashStable> for ast::Attribute { +impl<'a> HashStable> for ast::Attribute { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { // Make sure that these have been filtered out. debug_assert!(self.name().map(|name| !hcx.is_ignored_attr(name)).unwrap_or(true)); @@ -220,10 +220,10 @@ impl<'gcx> HashStable> for ast::Attribute { } } -impl<'gcx> HashStable> +impl<'a> HashStable> for tokenstream::TokenTree { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -242,10 +242,10 @@ for tokenstream::TokenTree { } } -impl<'gcx> HashStable> +impl<'a> HashStable> for tokenstream::TokenStream { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { for sub_tt in self.trees() { sub_tt.hash_stable(hcx, hasher); @@ -253,9 +253,11 @@ for tokenstream::TokenStream { } } -fn hash_token<'gcx, W: StableHasherResult>(token: &token::Token, - hcx: &mut StableHashingContext<'gcx>, - hasher: &mut StableHasher) { +fn hash_token<'a, 'gcx, W: StableHasherResult>( + token: &token::Token, + hcx: &mut StableHashingContext<'a>, + hasher: &mut StableHasher, +) { mem::discriminant(token).hash_stable(hcx, hasher); match *token { token::Token::Eq | @@ -383,9 +385,9 @@ impl_stable_hash_for!(enum ::syntax_pos::FileName { Custom(s) }); -impl<'gcx> HashStable> for FileMap { +impl<'a> HashStable> for FileMap { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let FileMap { name: _, // We hash the smaller name_hash instead of this diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index a8ed885e78d..d927a151610 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -21,12 +21,13 @@ use std::mem; use middle::region; use traits; use ty; +use mir; -impl<'gcx, T> HashStable> +impl<'a, 'gcx, T> HashStable> for &'gcx ty::Slice - where T: HashStable> { + where T: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { thread_local! { static CACHE: RefCell> = @@ -51,19 +52,19 @@ for &'gcx ty::Slice } } -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for ty::subst::Kind<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.unpack().hash_stable(hcx, hasher); } } -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for ty::subst::UnpackedKind<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { match self { ty::subst::UnpackedKind::Lifetime(lt) => lt.hash_stable(hcx, hasher), @@ -72,10 +73,10 @@ for ty::subst::UnpackedKind<'gcx> { } } -impl<'gcx> HashStable> +impl<'a> HashStable> for ty::RegionKind { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -119,20 +120,20 @@ for ty::RegionKind { } } -impl<'gcx> HashStable> for ty::RegionVid { +impl<'a> HashStable> for ty::RegionVid { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use rustc_data_structures::indexed_vec::Idx; self.index().hash_stable(hcx, hasher); } } -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for ty::adjustment::AutoBorrow<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -147,10 +148,10 @@ for ty::adjustment::AutoBorrow<'gcx> { } } -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for ty::adjustment::Adjust<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -196,10 +197,10 @@ impl_stable_hash_for!(enum ty::BorrowKind { MutBorrow }); -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for ty::UpvarCapture<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -223,11 +224,11 @@ impl_stable_hash_for!(struct ty::FnSig<'tcx> { abi }); -impl<'gcx, T> HashStable> for ty::Binder - where T: HashStable> +impl<'a, 'gcx, T> HashStable> for ty::Binder + where T: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ty::Binder(ref inner) = *self; inner.hash_stable(hcx, hasher); @@ -246,13 +247,13 @@ impl_stable_hash_for!(struct ty::TraitRef<'tcx> { def_id, substs }); impl_stable_hash_for!(struct ty::TraitPredicate<'tcx> { trait_ref }); impl_stable_hash_for!(struct ty::SubtypePredicate<'tcx> { a_is_expected, a, b }); -impl<'gcx, A, B> HashStable> +impl<'a, 'gcx, A, B> HashStable> for ty::OutlivesPredicate - where A: HashStable>, - B: HashStable>, + where A: HashStable>, + B: HashStable>, { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ty::OutlivesPredicate(ref a, ref b) = *self; a.hash_stable(hcx, hasher); @@ -264,9 +265,9 @@ impl_stable_hash_for!(struct ty::ProjectionPredicate<'tcx> { projection_ty, ty } impl_stable_hash_for!(struct ty::ProjectionTy<'tcx> { substs, item_def_id }); -impl<'gcx> HashStable> for ty::Predicate<'gcx> { +impl<'a, 'gcx> HashStable> for ty::Predicate<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -304,9 +305,9 @@ impl<'gcx> HashStable> for ty::Predicate<'gcx> { } } -impl<'gcx> HashStable> for ty::AdtFlags { +impl<'a> HashStable> for ty::AdtFlags { fn hash_stable(&self, - _: &mut StableHashingContext<'gcx>, + _: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { std_hash::Hash::hash(self, hasher); } @@ -331,69 +332,102 @@ impl_stable_hash_for!(struct ty::FieldDef { vis }); -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for ::middle::const_val::ConstVal<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use middle::const_val::ConstVal::*; - use middle::const_val::ConstAggregate::*; mem::discriminant(self).hash_stable(hcx, hasher); match *self { - Integral(ref value) => { - value.hash_stable(hcx, hasher); - } - Float(ref value) => { - value.hash_stable(hcx, hasher); - } - Str(ref value) => { - value.hash_stable(hcx, hasher); - } - ByteStr(ref value) => { - value.hash_stable(hcx, hasher); - } - Bool(value) => { - value.hash_stable(hcx, hasher); - } - Char(value) => { - value.hash_stable(hcx, hasher); - } - Variant(def_id) => { - def_id.hash_stable(hcx, hasher); - } - Function(def_id, substs) => { - def_id.hash_stable(hcx, hasher); - hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { - substs.hash_stable(hcx, hasher); - }); - } - Aggregate(Struct(ref name_values)) => { - let mut values = name_values.to_vec(); - values.sort_unstable_by_key(|&(ref name, _)| name.clone()); - values.hash_stable(hcx, hasher); - } - Aggregate(Tuple(ref value)) => { - value.hash_stable(hcx, hasher); - } - Aggregate(Array(ref value)) => { - value.hash_stable(hcx, hasher); - } - Aggregate(Repeat(ref value, times)) => { - value.hash_stable(hcx, hasher); - times.hash_stable(hcx, hasher); - } Unevaluated(def_id, substs) => { def_id.hash_stable(hcx, hasher); substs.hash_stable(hcx, hasher); } + Value(ref value) => { + value.hash_stable(hcx, hasher); + } } } } -impl_stable_hash_for!(struct ::middle::const_val::ByteArray<'tcx> { - data +impl_stable_hash_for!(enum mir::interpret::Value { + ByVal(v), + ByValPair(a, b), + ByRef(ptr, align) +}); + +impl_stable_hash_for!(struct mir::interpret::MemoryPointer { + alloc_id, + offset +}); + +enum AllocDiscriminant { + Static, + Constant, + Function, +} +impl_stable_hash_for!(enum self::AllocDiscriminant { + Static, + Constant, + Function +}); + +impl<'a> HashStable> for mir::interpret::AllocId { + fn hash_stable( + &self, + hcx: &mut StableHashingContext<'a>, + hasher: &mut StableHasher, + ) { + ty::tls::with_opt(|tcx| { + let tcx = tcx.expect("can't hash AllocIds during hir lowering"); + if let Some(def_id) = tcx.interpret_interner.get_corresponding_static_def_id(*self) { + AllocDiscriminant::Static.hash_stable(hcx, hasher); + // statics are unique via their DefId + def_id.hash_stable(hcx, hasher); + } else if let Some(alloc) = tcx.interpret_interner.get_alloc(*self) { + // not a static, can't be recursive, hash the allocation + AllocDiscriminant::Constant.hash_stable(hcx, hasher); + alloc.hash_stable(hcx, hasher); + } else if let Some(inst) = tcx.interpret_interner.get_fn(*self) { + AllocDiscriminant::Function.hash_stable(hcx, hasher); + inst.hash_stable(hcx, hasher); + } else { + bug!("no allocation for {}", self); + } + }); + } +} + +impl<'a> HashStable> for mir::interpret::Allocation { + fn hash_stable( + &self, + hcx: &mut StableHashingContext<'a>, + hasher: &mut StableHasher, + ) { + self.bytes.hash_stable(hcx, hasher); + for reloc in self.relocations.iter() { + reloc.hash_stable(hcx, hasher); + } + self.undef_mask.hash_stable(hcx, hasher); + self.align.hash_stable(hcx, hasher); + self.runtime_mutability.hash_stable(hcx, hasher); + } +} + +impl_stable_hash_for!(enum ::syntax::ast::Mutability { + Immutable, + Mutable +}); + +impl_stable_hash_for!(struct mir::interpret::Pointer{primval}); + +impl_stable_hash_for!(enum mir::interpret::PrimVal { + Bytes(b), + Ptr(p), + Undef }); impl_stable_hash_for!(struct ty::Const<'tcx> { @@ -406,26 +440,22 @@ impl_stable_hash_for!(struct ::middle::const_val::ConstEvalErr<'tcx> { kind }); -impl<'gcx> HashStable> +impl_stable_hash_for!(struct ::middle::const_val::FrameInfo { + span, + location +}); + +impl<'a, 'gcx> HashStable> for ::middle::const_val::ErrKind<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use middle::const_val::ErrKind::*; mem::discriminant(self).hash_stable(hcx, hasher); match *self { - CannotCast | - MissingStructField | NonConstPath | - ExpectedConstTuple | - ExpectedConstStruct | - IndexedNonVec | - IndexNotUsize | - MiscBinaryOp | - MiscCatchAll | - IndexOpFeatureGated | TypeckError | CheckMatchError => { // nothing to do @@ -443,9 +473,10 @@ for ::middle::const_val::ErrKind<'gcx> { LayoutError(ref layout_error) => { layout_error.hash_stable(hcx, hasher); } - ErroneousReferencedConstant(ref const_val) => { - const_val.hash_stable(hcx, hasher); - } + Miri(ref err, ref trace) => { + err.hash_stable(hcx, hasher); + trace.hash_stable(hcx, hasher); + }, } } } @@ -459,6 +490,167 @@ impl_stable_hash_for!(struct ty::GenericPredicates<'tcx> { predicates }); +impl<'a, 'gcx> HashStable> +for ::mir::interpret::EvalError<'gcx> { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'a>, + hasher: &mut StableHasher) { + use mir::interpret::EvalErrorKind::*; + + mem::discriminant(&self.kind).hash_stable(hcx, hasher); + + match self.kind { + DanglingPointerDeref | + DoubleFree | + InvalidMemoryAccess | + InvalidFunctionPointer | + InvalidBool | + InvalidDiscriminant | + InvalidNullPointerUsage | + ReadPointerAsBytes | + ReadBytesAsPointer | + InvalidPointerMath | + ReadUndefBytes | + DeadLocal | + ExecutionTimeLimitReached | + StackFrameLimitReached | + OutOfTls | + TlsOutOfBounds | + CalledClosureAsFunction | + VtableForArgumentlessMethod | + ModifiedConstantMemory | + AssumptionNotHeld | + InlineAsm | + ReallocateNonBasePtr | + DeallocateNonBasePtr | + HeapAllocZeroBytes | + Unreachable | + Panic | + ReadFromReturnPointer | + UnimplementedTraitSelection | + TypeckError | + DerefFunctionPointer | + ExecuteMemory | + ReferencedConstant | + OverflowingMath => {} + MachineError(ref err) => err.hash_stable(hcx, hasher), + FunctionPointerTyMismatch(a, b) => { + a.hash_stable(hcx, hasher); + b.hash_stable(hcx, hasher) + }, + NoMirFor(ref s) => s.hash_stable(hcx, hasher), + UnterminatedCString(ptr) => ptr.hash_stable(hcx, hasher), + PointerOutOfBounds { + ptr, + access, + allocation_size, + } => { + ptr.hash_stable(hcx, hasher); + access.hash_stable(hcx, hasher); + allocation_size.hash_stable(hcx, hasher) + }, + InvalidBoolOp(bop) => bop.hash_stable(hcx, hasher), + Unimplemented(ref s) => s.hash_stable(hcx, hasher), + ArrayIndexOutOfBounds(sp, a, b) => { + sp.hash_stable(hcx, hasher); + a.hash_stable(hcx, hasher); + b.hash_stable(hcx, hasher) + }, + Math(sp, ref err) => { + sp.hash_stable(hcx, hasher); + err.hash_stable(hcx, hasher) + }, + Intrinsic(ref s) => s.hash_stable(hcx, hasher), + InvalidChar(c) => c.hash_stable(hcx, hasher), + AbiViolation(ref s) => s.hash_stable(hcx, hasher), + AlignmentCheckFailed { + required, + has, + } => { + required.hash_stable(hcx, hasher); + has.hash_stable(hcx, hasher) + }, + MemoryLockViolation { + ptr, + len, + frame, + access, + ref lock, + } => { + ptr.hash_stable(hcx, hasher); + len.hash_stable(hcx, hasher); + frame.hash_stable(hcx, hasher); + access.hash_stable(hcx, hasher); + lock.hash_stable(hcx, hasher) + }, + MemoryAcquireConflict { + ptr, + len, + kind, + ref lock, + } => { + ptr.hash_stable(hcx, hasher); + len.hash_stable(hcx, hasher); + kind.hash_stable(hcx, hasher); + lock.hash_stable(hcx, hasher) + }, + InvalidMemoryLockRelease { + ptr, + len, + frame, + ref lock, + } => { + ptr.hash_stable(hcx, hasher); + len.hash_stable(hcx, hasher); + frame.hash_stable(hcx, hasher); + lock.hash_stable(hcx, hasher) + }, + DeallocatedLockedMemory { + ptr, + ref lock, + } => { + ptr.hash_stable(hcx, hasher); + lock.hash_stable(hcx, hasher) + }, + ValidationFailure(ref s) => s.hash_stable(hcx, hasher), + TypeNotPrimitive(ty) => ty.hash_stable(hcx, hasher), + ReallocatedWrongMemoryKind(ref a, ref b) => { + a.hash_stable(hcx, hasher); + b.hash_stable(hcx, hasher) + }, + DeallocatedWrongMemoryKind(ref a, ref b) => { + a.hash_stable(hcx, hasher); + b.hash_stable(hcx, hasher) + }, + IncorrectAllocationInformation(a, b, c, d) => { + a.hash_stable(hcx, hasher); + b.hash_stable(hcx, hasher); + c.hash_stable(hcx, hasher); + d.hash_stable(hcx, hasher) + }, + Layout(lay) => lay.hash_stable(hcx, hasher), + HeapAllocNonPowerOfTwoAlignment(n) => n.hash_stable(hcx, hasher), + PathNotFound(ref v) => v.hash_stable(hcx, hasher), + } + } +} + +impl_stable_hash_for!(enum mir::interpret::Lock { + NoLock, + WriteLock(dl), + ReadLock(v) +}); + +impl_stable_hash_for!(struct mir::interpret::DynamicLifetime { + frame, + region +}); + +impl_stable_hash_for!(enum mir::interpret::AccessKind { + Read, + Write +}); + impl_stable_hash_for!(enum ty::Variance { Covariant, Invariant, @@ -470,9 +662,9 @@ impl_stable_hash_for!(enum ty::adjustment::CustomCoerceUnsized { Struct(index) }); -impl<'gcx> HashStable> for ty::Generics { +impl<'a> HashStable> for ty::Generics { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ty::Generics { parent, @@ -498,10 +690,10 @@ impl<'gcx> HashStable> for ty::Generics { } } -impl<'gcx> HashStable> +impl<'a> HashStable> for ty::RegionParameterDef { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ty::RegionParameterDef { name, @@ -527,12 +719,12 @@ impl_stable_hash_for!(struct ty::TypeParameterDef { synthetic }); -impl<'gcx, T> HashStable> +impl<'a, 'gcx, T> HashStable> for ::middle::resolve_lifetime::Set1 - where T: HashStable> + where T: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use middle::resolve_lifetime::Set1; @@ -583,11 +775,11 @@ impl_stable_hash_for!(enum ty::cast::CastKind { impl_stable_hash_for!(tuple_struct ::middle::region::FirstStatementIndex { idx }); impl_stable_hash_for!(struct ::middle::region::Scope { id, code }); -impl<'gcx> ToStableHashKey> for region::Scope { +impl<'a> ToStableHashKey> for region::Scope { type KeyType = region::Scope; #[inline] - fn to_stable_hash_key(&self, _: &StableHashingContext<'gcx>) -> region::Scope { + fn to_stable_hash_key(&self, _: &StableHashingContext<'a>) -> region::Scope { *self } } @@ -613,11 +805,11 @@ impl_stable_hash_for!(enum ty::BoundRegion { BrEnv }); -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for ty::TypeVariants<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use ty::TypeVariants::*; @@ -714,11 +906,11 @@ impl_stable_hash_for!(struct ty::TypeAndMut<'tcx> { mutbl }); -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for ty::ExistentialPredicate<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -751,9 +943,9 @@ impl_stable_hash_for!(struct ty::Instance<'tcx> { substs }); -impl<'gcx> HashStable> for ty::InstanceDef<'gcx> { +impl<'a, 'gcx> HashStable> for ty::InstanceDef<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -775,21 +967,21 @@ impl<'gcx> HashStable> for ty::InstanceDef<'gcx> { ty::InstanceDef::ClosureOnceShim { call_once } => { call_once.hash_stable(hcx, hasher); } - ty::InstanceDef::DropGlue(def_id, t) => { + ty::InstanceDef::DropGlue(def_id, ty) => { def_id.hash_stable(hcx, hasher); - t.hash_stable(hcx, hasher); + ty.hash_stable(hcx, hasher); } - ty::InstanceDef::CloneShim(def_id, t) => { + ty::InstanceDef::CloneShim(def_id, ty) => { def_id.hash_stable(hcx, hasher); - t.hash_stable(hcx, hasher); + ty.hash_stable(hcx, hasher); } } } } -impl<'gcx> HashStable> for ty::TraitDef { +impl<'a> HashStable> for ty::TraitDef { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ty::TraitDef { // We already have the def_path_hash below, no need to hash it twice @@ -817,9 +1009,9 @@ impl_stable_hash_for!(struct ty::DtorckConstraint<'tcx> { }); -impl<'gcx> HashStable> for ty::CrateVariancesMap { +impl<'a> HashStable> for ty::CrateVariancesMap { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ty::CrateVariancesMap { ref variances, @@ -853,12 +1045,12 @@ impl_stable_hash_for!(enum ty::AssociatedItemContainer { }); -impl<'gcx, T> HashStable> +impl<'a, 'gcx, T> HashStable> for ty::steal::Steal - where T: HashStable> + where T: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.borrow().hash_stable(hcx, hasher); } @@ -881,10 +1073,10 @@ impl_stable_hash_for!(enum ::middle::privacy::AccessLevel { Public }); -impl<'gcx> HashStable> +impl<'a> HashStable> for ::middle::privacy::AccessLevels { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { let ::middle::privacy::AccessLevels { @@ -911,10 +1103,10 @@ impl_stable_hash_for!(tuple_struct ::middle::reachable::ReachableSet { reachable_set }); -impl<'gcx, N> HashStable> -for traits::Vtable<'gcx, N> where N: HashStable> { +impl<'a, 'gcx, N> HashStable> +for traits::Vtable<'gcx, N> where N: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use traits::Vtable::*; @@ -933,10 +1125,10 @@ for traits::Vtable<'gcx, N> where N: HashStable> { } } -impl<'gcx, N> HashStable> -for traits::VtableImplData<'gcx, N> where N: HashStable> { +impl<'a, 'gcx, N> HashStable> +for traits::VtableImplData<'gcx, N> where N: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let traits::VtableImplData { impl_def_id, @@ -949,10 +1141,10 @@ for traits::VtableImplData<'gcx, N> where N: HashStable HashStable> -for traits::VtableAutoImplData where N: HashStable> { +impl<'a, 'gcx, N> HashStable> +for traits::VtableAutoImplData where N: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let traits::VtableAutoImplData { trait_def_id, @@ -963,10 +1155,10 @@ for traits::VtableAutoImplData where N: HashStable } } -impl<'gcx, N> HashStable> -for traits::VtableObjectData<'gcx, N> where N: HashStable> { +impl<'a, 'gcx, N> HashStable> +for traits::VtableObjectData<'gcx, N> where N: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let traits::VtableObjectData { upcast_trait_ref, @@ -979,10 +1171,10 @@ for traits::VtableObjectData<'gcx, N> where N: HashStable HashStable> -for traits::VtableBuiltinData where N: HashStable> { +impl<'a, 'gcx, N> HashStable> +for traits::VtableBuiltinData where N: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let traits::VtableBuiltinData { ref nested, @@ -991,10 +1183,10 @@ for traits::VtableBuiltinData where N: HashStable> } } -impl<'gcx, N> HashStable> -for traits::VtableClosureData<'gcx, N> where N: HashStable> { +impl<'a, 'gcx, N> HashStable> +for traits::VtableClosureData<'gcx, N> where N: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let traits::VtableClosureData { closure_def_id, @@ -1007,10 +1199,10 @@ for traits::VtableClosureData<'gcx, N> where N: HashStable HashStable> -for traits::VtableFnPointerData<'gcx, N> where N: HashStable> { +impl<'a, 'gcx, N> HashStable> +for traits::VtableFnPointerData<'gcx, N> where N: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let traits::VtableFnPointerData { fn_ty, @@ -1021,10 +1213,10 @@ for traits::VtableFnPointerData<'gcx, N> where N: HashStable HashStable> -for traits::VtableGeneratorData<'gcx, N> where N: HashStable> { +impl<'a, 'gcx, N> HashStable> +for traits::VtableGeneratorData<'gcx, N> where N: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let traits::VtableGeneratorData { closure_def_id, diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index d08a41010ab..56de2939ffa 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -49,6 +49,7 @@ #![feature(core_intrinsics)] #![feature(drain_filter)] #![feature(dyn_trait)] +#![feature(entry_or_default)] #![feature(from_ref)] #![feature(fs_read_write)] #![feature(i128)] diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index b68b7dc6c06..a951265d458 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -20,6 +20,12 @@ use session::Session; use session::config::Epoch; use syntax::codemap::Span; +declare_lint! { + pub EXCEEDING_BITSHIFTS, + Deny, + "shift exceeds the type's number of bits" +} + declare_lint! { pub CONST_ERR, Warn, @@ -263,6 +269,12 @@ declare_lint! { Epoch::Epoch2018 } +declare_lint! { + pub ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, + Warn, + "floating-point literals cannot be used in patterns" +} + /// Does nothing as a lint pass, but registers some `Lint`s /// which are used by other parts of the compiler. #[derive(Copy, Clone)] @@ -271,6 +283,8 @@ pub struct HardwiredLints; impl LintPass for HardwiredLints { fn get_lints(&self) -> LintArray { lint_array!( + ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, + EXCEEDING_BITSHIFTS, UNUSED_IMPORTS, UNUSED_EXTERN_CRATES, UNUSED_QUALIFICATIONS, diff --git a/src/librustc/lint/levels.rs b/src/librustc/lint/levels.rs index 909904b4fc3..8a899a35ecb 100644 --- a/src/librustc/lint/levels.rs +++ b/src/librustc/lint/levels.rs @@ -394,10 +394,10 @@ impl LintLevelMap { } } -impl<'gcx> HashStable> for LintLevelMap { +impl<'a> HashStable> for LintLevelMap { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let LintLevelMap { ref sets, diff --git a/src/librustc/macros.rs b/src/librustc/macros.rs index f0285d6a937..9a394e52481 100644 --- a/src/librustc/macros.rs +++ b/src/librustc/macros.rs @@ -73,10 +73,10 @@ macro_rules! __impl_stable_hash_field { #[macro_export] macro_rules! impl_stable_hash_for { (enum $enum_name:path { $( $variant:ident $( ( $($arg:ident),* ) )* ),* }) => { - impl<'tcx> ::rustc_data_structures::stable_hasher::HashStable<$crate::ich::StableHashingContext<'tcx>> for $enum_name { + impl<'a, 'tcx> ::rustc_data_structures::stable_hasher::HashStable<$crate::ich::StableHashingContext<'a>> for $enum_name { #[inline] fn hash_stable(&self, - __ctx: &mut $crate::ich::StableHashingContext<'tcx>, + __ctx: &mut $crate::ich::StableHashingContext<'a>, __hasher: &mut ::rustc_data_structures::stable_hasher::StableHasher) { use $enum_name::*; ::std::mem::discriminant(self).hash_stable(__ctx, __hasher); @@ -92,10 +92,10 @@ macro_rules! impl_stable_hash_for { } }; (struct $struct_name:path { $($field:ident),* }) => { - impl<'tcx> ::rustc_data_structures::stable_hasher::HashStable<$crate::ich::StableHashingContext<'tcx>> for $struct_name { + impl<'a, 'tcx> ::rustc_data_structures::stable_hasher::HashStable<$crate::ich::StableHashingContext<'a>> for $struct_name { #[inline] fn hash_stable(&self, - __ctx: &mut $crate::ich::StableHashingContext<'tcx>, + __ctx: &mut $crate::ich::StableHashingContext<'a>, __hasher: &mut ::rustc_data_structures::stable_hasher::StableHasher) { let $struct_name { $(ref $field),* @@ -106,10 +106,10 @@ macro_rules! impl_stable_hash_for { } }; (tuple_struct $struct_name:path { $($field:ident),* }) => { - impl<'tcx> ::rustc_data_structures::stable_hasher::HashStable<$crate::ich::StableHashingContext<'tcx>> for $struct_name { + impl<'a, 'tcx> ::rustc_data_structures::stable_hasher::HashStable<$crate::ich::StableHashingContext<'a>> for $struct_name { #[inline] fn hash_stable(&self, - __ctx: &mut $crate::ich::StableHashingContext<'tcx>, + __ctx: &mut $crate::ich::StableHashingContext<'a>, __hasher: &mut ::rustc_data_structures::stable_hasher::StableHasher) { let $struct_name ( $(ref $field),* @@ -125,11 +125,11 @@ macro_rules! impl_stable_hash_for { macro_rules! impl_stable_hash_for_spanned { ($T:path) => ( - impl<'tcx> HashStable> for ::syntax::codemap::Spanned<$T> + impl<'a, 'tcx> HashStable> for ::syntax::codemap::Spanned<$T> { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'tcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.node.hash_stable(hcx, hasher); self.span.hash_stable(hcx, hasher); diff --git a/src/librustc/middle/borrowck.rs b/src/librustc/middle/borrowck.rs index 380f79361e2..6f5791ed5d7 100644 --- a/src/librustc/middle/borrowck.rs +++ b/src/librustc/middle/borrowck.rs @@ -20,9 +20,9 @@ pub struct BorrowCheckResult { pub used_mut_nodes: FxHashSet, } -impl<'gcx> HashStable> for BorrowCheckResult { +impl<'a> HashStable> for BorrowCheckResult { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let BorrowCheckResult { ref used_mut_nodes, diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index 440af39a0d4..8c3dfd0bce7 100644 --- a/src/librustc/middle/const_val.rs +++ b/src/librustc/middle/const_val.rs @@ -8,72 +8,43 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -pub use rustc_const_math::ConstInt; - use hir::def_id::DefId; use ty::{self, TyCtxt, layout}; use ty::subst::Substs; use rustc_const_math::*; +use mir::interpret::{Value, PrimVal}; +use errors::DiagnosticBuilder; use graphviz::IntoCow; -use errors::DiagnosticBuilder; -use serialize::{self, Encodable, Encoder, Decodable, Decoder}; -use syntax::symbol::InternedString; -use syntax::ast; use syntax_pos::Span; use std::borrow::Cow; +use std::rc::Rc; pub type EvalResult<'tcx> = Result<&'tcx ty::Const<'tcx>, ConstEvalErr<'tcx>>; #[derive(Copy, Clone, Debug, Hash, RustcEncodable, RustcDecodable, Eq, PartialEq)] pub enum ConstVal<'tcx> { - Integral(ConstInt), - Float(ConstFloat), - Str(InternedString), - ByteStr(ByteArray<'tcx>), - Bool(bool), - Char(char), - Variant(DefId), - Function(DefId, &'tcx Substs<'tcx>), - Aggregate(ConstAggregate<'tcx>), Unevaluated(DefId, &'tcx Substs<'tcx>), -} - -#[derive(Copy, Clone, Debug, Hash, RustcEncodable, Eq, PartialEq)] -pub struct ByteArray<'tcx> { - pub data: &'tcx [u8], -} - -impl<'tcx> serialize::UseSpecializedDecodable for ByteArray<'tcx> {} - -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] -pub enum ConstAggregate<'tcx> { - Struct(&'tcx [(ast::Name, &'tcx ty::Const<'tcx>)]), - Tuple(&'tcx [&'tcx ty::Const<'tcx>]), - Array(&'tcx [&'tcx ty::Const<'tcx>]), - Repeat(&'tcx ty::Const<'tcx>, u64), -} - -impl<'tcx> Encodable for ConstAggregate<'tcx> { - fn encode(&self, _: &mut S) -> Result<(), S::Error> { - bug!("should never encode ConstAggregate::{:?}", self) - } -} - -impl<'tcx> Decodable for ConstAggregate<'tcx> { - fn decode(_: &mut D) -> Result { - bug!("should never decode ConstAggregate") - } + Value(Value), } impl<'tcx> ConstVal<'tcx> { - pub fn to_const_int(&self) -> Option { + pub fn to_raw_bits(&self) -> Option { match *self { - ConstVal::Integral(i) => Some(i), - ConstVal::Bool(b) => Some(ConstInt::U8(b as u8)), - ConstVal::Char(ch) => Some(ConstInt::U32(ch as u32)), - _ => None + ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))) => { + Some(b) + }, + _ => None, + } + } + pub fn unwrap_u64(&self) -> u64 { + match self.to_raw_bits() { + Some(val) => { + assert_eq!(val as u64 as u128, val); + val as u64 + }, + None => bug!("expected constant u64, got {:#?}", self), } } } @@ -81,33 +52,28 @@ impl<'tcx> ConstVal<'tcx> { #[derive(Clone, Debug)] pub struct ConstEvalErr<'tcx> { pub span: Span, - pub kind: ErrKind<'tcx>, + pub kind: Rc>, } #[derive(Clone, Debug)] pub enum ErrKind<'tcx> { - CannotCast, - MissingStructField, NonConstPath, UnimplementedConstVal(&'static str), - ExpectedConstTuple, - ExpectedConstStruct, - IndexedNonVec, - IndexNotUsize, IndexOutOfBounds { len: u64, index: u64 }, - MiscBinaryOp, - MiscCatchAll, - - IndexOpFeatureGated, Math(ConstMathErr), LayoutError(layout::LayoutError<'tcx>), - ErroneousReferencedConstant(Box>), - TypeckError, CheckMatchError, + Miri(::mir::interpret::EvalError<'tcx>, Vec), +} + +#[derive(Clone, Debug)] +pub struct FrameInfo { + pub span: Span, + pub location: String, } impl<'tcx> From for ErrKind<'tcx> { @@ -120,21 +86,23 @@ impl<'tcx> From for ErrKind<'tcx> { } #[derive(Clone, Debug)] -pub enum ConstEvalErrDescription<'a> { +pub enum ConstEvalErrDescription<'a, 'tcx: 'a> { Simple(Cow<'a, str>), + Backtrace(&'a ::mir::interpret::EvalError<'tcx>, &'a [FrameInfo]), } -impl<'a> ConstEvalErrDescription<'a> { +impl<'a, 'tcx> ConstEvalErrDescription<'a, 'tcx> { /// Return a one-line description of the error, for lints and such pub fn into_oneline(self) -> Cow<'a, str> { match self { ConstEvalErrDescription::Simple(simple) => simple, + ConstEvalErrDescription::Backtrace(miri, _) => format!("{}", miri).into_cow(), } } } impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { - pub fn description(&self) -> ConstEvalErrDescription { + pub fn description(&'a self) -> ConstEvalErrDescription<'a, 'tcx> { use self::ErrKind::*; use self::ConstEvalErrDescription::*; @@ -145,31 +113,21 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { }) } - match self.kind { - CannotCast => simple!("can't cast this type"), - MissingStructField => simple!("nonexistent struct field"), + match *self.kind { NonConstPath => simple!("non-constant path in constant expression"), UnimplementedConstVal(what) => simple!("unimplemented constant expression: {}", what), - ExpectedConstTuple => simple!("expected constant tuple"), - ExpectedConstStruct => simple!("expected constant struct"), - IndexedNonVec => simple!("indexing is only supported for arrays"), - IndexNotUsize => simple!("indices must be of type `usize`"), IndexOutOfBounds { len, index } => { simple!("index out of bounds: the len is {} but the index is {}", len, index) } - MiscBinaryOp => simple!("bad operands for binary"), - MiscCatchAll => simple!("unsupported constant expr"), - IndexOpFeatureGated => simple!("the index operation on const values is unstable"), Math(ref err) => Simple(err.description().into_cow()), LayoutError(ref err) => Simple(err.to_string().into_cow()), - ErroneousReferencedConstant(_) => simple!("could not evaluate referenced constant"), - TypeckError => simple!("type-checking failed"), CheckMatchError => simple!("match-checking failed"), + Miri(ref err, ref trace) => Backtrace(err, trace), } } @@ -179,15 +137,8 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { primary_kind: &str) -> DiagnosticBuilder<'gcx> { - let mut err = self; - while let &ConstEvalErr { - kind: ErrKind::ErroneousReferencedConstant(box ref i_err), .. - } = err { - err = i_err; - } - - let mut diag = struct_span_err!(tcx.sess, err.span, E0080, "constant evaluation error"); - err.note(tcx, primary_span, primary_kind, &mut diag); + let mut diag = struct_error(tcx, self.span, "constant evaluation error"); + self.note(tcx, primary_span, primary_kind, &mut diag); diag } @@ -201,6 +152,12 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { ConstEvalErrDescription::Simple(message) => { diag.span_label(self.span, message); } + ConstEvalErrDescription::Backtrace(miri, frames) => { + diag.span_label(self.span, format!("{}", miri)); + for frame in frames { + diag.span_label(frame.span, format!("inside call to `{}`", frame.location)); + } + } } if !primary_span.contains(self.span) { @@ -214,10 +171,25 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { primary_span: Span, primary_kind: &str) { - match self.kind { + match *self.kind { ErrKind::TypeckError | ErrKind::CheckMatchError => return, + ErrKind::Miri(ref miri, _) => { + match miri.kind { + ::mir::interpret::EvalErrorKind::TypeckError | + ::mir::interpret::EvalErrorKind::Layout(_) => return, + _ => {}, + } + } _ => {} } self.struct_error(tcx, primary_span, primary_kind).emit(); } } + +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/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 447ce46ee5c..3b37031cf46 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -280,6 +280,7 @@ language_item_table! { GeneratorTraitLangItem, "generator", gen_trait; EqTraitLangItem, "eq", eq_trait; + PartialOrdTraitLangItem, "partial_ord", partial_ord_trait; OrdTraitLangItem, "ord", ord_trait; // A number of panic-related lang items. The `panic` item corresponds to diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index c532427cc9b..30d63b8443e 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -913,8 +913,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { // Always promote `[T; 0]` (even when e.g. borrowed mutably). let promotable = match expr_ty.sty { - ty::TyArray(_, len) if - len.val.to_const_int().and_then(|i| i.to_u64()) == Some(0) => true, + ty::TyArray(_, len) if len.val.to_raw_bits() == Some(0) => true, _ => promotable, }; diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 812ee0dc72f..c73930553cd 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -1488,9 +1488,9 @@ pub fn provide(providers: &mut Providers) { }; } -impl<'gcx> HashStable> for ScopeTree { +impl<'a> HashStable> for ScopeTree { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ScopeTree { root_body, diff --git a/src/librustc/mir/cache.rs b/src/librustc/mir/cache.rs index efc2f647cfd..45cb70d0070 100644 --- a/src/librustc/mir/cache.rs +++ b/src/librustc/mir/cache.rs @@ -35,9 +35,9 @@ impl serialize::Decodable for Cache { } } -impl<'gcx> HashStable> for Cache { +impl<'a> HashStable> for Cache { fn hash_stable(&self, - _: &mut StableHashingContext<'gcx>, + _: &mut StableHashingContext<'a>, _: &mut StableHasher) { // do nothing } diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs index f9ea544156c..51660b180cd 100644 --- a/src/librustc/mir/interpret/error.rs +++ b/src/librustc/mir/interpret/error.rs @@ -12,7 +12,7 @@ use rustc_const_math::ConstMathErr; use syntax::codemap::Span; use backtrace::Backtrace; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct EvalError<'tcx> { pub kind: EvalErrorKind<'tcx>, pub backtrace: Option, @@ -31,11 +31,11 @@ impl<'tcx> From> for EvalError<'tcx> { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum EvalErrorKind<'tcx> { /// This variant is used by machines to signal their own errors that do not /// match an existing variant - MachineError(Box), + MachineError(String), FunctionPointerTyMismatch(FnSig<'tcx>, FnSig<'tcx>), NoMirFor(String), UnterminatedCString(MemoryPointer), @@ -65,11 +65,6 @@ pub enum EvalErrorKind<'tcx> { Intrinsic(String), OverflowingMath, InvalidChar(u128), - OutOfMemory { - allocation_size: u64, - memory_size: u64, - memory_usage: u64, - }, ExecutionTimeLimitReached, StackFrameLimitReached, OutOfTls, @@ -124,6 +119,9 @@ pub enum EvalErrorKind<'tcx> { UnimplementedTraitSelection, /// Abort in case type errors are reached TypeckError, + /// Cannot compute this constant because it depends on another one + /// which already produced an error + ReferencedConstant, } pub type EvalResult<'tcx, T = ()> = Result>; @@ -132,7 +130,7 @@ impl<'tcx> Error for EvalError<'tcx> { fn description(&self) -> &str { use self::EvalErrorKind::*; match self.kind { - MachineError(ref inner) => inner.description(), + MachineError(ref inner) => inner, FunctionPointerTyMismatch(..) => "tried to call a function through a function pointer of a different type", InvalidMemoryAccess => @@ -190,10 +188,8 @@ impl<'tcx> Error for EvalError<'tcx> { "mir not found", InvalidChar(..) => "tried to interpret an invalid 32-bit value as a char", - OutOfMemory{..} => - "could not allocate more memory", ExecutionTimeLimitReached => - "reached the configured maximum execution time", + "the expression was too complex to be evaluated or resulted in an infinite loop", StackFrameLimitReached => "reached the configured maximum number of stack frames", OutOfTls => @@ -245,14 +241,8 @@ impl<'tcx> Error for EvalError<'tcx> { "there were unresolved type arguments during trait selection", TypeckError => "encountered constants with type errors, stopping evaluation", - } - } - - fn cause(&self) -> Option<&dyn Error> { - use self::EvalErrorKind::*; - match self.kind { - MachineError(ref inner) => Some(&**inner), - _ => None, + ReferencedConstant => + "referenced constant has errors", } } } @@ -294,15 +284,12 @@ 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) => write!(f, "tried to interpret an invalid 32-bit value as a char: {}", c), - OutOfMemory { allocation_size, memory_size, memory_usage } => - write!(f, "tried to allocate {} more bytes, but only {} bytes are free of the {} byte memory", - allocation_size, memory_size - memory_usage, memory_size), AlignmentCheckFailed { required, has } => write!(f, "tried to access memory with alignment {}, but alignment {} is required", has, required), @@ -313,7 +300,7 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { PathNotFound(ref path) => write!(f, "Cannot find path {:?}", path), MachineError(ref inner) => - write!(f, "machine error: {}", inner), + write!(f, "{}", inner), IncorrectAllocationInformation(size, size2, align, align2) => write!(f, "incorrect alloc info: expected size {} and align {}, got size {} and align {}", size, align, size2, align2), _ => write!(f, "{}", self.description()), diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs index a80695ec9b9..67f30f53a68 100644 --- a/src/librustc/mir/interpret/mod.rs +++ b/src/librustc/mir/interpret/mod.rs @@ -10,7 +10,7 @@ mod value; pub use self::error::{EvalError, EvalResult, EvalErrorKind}; -pub use self::value::{PrimVal, PrimValKind, Value, Pointer, bytes_to_f32, bytes_to_f64}; +pub use self::value::{PrimVal, PrimValKind, Value, Pointer}; use std::collections::BTreeMap; use std::fmt; @@ -19,6 +19,7 @@ use ty; use ty::layout::{self, Align, HasDataLayout}; use middle::region; use std::iter; +use syntax::ast::Mutability; #[derive(Clone, Debug, PartialEq)] pub enum Lock { @@ -41,7 +42,7 @@ pub enum AccessKind { } /// Uniquely identifies a specific constant or static. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, RustcEncodable, RustcDecodable)] pub struct GlobalId<'tcx> { /// For a constant or static, the `Instance` of the item itself. /// For a promoted global, the `Instance` of the function they belong to. @@ -101,7 +102,7 @@ pub trait PointerArithmetic: layout::HasDataLayout { impl PointerArithmetic for T {} -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash)] pub struct MemoryPointer { pub alloc_id: AllocId, pub offset: u64, @@ -148,13 +149,16 @@ impl<'tcx> MemoryPointer { #[derive(Copy, Clone, Default, Eq, Hash, Ord, PartialEq, PartialOrd, Debug)] pub struct AllocId(pub u64); +impl ::rustc_serialize::UseSpecializedEncodable for AllocId {} +impl ::rustc_serialize::UseSpecializedDecodable for AllocId {} + impl fmt::Display for AllocId { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) } } -#[derive(Debug, Eq, PartialEq, Hash)] +#[derive(Debug, Eq, PartialEq, Hash, RustcEncodable, RustcDecodable)] pub struct Allocation { /// The actual bytes of the allocation. /// Note that the bytes of a pointer represent the offset of the pointer @@ -166,6 +170,10 @@ pub struct Allocation { pub undef_mask: UndefMask, /// The alignment of the allocation to detect unaligned reads. pub align: Align, + /// Whether the allocation (of a static) should be put into mutable memory when translating + /// + /// Only happens for `static mut` or `static` with interior mutability + pub runtime_mutability: Mutability, } impl Allocation { @@ -177,6 +185,7 @@ impl Allocation { relocations: BTreeMap::new(), undef_mask, align: Align::from_bytes(1, 1).unwrap(), + runtime_mutability: Mutability::Immutable, } } } @@ -188,12 +197,14 @@ impl Allocation { type Block = u64; const BLOCK_SIZE: u64 = 64; -#[derive(Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Clone, Debug, Eq, PartialEq, Hash, RustcEncodable, RustcDecodable)] pub struct UndefMask { blocks: Vec, len: u64, } +impl_stable_hash_for!(struct mir::interpret::UndefMask{blocks, len}); + impl UndefMask { pub fn new(size: u64) -> Self { let mut m = UndefMask { diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc/mir/interpret/value.rs index 0bfff2a80e6..7289d74bfbb 100644 --- a/src/librustc/mir/interpret/value.rs +++ b/src/librustc/mir/interpret/value.rs @@ -1,24 +1,9 @@ #![allow(unknown_lints)] use ty::layout::{Align, HasDataLayout}; +use ty; use super::{EvalResult, MemoryPointer, PointerArithmetic}; -use syntax::ast::FloatTy; -use rustc_const_math::ConstFloat; - -pub fn bytes_to_f32(bits: u128) -> ConstFloat { - ConstFloat { - bits, - ty: FloatTy::F32, - } -} - -pub fn bytes_to_f64(bits: u128) -> ConstFloat { - ConstFloat { - bits, - ty: FloatTy::F64, - } -} /// A `Value` represents a single self-contained Rust value. /// @@ -29,13 +14,22 @@ pub fn bytes_to_f64(bits: u128) -> ConstFloat { /// For optimization of a few very common cases, there is also a representation for a pair of /// primitive values (`ByValPair`). It allows Miri to avoid making allocations for checked binary /// operations and fat pointers. This idea was taken from rustc's trans. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash)] pub enum Value { ByRef(Pointer, Align), ByVal(PrimVal), ByValPair(PrimVal, PrimVal), } +impl<'tcx> ty::TypeFoldable<'tcx> for Value { + fn super_fold_with<'gcx: 'tcx, F: ty::fold::TypeFolder<'gcx, 'tcx>>(&self, _: &mut F) -> Self { + *self + } + fn super_visit_with>(&self, _: &mut V) -> bool { + false + } +} + /// A wrapper type around `PrimVal` that cannot be turned back into a `PrimVal` accidentally. /// This type clears up a few APIs where having a `PrimVal` argument for something that is /// potentially an integer pointer or a pointer to an allocation was unclear. @@ -43,9 +37,9 @@ pub enum Value { /// I (@oli-obk) believe it is less easy to mix up generic primvals and primvals that are just /// the representation of pointers. Also all the sites that convert between primvals and pointers /// are explicit now (and rare!) -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash)] pub struct Pointer { - primval: PrimVal, + pub primval: PrimVal, } impl<'tcx> Pointer { @@ -138,7 +132,7 @@ impl ::std::convert::From for Pointer { /// `memory::Allocation`. It is in many ways like a small chunk of a `Allocation`, up to 8 bytes in /// size. Like a range of bytes in an `Allocation`, a `PrimVal` can either represent the raw bytes /// of a simple value, a pointer into another `Allocation`, or be undefined. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash)] pub enum PrimVal { /// The raw bytes of a simple value. Bytes(u128), @@ -172,10 +166,6 @@ impl<'tcx> PrimVal { PrimVal::Bytes(n as u128) } - pub fn from_float(f: ConstFloat) -> Self { - PrimVal::Bytes(f.bits) - } - pub fn from_bool(b: bool) -> Self { PrimVal::Bytes(b as u128) } @@ -250,14 +240,6 @@ impl<'tcx> PrimVal { }) } - pub fn to_f32(self) -> EvalResult<'tcx, ConstFloat> { - self.to_bytes().map(bytes_to_f32) - } - - pub fn to_f64(self) -> EvalResult<'tcx, ConstFloat> { - self.to_bytes().map(bytes_to_f64) - } - pub fn to_bool(self) -> EvalResult<'tcx, bool> { match self.to_bytes()? { 0 => Ok(false), diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 7c9feb506af..e39765699f9 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -15,7 +15,7 @@ use graphviz::IntoCow; use middle::const_val::ConstVal; use middle::region; -use rustc_const_math::{ConstUsize, ConstInt, ConstMathErr}; +use rustc_const_math::ConstMathErr; use rustc_data_structures::sync::{Lrc}; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; use rustc_data_structures::control_flow_graph::dominators::{Dominators, dominators}; @@ -25,13 +25,14 @@ use rustc_serialize as serialize; use hir::def::CtorKind; use hir::def_id::DefId; use mir::visit::MirVisitable; +use mir::interpret::{Value, PrimVal}; use ty::subst::{Subst, Substs}; use ty::{self, AdtDef, ClosureSubsts, Region, Ty, TyCtxt, GeneratorInterior}; use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; +use ty::TypeAndMut; use util::ppaux; use std::slice; use hir::{self, InlineAsm}; -use std::ascii; use std::borrow::{Cow}; use std::cell::Ref; use std::fmt::{self, Debug, Formatter, Write}; @@ -707,7 +708,7 @@ pub enum TerminatorKind<'tcx> { /// Possible values. The locations to branch to in each case /// are found in the corresponding indices from the `targets` vector. - values: Cow<'tcx, [ConstInt]>, + values: Cow<'tcx, [u128]>, /// Possible branch sites. The last element of this vector is used /// for the otherwise branch, so targets.len() == values.len() + 1 @@ -858,7 +859,7 @@ impl<'tcx> Terminator<'tcx> { impl<'tcx> TerminatorKind<'tcx> { pub fn if_<'a, 'gcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, cond: Operand<'tcx>, t: BasicBlock, f: BasicBlock) -> TerminatorKind<'tcx> { - static BOOL_SWITCH_FALSE: &'static [ConstInt] = &[ConstInt::U8(0)]; + static BOOL_SWITCH_FALSE: &'static [u128] = &[0]; TerminatorKind::SwitchInt { discr: cond, switch_ty: tcx.types.bool, @@ -1144,12 +1145,16 @@ impl<'tcx> TerminatorKind<'tcx> { match *self { Return | Resume | Abort | Unreachable | GeneratorDrop => vec![], Goto { .. } => vec!["".into()], - SwitchInt { ref values, .. } => { + SwitchInt { ref values, switch_ty, .. } => { values.iter() - .map(|const_val| { - let mut buf = String::new(); - fmt_const_val(&mut buf, &ConstVal::Integral(*const_val)).unwrap(); - buf.into() + .map(|&u| { + let mut s = String::new(); + print_miri_value( + Value::ByVal(PrimVal::Bytes(u)), + switch_ty, + &mut s, + ).unwrap(); + s.into() }) .chain(iter::once(String::from("otherwise").into())) .collect() @@ -1533,7 +1538,8 @@ impl<'tcx> Operand<'tcx> { ty, literal: Literal::Value { value: tcx.mk_const(ty::Const { - val: ConstVal::Function(def_id, substs), + // ZST function type + val: ConstVal::Value(Value::ByVal(PrimVal::Undef)), ty }) }, @@ -1557,7 +1563,7 @@ pub enum Rvalue<'tcx> { Use(Operand<'tcx>), /// [x; 32] - Repeat(Operand<'tcx>, ConstUsize), + Repeat(Operand<'tcx>, u64), /// &x or &mut x Ref(Region<'tcx>, BorrowKind, Place<'tcx>), @@ -1853,7 +1859,7 @@ impl<'tcx> Debug for Literal<'tcx> { match *self { Value { value } => { write!(fmt, "const ")?; - fmt_const_val(fmt, &value.val) + fmt_const_val(fmt, value) } Promoted { index } => { write!(fmt, "{:?}", index) @@ -1863,25 +1869,47 @@ impl<'tcx> Debug for Literal<'tcx> { } /// Write a `ConstVal` in a way closer to the original source code than the `Debug` output. -fn fmt_const_val(fmt: &mut W, const_val: &ConstVal) -> fmt::Result { +fn fmt_const_val(fmt: &mut W, const_val: &ty::Const) -> fmt::Result { use middle::const_val::ConstVal::*; - match *const_val { - Float(f) => write!(fmt, "{:?}", f), - Integral(n) => write!(fmt, "{}", n), - Str(s) => write!(fmt, "{:?}", s), - ByteStr(bytes) => { - let escaped: String = bytes.data - .iter() - .flat_map(|&ch| ascii::escape_default(ch).map(|c| c as char)) - .collect(); - write!(fmt, "b\"{}\"", escaped) - } - Bool(b) => write!(fmt, "{:?}", b), - Char(c) => write!(fmt, "{:?}", c), - Variant(def_id) | - Function(def_id, _) => write!(fmt, "{}", item_path_str(def_id)), - Aggregate(_) => bug!("`ConstVal::{:?}` should not be in MIR", const_val), - Unevaluated(..) => write!(fmt, "{:?}", const_val) + match const_val.val { + Unevaluated(..) => write!(fmt, "{:?}", const_val), + Value(val) => print_miri_value(val, const_val.ty, fmt), + } +} + +pub fn print_miri_value(value: Value, ty: Ty, f: &mut W) -> fmt::Result { + use ty::TypeVariants::*; + use rustc_const_math::ConstFloat; + match (value, &ty.sty) { + (Value::ByVal(PrimVal::Bytes(0)), &TyBool) => write!(f, "false"), + (Value::ByVal(PrimVal::Bytes(1)), &TyBool) => write!(f, "true"), + (Value::ByVal(PrimVal::Bytes(bits)), &TyFloat(fty)) => + write!(f, "{}", ConstFloat { bits, ty: fty }), + (Value::ByVal(PrimVal::Bytes(n)), &TyUint(ui)) => write!(f, "{:?}{}", n, ui), + (Value::ByVal(PrimVal::Bytes(n)), &TyInt(i)) => write!(f, "{:?}{}", n as i128, i), + (Value::ByVal(PrimVal::Bytes(n)), &TyChar) => + write!(f, "{:?}", ::std::char::from_u32(n as u32).unwrap()), + (Value::ByVal(PrimVal::Undef), &TyFnDef(did, _)) => + write!(f, "{}", item_path_str(did)), + (Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(len)), &TyRef(_, TypeAndMut { + ty: &ty::TyS { sty: TyStr, .. }, .. + })) => { + ty::tls::with(|tcx| { + let alloc = tcx + .interpret_interner + .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), } } @@ -2467,6 +2495,15 @@ impl<'tcx, B, V, T> TypeFoldable<'tcx> for Projection<'tcx, B, V, T> } } +impl<'tcx> TypeFoldable<'tcx> for Field { + fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, _: &mut F) -> Self { + *self + } + fn super_visit_with>(&self, _: &mut V) -> bool { + false + } +} + impl<'tcx> TypeFoldable<'tcx> for Constant<'tcx> { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { Constant { diff --git a/src/librustc/mir/mono.rs b/src/librustc/mir/mono.rs index d8eac2b4159..d01059a3e01 100644 --- a/src/librustc/mir/mono.rs +++ b/src/librustc/mir/mono.rs @@ -41,9 +41,9 @@ impl<'tcx> MonoItem<'tcx> { } } -impl<'tcx> HashStable> for MonoItem<'tcx> { +impl<'a, 'tcx> HashStable> for MonoItem<'tcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'tcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { ::std::mem::discriminant(self).hash_stable(hcx, hasher); @@ -171,9 +171,9 @@ impl<'tcx> CodegenUnit<'tcx> { } } -impl<'tcx> HashStable> for CodegenUnit<'tcx> { +impl<'a, 'tcx> HashStable> for CodegenUnit<'tcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'tcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let CodegenUnit { ref items, diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index bbfb9c89b3f..7d232ac20bf 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -70,7 +70,7 @@ impl<'a, 'gcx, 'tcx> PlaceTy<'tcx> { PlaceTy::Ty { ty: match ty.sty { ty::TyArray(inner, size) => { - let size = size.val.to_const_int().unwrap().to_u64().unwrap(); + let size = size.val.unwrap_u64(); let len = size - (from as u64) - (to as u64); tcx.mk_array(inner, len) } @@ -149,7 +149,7 @@ impl<'tcx> Rvalue<'tcx> { match *self { Rvalue::Use(ref operand) => operand.ty(local_decls, tcx), Rvalue::Repeat(ref operand, count) => { - tcx.mk_array_const_usize(operand.ty(local_decls, tcx), count) + tcx.mk_array(operand.ty(local_decls, tcx), count) } Rvalue::Ref(reg, bk, ref place) => { let place_ty = place.ty(local_decls, tcx).to_ty(tcx); diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 0b6f1275bdb..650af8dc4d9 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -12,7 +12,6 @@ use hir::def_id::DefId; use ty::subst::Substs; use ty::{ClosureSubsts, Region, Ty, GeneratorInterior}; use mir::*; -use rustc_const_math::ConstUsize; use syntax_pos::Span; // # The MIR Visitor @@ -243,18 +242,6 @@ macro_rules! make_mir_visitor { self.super_generator_interior(interior); } - fn visit_const_int(&mut self, - const_int: &ConstInt, - _: Location) { - self.super_const_int(const_int); - } - - fn visit_const_usize(&mut self, - const_usize: & $($mutability)* ConstUsize, - _: Location) { - self.super_const_usize(const_usize); - } - fn visit_local_decl(&mut self, local: Local, local_decl: & $($mutability)* LocalDecl<'tcx>) { @@ -426,13 +413,10 @@ macro_rules! make_mir_visitor { TerminatorKind::SwitchInt { ref $($mutability)* discr, ref $($mutability)* switch_ty, - ref values, + values: _, ref targets } => { self.visit_operand(discr, source_location); self.visit_ty(switch_ty, TyContext::Location(source_location)); - for value in &values[..] { - self.visit_const_int(value, source_location); - } for &target in targets { self.visit_branch(block, target); } @@ -538,10 +522,8 @@ macro_rules! make_mir_visitor { self.visit_operand(operand, location); } - Rvalue::Repeat(ref $($mutability)* value, - ref $($mutability)* length) => { + Rvalue::Repeat(ref $($mutability)* value, _) => { self.visit_operand(value, location); - self.visit_const_usize(length, location); } Rvalue::Ref(ref $($mutability)* r, bk, ref $($mutability)* path) => { @@ -798,12 +780,6 @@ macro_rules! make_mir_visitor { _substs: & $($mutability)* ClosureSubsts<'tcx>) { } - fn super_const_int(&mut self, _const_int: &ConstInt) { - } - - fn super_const_usize(&mut self, _const_usize: & $($mutability)* ConstUsize) { - } - // Convenience methods fn visit_location(&mut self, mir: & $($mutability)* Mir<'tcx>, location: Location) { diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 157614f847a..437369400ed 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -179,10 +179,10 @@ impl_stable_hash_for!(enum self::OutputType { DepInfo }); -impl<'tcx> ToStableHashKey> for OutputType { +impl<'a, 'tcx> ToStableHashKey> for OutputType { type KeyType = OutputType; #[inline] - fn to_stable_hash_key(&self, _: &StableHashingContext<'tcx>) -> Self::KeyType { + fn to_stable_hash_key(&self, _: &StableHashingContext<'a>) -> Self::KeyType { *self } } diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 5e9eeb97300..defc5731f2f 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -103,6 +103,11 @@ pub struct Session { /// The maximum length of types during monomorphization. pub type_length_limit: Cell, + /// The maximum number of stackframes allowed in const eval + pub const_eval_stack_frame_limit: Cell, + /// The maximum number miri steps per constant + pub const_eval_step_limit: Cell, + /// The metadata::creader module may inject an allocator/panic_runtime /// dependency if it didn't already find one, and this tracks what was /// injected. @@ -1004,6 +1009,8 @@ pub fn build_session_(sopts: config::Options, features: RefCell::new(None), recursion_limit: Cell::new(64), type_length_limit: Cell::new(1048576), + const_eval_stack_frame_limit: Cell::new(100), + const_eval_step_limit: Cell::new(1_000_000), next_node_id: Cell::new(NodeId::new(1)), injected_allocator: Cell::new(None), allocator_kind: Cell::new(None), diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index ce23cb23496..cd2d0d7e2a0 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 2f3e19d92bc..bb2c7977f26 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -9,12 +9,14 @@ // except according to those terms. use infer::{RegionObligation, InferCtxt}; +use mir::interpret::GlobalId; use ty::{self, Ty, TypeFoldable, ToPolyTraitRef, ToPredicate}; use ty::error::ExpectedFound; 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; @@ -514,17 +516,35 @@ fn process_predicate<'a, 'gcx, 'tcx>( } Some(param_env) => { match selcx.tcx().lift_to_global(&substs) { + Some(substs) => { + let instance = ty::Instance::resolve( + selcx.tcx().global_tcx(), + param_env, + def_id, + substs, + ); + if let Some(instance) = instance { + let cid = GlobalId { + instance, + promoted: None, + }; + match selcx.tcx().at(obligation.cause.span) + .const_eval(param_env.and(cid)) { + Ok(_) => Ok(Some(vec![])), + Err(err) => Err(CodeSelectionError(ConstEvalFailure(err))) + } + } else { + Err(CodeSelectionError(ConstEvalFailure(ConstEvalErr { + span: obligation.cause.span, + kind: ErrKind::UnimplementedConstVal("could not resolve") + .into(), + }))) + } + }, None => { pending_obligation.stalled_on = substs.types().collect(); Ok(None) } - Some(substs) => { - match selcx.tcx().at(obligation.cause.span) - .const_eval(param_env.and((def_id, substs))) { - Ok(_) => Ok(Some(vec![])), - Err(e) => Err(CodeSelectionError(ConstEvalFailure(e))) - } - } } } } diff --git a/src/librustc/traits/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/traits/project.rs b/src/librustc/traits/project.rs index 3fe72344e8f..a9dc4915743 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -29,6 +29,7 @@ use hir::def_id::DefId; use infer::{InferCtxt, InferOk}; use infer::type_variable::TypeVariableOrigin; use middle::const_val::ConstVal; +use mir::interpret::{GlobalId}; use rustc_data_structures::snapshot_map::{Snapshot, SnapshotMap}; use syntax::symbol::Symbol; use ty::subst::{Subst, Substs}; @@ -400,12 +401,17 @@ impl<'a, 'b, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for AssociatedTypeNormalizer<'a, fn fold_const(&mut self, constant: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { if let ConstVal::Unevaluated(def_id, substs) = constant.val { - if substs.needs_infer() { - let identity_substs = Substs::identity_for_item(self.tcx(), def_id); - let data = self.param_env.and((def_id, identity_substs)); - match self.tcx().lift_to_global(&data) { - Some(data) => { - match self.tcx().const_eval(data) { + let tcx = self.selcx.tcx().global_tcx(); + if let Some(param_env) = self.tcx().lift_to_global(&self.param_env) { + if substs.needs_infer() { + let identity_substs = Substs::identity_for_item(tcx, def_id); + let instance = ty::Instance::resolve(tcx, param_env, def_id, identity_substs); + if let Some(instance) = instance { + let cid = GlobalId { + instance, + promoted: None + }; + match tcx.const_eval(param_env.and(cid)) { Ok(evaluated) => { let evaluated = evaluated.subst(self.tcx(), substs); return self.fold_const(evaluated); @@ -413,18 +419,20 @@ impl<'a, 'b, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for AssociatedTypeNormalizer<'a, Err(_) => {} } } - None => {} - } - } else { - let data = self.param_env.and((def_id, substs)); - match self.tcx().lift_to_global(&data) { - Some(data) => { - match self.tcx().const_eval(data) { - Ok(evaluated) => return self.fold_const(evaluated), - Err(_) => {} + } else { + if let Some(substs) = self.tcx().lift_to_global(&substs) { + let instance = ty::Instance::resolve(tcx, param_env, def_id, substs); + if let Some(instance) = instance { + let cid = GlobalId { + instance, + promoted: None + }; + match tcx.const_eval(param_env.and(cid)) { + Ok(evaluated) => return self.fold_const(evaluated), + Err(_) => {} + } } } - None => {} } } } diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 600b4a515f0..91d86394b01 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -42,6 +42,7 @@ use ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable}; use ty::fast_reject; use ty::relate::TypeRelation; use middle::lang_items; +use mir::interpret::{GlobalId}; use rustc_data_structures::bitvec::BitVector; use std::iter; @@ -732,11 +733,26 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { } ty::Predicate::ConstEvaluatable(def_id, substs) => { - match self.tcx().lift_to_global(&(obligation.param_env, substs)) { + let tcx = self.tcx(); + match tcx.lift_to_global(&(obligation.param_env, substs)) { Some((param_env, substs)) => { - match self.tcx().const_eval(param_env.and((def_id, substs))) { - Ok(_) => EvaluatedToOk, - Err(_) => EvaluatedToErr + let instance = ty::Instance::resolve( + tcx.global_tcx(), + param_env, + def_id, + substs, + ); + if let Some(instance) = instance { + let cid = GlobalId { + instance, + promoted: None + }; + match self.tcx().const_eval(param_env.and(cid)) { + Ok(_) => EvaluatedToOk, + Err(_) => EvaluatedToErr + } + } else { + EvaluatedToErr } } None => { diff --git a/src/librustc/traits/specialize/specialization_graph.rs b/src/librustc/traits/specialize/specialization_graph.rs index dbf15ad1707..f8b895177f3 100644 --- a/src/librustc/traits/specialize/specialization_graph.rs +++ b/src/librustc/traits/specialize/specialization_graph.rs @@ -391,9 +391,9 @@ pub fn ancestors(tcx: TyCtxt, } } -impl<'gcx> HashStable> for Children { +impl<'a> HashStable> for Children { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let Children { ref nonblanket_impls, diff --git a/src/librustc/ty/codec.rs b/src/librustc/ty/codec.rs index fbb14f39ade..f98bc953560 100644 --- a/src/librustc/ty/codec.rs +++ b/src/librustc/ty/codec.rs @@ -17,7 +17,6 @@ // persisting to incr. comp. caches. use hir::def_id::{DefId, CrateNum}; -use middle::const_val::ByteArray; use rustc_data_structures::fx::FxHashMap; use rustc_serialize::{Decodable, Decoder, Encoder, Encodable, opaque}; use std::hash::Hash; @@ -240,17 +239,6 @@ pub fn decode_existential_predicate_slice<'a, 'tcx, D>(decoder: &mut D) .mk_existential_predicates((0..len).map(|_| Decodable::decode(decoder)))?) } -#[inline] -pub fn decode_byte_array<'a, 'tcx, D>(decoder: &mut D) - -> Result, D::Error> - where D: TyDecoder<'a, 'tcx>, - 'tcx: 'a, -{ - Ok(ByteArray { - data: decoder.tcx().alloc_byte_array(&Vec::decode(decoder)?) - }) -} - #[inline] pub fn decode_const<'a, 'tcx, D>(decoder: &mut D) -> Result<&'tcx ty::Const<'tcx>, D::Error> @@ -278,7 +266,6 @@ macro_rules! implement_ty_decoder { use $crate::ty::codec::*; use $crate::ty::subst::Substs; use $crate::hir::def_id::{CrateNum}; - use $crate::middle::const_val::ByteArray; use rustc_serialize::{Decoder, SpecializedDecoder}; use std::borrow::Cow; @@ -377,13 +364,6 @@ macro_rules! implement_ty_decoder { } } - impl<$($typaram),*> SpecializedDecoder> - for $DecoderName<$($typaram),*> { - fn specialized_decode(&mut self) -> Result, Self::Error> { - decode_byte_array(self) - } - } - impl<$($typaram),*> SpecializedDecoder<&'tcx $crate::ty::Const<'tcx>> for $DecoderName<$($typaram),*> { fn specialized_decode(&mut self) -> Result<&'tcx ty::Const<'tcx>, Self::Error> { diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 47a3580e867..b760649c37d 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -30,7 +30,8 @@ use middle::cstore::EncodedMetadata; use middle::lang_items; use middle::resolve_lifetime::{self, ObjectLifetimeDefault}; use middle::stability; -use mir::{Mir, interpret}; +use mir::{self, Mir, interpret}; +use mir::interpret::{Value, PrimVal}; use ty::subst::{Kind, Substs}; use ty::ReprOptions; use ty::Instance; @@ -53,7 +54,6 @@ use rustc_data_structures::stable_hasher::{HashStable, hash_stable_hashmap, StableHasher, StableHasherResult, StableVec}; use arena::{TypedArena, DroplessArena}; -use rustc_const_math::{ConstInt, ConstUsize}; use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::sync::Lrc; use std::any::Any; @@ -675,9 +675,9 @@ impl<'tcx> TypeckTables<'tcx> { } } -impl<'gcx> HashStable> for TypeckTables<'gcx> { +impl<'a, 'gcx> HashStable> for TypeckTables<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ty::TypeckTables { local_id_root, @@ -868,7 +868,7 @@ pub struct GlobalCtxt<'tcx> { stability_interner: RefCell>, - pub interpret_interner: RefCell>, + pub interpret_interner: InterpretInterner<'tcx>, layout_interner: RefCell>, @@ -892,6 +892,11 @@ pub struct GlobalCtxt<'tcx> { /// Everything needed to efficiently work with interned allocations #[derive(Debug, Default)] pub struct InterpretInterner<'tcx> { + inner: RefCell>, +} + +#[derive(Debug, Default)] +struct InterpretInternerInner<'tcx> { /// Stores the value of constants (and deduplicates the actual memory) allocs: FxHashSet<&'tcx interpret::Allocation>, @@ -905,12 +910,18 @@ pub struct InterpretInterner<'tcx> { /// Allows obtaining const allocs via a unique identifier alloc_by_id: FxHashMap, + /// Reverse map of `alloc_cache` + 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. @@ -918,14 +929,15 @@ pub struct InterpretInterner<'tcx> { } impl<'tcx> InterpretInterner<'tcx> { - pub fn create_fn_alloc(&mut self, instance: Instance<'tcx>) -> interpret::AllocId { - if let Some(&alloc_id) = self.function_cache.get(&instance) { + pub fn create_fn_alloc(&self, instance: Instance<'tcx>) -> interpret::AllocId { + if let Some(&alloc_id) = self.inner.borrow().function_cache.get(&instance) { return alloc_id; } let id = self.reserve(); debug!("creating fn ptr: {}", id); - self.functions.insert(id, instance); - self.function_cache.insert(instance, id); + let mut inner = self.inner.borrow_mut(); + inner.functions.insert(id, instance); + inner.function_cache.insert(instance, id); id } @@ -933,39 +945,48 @@ impl<'tcx> InterpretInterner<'tcx> { &self, id: interpret::AllocId, ) -> Option> { - self.functions.get(&id).cloned() + self.inner.borrow().functions.get(&id).cloned() } pub fn get_alloc( &self, id: interpret::AllocId, ) -> Option<&'tcx interpret::Allocation> { - self.alloc_by_id.get(&id).cloned() + self.inner.borrow().alloc_by_id.get(&id).cloned() } pub fn get_cached( &self, - global_id: interpret::GlobalId<'tcx>, + static_id: DefId, ) -> Option { - self.alloc_cache.get(&global_id).cloned() + self.inner.borrow().alloc_cache.get(&static_id).cloned() } pub fn cache( - &mut self, - global_id: interpret::GlobalId<'tcx>, - ptr: interpret::AllocId, + &self, + static_id: DefId, + alloc_id: interpret::AllocId, ) { - if let Some(old) = self.alloc_cache.insert(global_id, ptr) { - bug!("tried to cache {:?}, but was already existing as {:#?}", global_id, old); + let mut inner = self.inner.borrow_mut(); + inner.global_cache.insert(alloc_id, static_id); + if let Some(old) = inner.alloc_cache.insert(static_id, alloc_id) { + bug!("tried to cache {:?}, but was already existing as {:#?}", static_id, old); } } + pub fn get_corresponding_static_def_id( + &self, + ptr: interpret::AllocId, + ) -> Option { + self.inner.borrow().global_cache.get(&ptr).cloned() + } + pub fn intern_at_reserved( - &mut self, + &self, id: interpret::AllocId, alloc: &'tcx interpret::Allocation, ) { - if let Some(old) = self.alloc_by_id.insert(id, alloc) { + if let Some(old) = self.inner.borrow_mut().alloc_by_id.insert(id, alloc) { bug!("tried to intern allocation at {}, but was already existing as {:#?}", id, old); } } @@ -973,10 +994,11 @@ impl<'tcx> InterpretInterner<'tcx> { /// obtains a new allocation ID that can be referenced but does not /// yet have an allocation backing it. pub fn reserve( - &mut self, + &self, ) -> interpret::AllocId { - let next = self.next_id; - self.next_id.0 = self.next_id.0 + let mut inner = self.inner.borrow_mut(); + let next = inner.next_id; + inner.next_id.0 = inner.next_id.0 .checked_add(1) .expect("You overflowed a u64 by incrementing by 1... \ You've just earned yourself a free drink if we ever meet. \ @@ -1056,12 +1078,12 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self, alloc: interpret::Allocation, ) -> &'gcx interpret::Allocation { - if let Some(alloc) = self.interpret_interner.borrow().allocs.get(&alloc) { + if let Some(alloc) = self.interpret_interner.inner.borrow().allocs.get(&alloc) { return alloc; } let interned = self.global_arenas.const_allocs.alloc(alloc); - if let Some(prev) = self.interpret_interner.borrow_mut().allocs.replace(interned) { + if let Some(prev) = self.interpret_interner.inner.borrow_mut().allocs.replace(interned) { bug!("Tried to overwrite interned Allocation: {:#?}", prev) } interned @@ -1070,20 +1092,20 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { /// Allocates a byte or string literal for `mir::interpret` pub fn allocate_cached(self, bytes: &[u8]) -> interpret::AllocId { // check whether we already allocated this literal or a constant with the same memory - if let Some(&alloc_id) = self.interpret_interner.borrow().literal_alloc_cache.get(bytes) { + if let Some(&alloc_id) = self.interpret_interner.inner.borrow() + .literal_alloc_cache.get(bytes) { return alloc_id; } // create an allocation that just contains these bytes let alloc = interpret::Allocation::from_bytes(bytes); let alloc = self.intern_const_alloc(alloc); - let mut int = self.interpret_interner.borrow_mut(); // the next unique id - let id = int.reserve(); + let id = self.interpret_interner.reserve(); // make the allocation identifiable - int.alloc_by_id.insert(id, alloc); + self.interpret_interner.inner.borrow_mut().alloc_by_id.insert(id, alloc); // cache it for the future - int.literal_alloc_cache.insert(bytes.to_owned(), id); + self.interpret_interner.inner.borrow_mut().literal_alloc_cache.insert(bytes.to_owned(), id); id } @@ -1248,6 +1270,40 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self.get_lang_items(LOCAL_CRATE) } + /// Due to missing llvm support for lowering 128 bit math to software emulation + /// (on some targets), the lowering can be done in MIR. + /// + /// This function only exists until said support is implemented. + pub fn is_binop_lang_item(&self, def_id: DefId) -> Option<(mir::BinOp, bool)> { + let items = self.lang_items(); + let def_id = Some(def_id); + if items.i128_add_fn() == def_id { Some((mir::BinOp::Add, false)) } + else if items.u128_add_fn() == def_id { Some((mir::BinOp::Add, false)) } + else if items.i128_sub_fn() == def_id { Some((mir::BinOp::Sub, false)) } + else if items.u128_sub_fn() == def_id { Some((mir::BinOp::Sub, false)) } + else if items.i128_mul_fn() == def_id { Some((mir::BinOp::Mul, false)) } + else if items.u128_mul_fn() == def_id { Some((mir::BinOp::Mul, false)) } + else if items.i128_div_fn() == def_id { Some((mir::BinOp::Div, false)) } + else if items.u128_div_fn() == def_id { Some((mir::BinOp::Div, false)) } + else if items.i128_rem_fn() == def_id { Some((mir::BinOp::Rem, false)) } + else if items.u128_rem_fn() == def_id { Some((mir::BinOp::Rem, false)) } + else if items.i128_shl_fn() == def_id { Some((mir::BinOp::Shl, false)) } + else if items.u128_shl_fn() == def_id { Some((mir::BinOp::Shl, false)) } + else if items.i128_shr_fn() == def_id { Some((mir::BinOp::Shr, false)) } + else if items.u128_shr_fn() == def_id { Some((mir::BinOp::Shr, false)) } + else if items.i128_addo_fn() == def_id { Some((mir::BinOp::Add, true)) } + else if items.u128_addo_fn() == def_id { Some((mir::BinOp::Add, true)) } + else if items.i128_subo_fn() == def_id { Some((mir::BinOp::Sub, true)) } + else if items.u128_subo_fn() == def_id { Some((mir::BinOp::Sub, true)) } + else if items.i128_mulo_fn() == def_id { Some((mir::BinOp::Mul, true)) } + else if items.u128_mulo_fn() == def_id { Some((mir::BinOp::Mul, true)) } + else if items.i128_shlo_fn() == def_id { Some((mir::BinOp::Shl, true)) } + else if items.u128_shlo_fn() == def_id { Some((mir::BinOp::Shl, true)) } + else if items.i128_shro_fn() == def_id { Some((mir::BinOp::Shr, true)) } + else if items.u128_shro_fn() == def_id { Some((mir::BinOp::Shr, true)) } + else { None } + } + pub fn stability(self) -> Lrc> { self.stability_index(LOCAL_CRATE) } @@ -1321,7 +1377,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self.cstore.crate_data_as_rc_any(cnum) } - pub fn create_stable_hashing_context(self) -> StableHashingContext<'gcx> { + pub fn create_stable_hashing_context(self) -> StableHashingContext<'a> { let krate = self.dep_graph.with_ignore(|| self.gcx.hir.krate()); StableHashingContext::new(self.sess, @@ -1731,7 +1787,7 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { println!("Substs interner: #{}", self.interners.substs.borrow().len()); println!("Region interner: #{}", self.interners.region.borrow().len()); println!("Stability interner: #{}", self.stability_interner.borrow().len()); - println!("Interpret interner: #{}", self.interpret_interner.borrow().allocs.len()); + println!("Interpret interner: #{}", self.interpret_interner.inner.borrow().allocs.len()); println!("Layout interner: #{}", self.layout_interner.borrow().len()); } } @@ -2043,13 +2099,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } pub fn mk_array(self, ty: Ty<'tcx>, n: u64) -> Ty<'tcx> { - let n = ConstUsize::new(n, self.sess.target.usize_ty).unwrap(); - self.mk_array_const_usize(ty, n) - } - - pub fn mk_array_const_usize(self, ty: Ty<'tcx>, n: ConstUsize) -> Ty<'tcx> { self.mk_ty(TyArray(ty, self.mk_const(ty::Const { - val: ConstVal::Integral(ConstInt::Usize(n)), + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(n.into()))), ty: self.types.usize }))) } diff --git a/src/librustc/ty/error.rs b/src/librustc/ty/error.rs index be89aeebdea..dcb70a8f86a 100644 --- a/src/librustc/ty/error.rs +++ b/src/librustc/ty/error.rs @@ -9,17 +9,13 @@ // except according to those terms. use hir::def_id::DefId; -use middle::const_val::ConstVal; use ty::{self, BoundRegion, Region, Ty, TyCtxt}; - use std::fmt; use syntax::abi; use syntax::ast; use errors::DiagnosticBuilder; use syntax_pos::Span; -use rustc_const_math::ConstInt; - use hir; #[derive(Clone, Copy, Debug)] @@ -186,10 +182,9 @@ impl<'a, 'gcx, 'lcx, 'tcx> ty::TyS<'tcx> { ty::TyAdt(def, _) => format!("{} `{}`", def.descr(), tcx.item_path_str(def.did)), ty::TyForeign(def_id) => format!("extern type `{}`", tcx.item_path_str(def_id)), ty::TyArray(_, n) => { - if let ConstVal::Integral(ConstInt::Usize(n)) = n.val { - format!("array of {} elements", n) - } else { - "array".to_string() + match n.val.to_raw_bits() { + Some(n) => format!("array of {} elements", n), + None => "array".to_string(), } } ty::TySlice(_) => "slice".to_string(), diff --git a/src/librustc/ty/fast_reject.rs b/src/librustc/ty/fast_reject.rs index 97c259e6bf3..93d8a4d979d 100644 --- a/src/librustc/ty/fast_reject.rs +++ b/src/librustc/ty/fast_reject.rs @@ -154,12 +154,12 @@ impl SimplifiedTypeGen { } } -impl<'gcx, D> HashStable> for SimplifiedTypeGen +impl<'a, 'gcx, D> HashStable> for SimplifiedTypeGen where D: Copy + Debug + Ord + Eq + Hash + - HashStable>, + HashStable>, { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { diff --git a/src/librustc/ty/flags.rs b/src/librustc/ty/flags.rs index 2889322a1ce..f067789771c 100644 --- a/src/librustc/ty/flags.rs +++ b/src/librustc/ty/flags.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use middle::const_val::{ConstVal, ConstAggregate}; +use middle::const_val::ConstVal; use ty::subst::Substs; use ty::{self, Ty, TypeFlags, TypeFoldable}; @@ -218,30 +218,7 @@ impl FlagComputation { fn add_const(&mut self, constant: &ty::Const) { self.add_ty(constant.ty); match constant.val { - ConstVal::Integral(_) | - ConstVal::Float(_) | - ConstVal::Str(_) | - ConstVal::ByteStr(_) | - ConstVal::Bool(_) | - ConstVal::Char(_) | - ConstVal::Variant(_) => {} - ConstVal::Function(_, substs) => { - self.add_substs(substs); - } - ConstVal::Aggregate(ConstAggregate::Struct(fields)) => { - for &(_, v) in fields { - self.add_const(v); - } - } - ConstVal::Aggregate(ConstAggregate::Tuple(fields)) | - ConstVal::Aggregate(ConstAggregate::Array(fields)) => { - for v in fields { - self.add_const(v); - } - } - ConstVal::Aggregate(ConstAggregate::Repeat(v, _)) => { - self.add_const(v); - } + ConstVal::Value(_) => {} ConstVal::Unevaluated(_, substs) => { self.add_flags(TypeFlags::HAS_PROJECTION); self.add_substs(substs); diff --git a/src/librustc/ty/inhabitedness/mod.rs b/src/librustc/ty/inhabitedness/mod.rs index 93e4cd9adf8..3e653cf126a 100644 --- a/src/librustc/ty/inhabitedness/mod.rs +++ b/src/librustc/ty/inhabitedness/mod.rs @@ -262,7 +262,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { })) }, TyArray(ty, len) => { - match len.val.to_const_int().and_then(|i| i.to_u64()) { + match len.val.to_raw_bits() { // If the array is definitely non-empty, it's uninhabited if // the type of its elements is uninhabited. Some(n) if n != 0 => ty.uninhabited_from(visited, tcx), diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs index a5f0abb9bc0..614158bafa6 100644 --- a/src/librustc/ty/instance.rs +++ b/src/librustc/ty/instance.rs @@ -17,13 +17,13 @@ use util::ppaux; use std::fmt; -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] pub struct Instance<'tcx> { pub def: InstanceDef<'tcx>, pub substs: &'tcx Substs<'tcx>, } -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] pub enum InstanceDef<'tcx> { Item(DefId), Intrinsic(DefId), diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 5069c595626..1b919ad68d0 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -342,7 +342,7 @@ impl AddAssign for Size { /// Each field is a power of two, giving the alignment a maximum /// value of 2(28 - 1), which is limited by LLVM to a i32, with /// a maximum capacity of 231 - 1 or 2147483647. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] pub struct Align { abi: u8, pref: u8, @@ -794,6 +794,17 @@ impl Abi { Abi::Aggregate { sized } => !sized } } + + /// Returns true if this is a single signed integer scalar + pub fn is_signed(&self) -> bool { + match *self { + Abi::Scalar(ref scal) => match scal.value { + Primitive::Int(_, signed) => signed, + _ => false, + }, + _ => false, + } + } } #[derive(PartialEq, Eq, Hash, Debug)] @@ -1237,7 +1248,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { } let element = self.layout_of(element)?; - let count = count.val.to_const_int().unwrap().to_u64().unwrap(); + let count = count.val.unwrap_u64(); let size = element.size.checked_mul(count, dl) .ok_or(LayoutError::SizeOverflow(ty))?; @@ -1537,7 +1548,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { if variants[i].iter().any(|f| f.abi == Abi::Uninhabited) { continue; } - let x = discr.to_u128_unchecked() as i128; + let x = discr.val as i128; if x < min { min = x; } if x > max { max = x; } } @@ -2369,9 +2380,9 @@ impl<'a, 'tcx> TyLayout<'tcx> { } } -impl<'gcx> HashStable> for Variants { +impl<'a> HashStable> for Variants { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use ty::layout::Variants::*; mem::discriminant(self).hash_stable(hcx, hasher); @@ -2405,9 +2416,9 @@ impl<'gcx> HashStable> for Variants { } } -impl<'gcx> HashStable> for FieldPlacement { +impl<'a> HashStable> for FieldPlacement { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use ty::layout::FieldPlacement::*; mem::discriminant(self).hash_stable(hcx, hasher); @@ -2428,9 +2439,9 @@ impl<'gcx> HashStable> for FieldPlacement { } } -impl<'gcx> HashStable> for Abi { +impl<'a> HashStable> for Abi { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use ty::layout::Abi::*; mem::discriminant(self).hash_stable(hcx, hasher); @@ -2455,9 +2466,9 @@ impl<'gcx> HashStable> for Abi { } } -impl<'gcx> HashStable> for Scalar { +impl<'a> HashStable> for Scalar { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let Scalar { value, valid_range: RangeInclusive { start, end } } = *self; value.hash_stable(hcx, hasher); @@ -2498,10 +2509,10 @@ impl_stable_hash_for!(struct ::ty::layout::Size { raw }); -impl<'gcx> HashStable> for LayoutError<'gcx> +impl<'a, 'gcx> HashStable> for LayoutError<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use ty::layout::LayoutError::*; mem::discriminant(self).hash_stable(hcx, hasher); diff --git a/src/librustc/ty/maps/config.rs b/src/librustc/ty/maps/config.rs index d880b022e2f..21affcbc9ed 100644 --- a/src/librustc/ty/maps/config.rs +++ b/src/librustc/ty/maps/config.rs @@ -10,9 +10,10 @@ use dep_graph::SerializedDepNodeIndex; use hir::def_id::{CrateNum, DefId, DefIndex}; +use mir::interpret::{GlobalId}; use ty::{self, Ty, TyCtxt}; -use ty::maps::queries; use ty::subst::Substs; +use ty::maps::queries; use std::hash::Hash; use syntax_pos::symbol::InternedString; @@ -152,8 +153,8 @@ impl<'tcx> QueryDescription<'tcx> for queries::reachable_set<'tcx> { } impl<'tcx> QueryDescription<'tcx> for queries::const_eval<'tcx> { - fn describe(tcx: TyCtxt, key: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>) -> String { - format!("const-evaluating `{}`", tcx.item_path_str(key.value.0)) + fn describe(tcx: TyCtxt, key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) -> String { + format!("const-evaluating `{}`", tcx.item_path_str(key.value.instance.def.def_id())) } } diff --git a/src/librustc/ty/maps/keys.rs b/src/librustc/ty/maps/keys.rs index b7b64c9761a..8fb1ad0da82 100644 --- a/src/librustc/ty/maps/keys.rs +++ b/src/librustc/ty/maps/keys.rs @@ -14,6 +14,7 @@ use hir::def_id::{CrateNum, DefId, LOCAL_CRATE, DefIndex}; use ty::{self, Ty, TyCtxt}; use ty::subst::Substs; use ty::fast_reject::SimplifiedType; +use mir; use std::fmt::Debug; use std::hash::Hash; @@ -52,6 +53,16 @@ impl<'tcx> Key for ty::Instance<'tcx> { } } +impl<'tcx> Key for mir::interpret::GlobalId<'tcx> { + fn map_crate(&self) -> CrateNum { + self.instance.map_crate() + } + + fn default_span(&self, tcx: TyCtxt) -> Span { + self.instance.default_span(tcx) + } +} + impl Key for CrateNum { fn map_crate(&self) -> CrateNum { *self diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs index 43a71f1c0d3..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,8 +26,10 @@ 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}; use session::{CompileResult, CrateDisambiguator}; use session::config::OutputFilenames; use traits::Vtable; @@ -210,8 +211,8 @@ define_maps! { <'tcx> /// Results of evaluating const items or constants embedded in /// other items (such as enum variant explicit discriminants). - [] fn const_eval: const_eval_dep_node(ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>) - -> const_val::EvalResult<'tcx>, + [] fn const_eval: const_eval_dep_node(ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) + -> EvalResult<'tcx>, [] fn check_match: CheckMatch(DefId) -> Result<(), ErrorReported>, @@ -450,7 +451,7 @@ fn typeck_item_bodies_dep_node<'tcx>(_: CrateNum) -> DepConstructor<'tcx> { DepConstructor::TypeckBodiesKrate } -fn const_eval_dep_node<'tcx>(param_env: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>) +fn const_eval_dep_node<'tcx>(param_env: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) -> DepConstructor<'tcx> { DepConstructor::ConstEval { param_env } } diff --git a/src/librustc/ty/maps/on_disk_cache.rs b/src/librustc/ty/maps/on_disk_cache.rs index b18837ff35a..49c4b8bc49d 100644 --- a/src/librustc/ty/maps/on_disk_cache.rs +++ b/src/librustc/ty/maps/on_disk_cache.rs @@ -15,7 +15,7 @@ use hir::def_id::{CrateNum, DefIndex, DefId, LocalDefId, RESERVED_FOR_INCR_COMP_CACHE, LOCAL_CRATE}; use hir::map::definitions::DefPathHash; use ich::{CachingCodemapView, Fingerprint}; -use mir; +use mir::{self, interpret}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; @@ -187,6 +187,7 @@ impl<'sess> OnDiskCache<'sess> { type_shorthands: FxHashMap(), predicate_shorthands: FxHashMap(), expn_info_shorthands: FxHashMap(), + interpret_alloc_shorthands: FxHashMap(), codemap: CachingCodemapView::new(tcx.sess.codemap()), file_to_file_index, }; @@ -362,6 +363,7 @@ impl<'sess> OnDiskCache<'sess> { file_index_to_file: &self.file_index_to_file, file_index_to_stable_id: &self.file_index_to_stable_id, synthetic_expansion_infos: &self.synthetic_expansion_infos, + interpret_alloc_cache: FxHashMap::default(), }; match decode_tagged(&mut decoder, dep_node_index) { @@ -423,6 +425,7 @@ struct CacheDecoder<'a, 'tcx: 'a, 'x> { synthetic_expansion_infos: &'x RefCell>, file_index_to_file: &'x RefCell>>, file_index_to_stable_id: &'x FxHashMap, + interpret_alloc_cache: FxHashMap, } impl<'a, 'tcx, 'x> CacheDecoder<'a, 'tcx, 'x> { @@ -542,6 +545,50 @@ impl<'a, 'tcx: 'a, 'x> ty_codec::TyDecoder<'a, 'tcx> for CacheDecoder<'a, 'tcx, implement_ty_decoder!( CacheDecoder<'a, 'tcx, 'x> ); +impl<'a, 'tcx, 'x> SpecializedDecoder for CacheDecoder<'a, 'tcx, 'x> { + fn specialized_decode(&mut self) -> Result { + const MAX1: usize = usize::max_value() - 1; + let tcx = self.tcx; + let pos = TyDecoder::position(self); + match usize::decode(self)? { + ::std::usize::MAX => { + let alloc_id = tcx.interpret_interner.reserve(); + trace!("creating alloc id {:?} at {}", alloc_id, pos); + // insert early to allow recursive allocs + self.interpret_alloc_cache.insert(pos, alloc_id); + + let allocation = interpret::Allocation::decode(self)?; + trace!("decoded alloc {:?} {:#?}", alloc_id, allocation); + let allocation = self.tcx.intern_const_alloc(allocation); + tcx.interpret_interner.intern_at_reserved(alloc_id, allocation); + + if let Some(glob) = Option::::decode(self)? { + tcx.interpret_interner.cache(glob, alloc_id); + } + + Ok(alloc_id) + }, + MAX1 => { + trace!("creating fn alloc id at {}", pos); + let instance = ty::Instance::decode(self)?; + trace!("decoded fn alloc instance: {:?}", instance); + let id = tcx.interpret_interner.create_fn_alloc(instance); + trace!("created fn alloc id: {:?}", id); + self.interpret_alloc_cache.insert(pos, id); + Ok(id) + }, + shorthand => { + trace!("loading shorthand {}", shorthand); + if let Some(&alloc_id) = self.interpret_alloc_cache.get(&shorthand) { + return Ok(alloc_id); + } + trace!("shorthand {} not cached, loading entire allocation", shorthand); + // need to load allocation + self.with_position(shorthand, |this| interpret::AllocId::decode(this)) + }, + } + } +} impl<'a, 'tcx, 'x> SpecializedDecoder for CacheDecoder<'a, 'tcx, 'x> { fn specialized_decode(&mut self) -> Result { let tag: u8 = Decodable::decode(self)?; @@ -703,6 +750,7 @@ struct CacheEncoder<'enc, 'a, 'tcx, E> type_shorthands: FxHashMap, usize>, predicate_shorthands: FxHashMap, usize>, expn_info_shorthands: FxHashMap, + interpret_alloc_shorthands: FxHashMap, codemap: CachingCodemapView<'tcx>, file_to_file_index: FxHashMap<*const FileMap, FileMapIndex>, } @@ -735,6 +783,37 @@ impl<'enc, 'a, 'tcx, E> CacheEncoder<'enc, 'a, 'tcx, E> } } +impl<'enc, 'a, 'tcx, E> SpecializedEncoder for CacheEncoder<'enc, 'a, 'tcx, E> + where E: 'enc + ty_codec::TyEncoder +{ + fn specialized_encode(&mut self, alloc_id: &interpret::AllocId) -> Result<(), Self::Error> { + trace!("encoding {:?} at {}", alloc_id, self.position()); + if let Some(shorthand) = self.interpret_alloc_shorthands.get(alloc_id).cloned() { + trace!("encoding {:?} as shorthand to {}", alloc_id, shorthand); + return shorthand.encode(self); + } + let start = self.position(); + // cache the allocation shorthand now, because the allocation itself might recursively + // point to itself. + self.interpret_alloc_shorthands.insert(*alloc_id, start); + if let Some(alloc) = self.tcx.interpret_interner.get_alloc(*alloc_id) { + trace!("encoding {:?} with {:#?}", alloc_id, alloc); + usize::max_value().encode(self)?; + alloc.encode(self)?; + self.tcx.interpret_interner + .get_corresponding_static_def_id(*alloc_id) + .encode(self)?; + } else if let Some(fn_instance) = self.tcx.interpret_interner.get_fn(*alloc_id) { + trace!("encoding {:?} with {:#?}", alloc_id, fn_instance); + (usize::max_value() - 1).encode(self)?; + fn_instance.encode(self)?; + } else { + bug!("alloc id without corresponding allocation: {}", alloc_id); + } + Ok(()) + } +} + impl<'enc, 'a, 'tcx, E> SpecializedEncoder for CacheEncoder<'enc, 'a, 'tcx, E> where E: 'enc + ty_codec::TyEncoder { diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index be27e3d5152..63494438f7d 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -26,12 +26,13 @@ use middle::lang_items::{FnTraitLangItem, FnMutTraitLangItem, FnOnceTraitLangIte use middle::privacy::AccessLevels; use middle::resolve_lifetime::ObjectLifetimeDefault; use mir::Mir; +use mir::interpret::{GlobalId, Value, PrimVal}; use mir::GeneratorLayout; use session::CrateDisambiguator; use traits; use ty; use ty::subst::{Subst, Substs}; -use ty::util::IntTypeExt; +use ty::util::{IntTypeExt, Discr}; use ty::walk::TypeWalker; use util::common::ErrorReported; use util::nodemap::{NodeSet, DefIdMap, FxHashMap, FxHashSet}; @@ -52,7 +53,6 @@ use syntax::attr; use syntax::ext::hygiene::{Mark, SyntaxContext}; use syntax::symbol::{Symbol, InternedString}; use syntax_pos::{DUMMY_SP, Span}; -use rustc_const_math::ConstInt; use rustc_data_structures::accumulate_vec::IntoIter as AccIntoIter; use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult, @@ -79,7 +79,7 @@ pub use self::binding::BindingMode; pub use self::binding::BindingMode::*; pub use self::context::{TyCtxt, GlobalArenas, AllArenas, tls, keep_local}; -pub use self::context::{Lift, TypeckTables}; +pub use self::context::{Lift, TypeckTables, InterpretInterner}; pub use self::instance::{Instance, InstanceDef}; @@ -528,9 +528,9 @@ impl<'tcx> TyS<'tcx> { } } -impl<'gcx> HashStable> for ty::TyS<'gcx> { +impl<'a, 'gcx> HashStable> for ty::TyS<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ty::TyS { ref sty, @@ -1439,11 +1439,11 @@ impl<'tcx, T> ParamEnvAnd<'tcx, T> { } } -impl<'gcx, T> HashStable> for ParamEnvAnd<'gcx, T> - where T: HashStable> +impl<'a, 'gcx, T> HashStable> for ParamEnvAnd<'gcx, T> + where T: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ParamEnvAnd { ref param_env, @@ -1544,9 +1544,9 @@ impl<'tcx> serialize::UseSpecializedEncodable for &'tcx AdtDef { impl<'tcx> serialize::UseSpecializedDecodable for &'tcx AdtDef {} -impl<'gcx> HashStable> for AdtDef { +impl<'a> HashStable> for AdtDef { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { thread_local! { static CACHE: RefCell> = @@ -1824,27 +1824,79 @@ impl<'a, 'gcx, 'tcx> AdtDef { } #[inline] - pub fn discriminants(&'a self, tcx: TyCtxt<'a, 'gcx, 'tcx>) - -> impl Iterator + 'a { + pub fn eval_explicit_discr( + &self, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + expr_did: DefId, + ) -> Option> { let param_env = ParamEnv::empty(traits::Reveal::UserFacing); let repr_type = self.repr.discr_type(); + let bit_size = layout::Integer::from_attr(tcx, repr_type).size().bits(); + let substs = Substs::identity_for_item(tcx.global_tcx(), expr_did); + let instance = ty::Instance::new(expr_did, substs); + let cid = GlobalId { + instance, + promoted: None + }; + match tcx.const_eval(param_env.and(cid)) { + Ok(&ty::Const { + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))), + .. + }) => { + trace!("discriminants: {} ({:?})", b, repr_type); + let ty = repr_type.to_ty(tcx); + if repr_type.is_signed() { + let val = b as i128; + // sign extend to i128 + let amt = 128 - bit_size; + let val = (val << amt) >> amt; + Some(Discr { + val: val as u128, + ty, + }) + } else { + Some(Discr { + val: b, + ty, + }) + } + }, + Ok(&ty::Const { + val: ConstVal::Value(other), + .. + }) => { + info!("invalid enum discriminant: {:#?}", other); + ::middle::const_val::struct_error( + tcx, + tcx.def_span(expr_did), + "constant evaluation of enum discriminant resulted in non-integer", + ).emit(); + None + } + Err(err) => { + err.report(tcx, tcx.def_span(expr_did), "enum discriminant"); + if !expr_did.is_local() { + span_bug!(tcx.def_span(expr_did), + "variant discriminant evaluation succeeded \ + in its crate but failed locally"); + } + None + } + _ => span_bug!(tcx.def_span(expr_did), "const eval "), + } + } + + #[inline] + pub fn discriminants(&'a self, tcx: TyCtxt<'a, 'gcx, 'tcx>) + -> impl Iterator> + 'a { + let repr_type = self.repr.discr_type(); let initial = repr_type.initial_discriminant(tcx.global_tcx()); - let mut prev_discr = None::; + let mut prev_discr = None::>; self.variants.iter().map(move |v| { - let mut discr = prev_discr.map_or(initial, |d| d.wrap_incr()); + let mut discr = prev_discr.map_or(initial, |d| d.wrap_incr(tcx)); if let VariantDiscr::Explicit(expr_did) = v.discr { - let substs = Substs::identity_for_item(tcx.global_tcx(), expr_did); - match tcx.const_eval(param_env.and((expr_did, substs))) { - Ok(&ty::Const { val: ConstVal::Integral(v), .. }) => { - discr = v; - } - err => { - if !expr_did.is_local() { - span_bug!(tcx.def_span(expr_did), - "variant discriminant evaluation succeeded \ - in its crate but failed locally: {:?}", err); - } - } + if let Some(new_discr) = self.eval_explicit_discr(tcx, expr_did) { + discr = new_discr; } } prev_discr = Some(discr); @@ -1861,8 +1913,7 @@ impl<'a, 'gcx, 'tcx> AdtDef { pub fn discriminant_for_variant(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>, variant_index: usize) - -> ConstInt { - let param_env = ParamEnv::empty(traits::Reveal::UserFacing); + -> Discr<'tcx> { let repr_type = self.repr.discr_type(); let mut explicit_value = repr_type.initial_discriminant(tcx.global_tcx()); let mut explicit_index = variant_index; @@ -1873,18 +1924,12 @@ impl<'a, 'gcx, 'tcx> AdtDef { explicit_index -= distance; } ty::VariantDiscr::Explicit(expr_did) => { - let substs = Substs::identity_for_item(tcx.global_tcx(), expr_did); - match tcx.const_eval(param_env.and((expr_did, substs))) { - Ok(&ty::Const { val: ConstVal::Integral(v), .. }) => { - explicit_value = v; + match self.eval_explicit_discr(tcx, expr_did) { + Some(discr) => { + explicit_value = discr; 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); - } + }, + None => { if explicit_index == 0 { break; } @@ -1894,18 +1939,7 @@ impl<'a, 'gcx, 'tcx> AdtDef { } } } - let discr = explicit_value.to_u128_unchecked() - .wrapping_add((variant_index - explicit_index) as u128); - match repr_type { - attr::UnsignedInt(ty) => { - ConstInt::new_unsigned_truncating(discr, ty, - tcx.sess.target.usize_ty) - } - attr::SignedInt(ty) => { - ConstInt::new_signed_truncating(discr as i128, ty, - tcx.sess.target.isize_ty) - } - } + explicit_value.checked_add(tcx, (variant_index - explicit_index) as u128).0 } pub fn destructor(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option { diff --git a/src/librustc/ty/relate.rs b/src/librustc/ty/relate.rs index b9927c7eeb2..bae1ce31a5e 100644 --- a/src/librustc/ty/relate.rs +++ b/src/librustc/ty/relate.rs @@ -20,6 +20,7 @@ use ty::subst::{UnpackedKind, Substs}; use ty::{self, Ty, TyCtxt, TypeFoldable}; use ty::fold::{TypeVisitor, TypeFolder}; use ty::error::{ExpectedFound, TypeError}; +use mir::interpret::{GlobalId, Value, PrimVal}; use util::common::ErrorReported; use std::rc::Rc; use std::iter; @@ -482,19 +483,35 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R, assert_eq!(sz_b.ty, tcx.types.usize); let to_u64 = |x: &'tcx ty::Const<'tcx>| -> Result { match x.val { - ConstVal::Integral(x) => Ok(x.to_u64().unwrap()), + ConstVal::Value(Value::ByVal(prim)) => Ok(prim.to_u64().unwrap()), ConstVal::Unevaluated(def_id, substs) => { // FIXME(eddyb) get the right param_env. let param_env = ty::ParamEnv::empty(Reveal::UserFacing); match tcx.lift_to_global(&substs) { Some(substs) => { - match tcx.const_eval(param_env.and((def_id, substs))) { - Ok(&ty::Const { val: ConstVal::Integral(x), .. }) => { - return Ok(x.to_u64().unwrap()); + let instance = ty::Instance::resolve( + tcx.global_tcx(), + param_env, + def_id, + substs, + ); + if let Some(instance) = instance { + let cid = GlobalId { + instance, + promoted: None + }; + match tcx.const_eval(param_env.and(cid)) { + Ok(&ty::Const { + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))), + .. + }) => { + assert_eq!(b as u64 as u128, b); + return Ok(b as u64); + } + _ => {} } - _ => {} } - } + }, None => {} } tcx.sess.delay_span_bug(tcx.def_span(def_id), diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 055835ed69c..78fccaa1131 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -13,11 +13,12 @@ //! hand, though we've recently added some macros (e.g., //! `BraceStructLiftImpl!`) to help with the tedium. -use middle::const_val::{self, ConstVal, ConstAggregate, ConstEvalErr}; +use middle::const_val::{self, ConstVal, ConstEvalErr}; use ty::{self, Lift, Ty, TyCtxt}; use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use rustc_data_structures::accumulate_vec::AccumulateVec; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; +use mir::interpret; use std::rc::Rc; @@ -56,6 +57,7 @@ CopyImpls! { ::syntax::abi::Abi, ::hir::def_id::DefId, ::mir::Local, + ::mir::Promoted, ::traits::Reveal, ::syntax_pos::Span, } @@ -576,44 +578,139 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> { impl<'a, 'tcx> Lift<'tcx> for ConstEvalErr<'a> { type Lifted = ConstEvalErr<'tcx>; fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option { - tcx.lift(&self.kind).map(|kind| { + tcx.lift(&*self.kind).map(|kind| { ConstEvalErr { span: self.span, - kind, + kind: Rc::new(kind), } }) } } +impl<'a, 'tcx> Lift<'tcx> for interpret::EvalError<'a> { + type Lifted = interpret::EvalError<'tcx>; + fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option { + use ::mir::interpret::EvalErrorKind::*; + let kind = match self.kind { + MachineError(ref err) => MachineError(err.clone()), + FunctionPointerTyMismatch(a, b) => FunctionPointerTyMismatch( + tcx.lift(&a)?, + tcx.lift(&b)?, + ), + NoMirFor(ref s) => NoMirFor(s.clone()), + UnterminatedCString(ptr) => UnterminatedCString(ptr), + DanglingPointerDeref => DanglingPointerDeref, + DoubleFree => DoubleFree, + InvalidMemoryAccess => InvalidMemoryAccess, + InvalidFunctionPointer => InvalidFunctionPointer, + InvalidBool => InvalidBool, + InvalidDiscriminant => InvalidDiscriminant, + PointerOutOfBounds { + ptr, + access, + allocation_size, + } => PointerOutOfBounds { ptr, access, allocation_size }, + InvalidNullPointerUsage => InvalidNullPointerUsage, + ReadPointerAsBytes => ReadPointerAsBytes, + ReadBytesAsPointer => ReadBytesAsPointer, + InvalidPointerMath => InvalidPointerMath, + ReadUndefBytes => ReadUndefBytes, + DeadLocal => DeadLocal, + InvalidBoolOp(bop) => InvalidBoolOp(bop), + Unimplemented(ref s) => Unimplemented(s.clone()), + DerefFunctionPointer => DerefFunctionPointer, + ExecuteMemory => ExecuteMemory, + ArrayIndexOutOfBounds(sp, a, b) => ArrayIndexOutOfBounds(sp, a, b), + Math(sp, ref err) => Math(sp, err.clone()), + Intrinsic(ref s) => Intrinsic(s.clone()), + OverflowingMath => OverflowingMath, + InvalidChar(c) => InvalidChar(c), + ExecutionTimeLimitReached => ExecutionTimeLimitReached, + StackFrameLimitReached => StackFrameLimitReached, + OutOfTls => OutOfTls, + TlsOutOfBounds => TlsOutOfBounds, + AbiViolation(ref s) => AbiViolation(s.clone()), + AlignmentCheckFailed { + required, + has, + } => AlignmentCheckFailed { required, has }, + MemoryLockViolation { + ptr, + len, + frame, + access, + ref lock, + } => MemoryLockViolation { ptr, len, frame, access, lock: lock.clone() }, + MemoryAcquireConflict { + ptr, + len, + kind, + ref lock, + } => MemoryAcquireConflict { ptr, len, kind, lock: lock.clone() }, + InvalidMemoryLockRelease { + ptr, + len, + frame, + ref lock, + } => InvalidMemoryLockRelease { ptr, len, frame, lock: lock.clone() }, + DeallocatedLockedMemory { + ptr, + ref lock, + } => DeallocatedLockedMemory { ptr, lock: lock.clone() }, + ValidationFailure(ref s) => ValidationFailure(s.clone()), + CalledClosureAsFunction => CalledClosureAsFunction, + VtableForArgumentlessMethod => VtableForArgumentlessMethod, + ModifiedConstantMemory => ModifiedConstantMemory, + AssumptionNotHeld => AssumptionNotHeld, + InlineAsm => InlineAsm, + TypeNotPrimitive(ty) => TypeNotPrimitive(tcx.lift(&ty)?), + ReallocatedWrongMemoryKind(ref a, ref b) => { + ReallocatedWrongMemoryKind(a.clone(), b.clone()) + }, + DeallocatedWrongMemoryKind(ref a, ref b) => { + DeallocatedWrongMemoryKind(a.clone(), b.clone()) + }, + ReallocateNonBasePtr => ReallocateNonBasePtr, + DeallocateNonBasePtr => DeallocateNonBasePtr, + IncorrectAllocationInformation(a, b, c, d) => { + IncorrectAllocationInformation(a, b, c, d) + }, + Layout(lay) => Layout(tcx.lift(&lay)?), + HeapAllocZeroBytes => HeapAllocZeroBytes, + HeapAllocNonPowerOfTwoAlignment(n) => HeapAllocNonPowerOfTwoAlignment(n), + Unreachable => Unreachable, + Panic => Panic, + ReadFromReturnPointer => ReadFromReturnPointer, + PathNotFound(ref v) => PathNotFound(v.clone()), + UnimplementedTraitSelection => UnimplementedTraitSelection, + TypeckError => TypeckError, + ReferencedConstant => ReferencedConstant, + }; + Some(interpret::EvalError { + kind: kind, + backtrace: self.backtrace.clone(), + }) + } +} + impl<'a, 'tcx> Lift<'tcx> for const_val::ErrKind<'a> { type Lifted = const_val::ErrKind<'tcx>; fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option { use middle::const_val::ErrKind::*; Some(match *self { - CannotCast => CannotCast, - MissingStructField => MissingStructField, NonConstPath => NonConstPath, UnimplementedConstVal(s) => UnimplementedConstVal(s), - ExpectedConstTuple => ExpectedConstTuple, - ExpectedConstStruct => ExpectedConstStruct, - IndexedNonVec => IndexedNonVec, - IndexNotUsize => IndexNotUsize, IndexOutOfBounds { len, index } => IndexOutOfBounds { len, index }, - MiscBinaryOp => MiscBinaryOp, - MiscCatchAll => MiscCatchAll, - IndexOpFeatureGated => IndexOpFeatureGated, Math(ref e) => Math(e.clone()), LayoutError(ref e) => { return tcx.lift(e).map(LayoutError) } - ErroneousReferencedConstant(ref e) => { - return tcx.lift(e).map(ErroneousReferencedConstant) - } TypeckError => TypeckError, CheckMatchError => CheckMatchError, + Miri(ref e, ref frames) => return tcx.lift(e).map(|e| Miri(e, frames.clone())), }) } } @@ -632,6 +729,42 @@ impl<'a, 'tcx> Lift<'tcx> for ty::layout::LayoutError<'a> { } } +impl<'a, 'tcx> Lift<'tcx> for ty::InstanceDef<'a> { + type Lifted = ty::InstanceDef<'tcx>; + fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option { + match *self { + ty::InstanceDef::Item(def_id) => + Some(ty::InstanceDef::Item(def_id)), + ty::InstanceDef::Intrinsic(def_id) => + Some(ty::InstanceDef::Intrinsic(def_id)), + ty::InstanceDef::FnPtrShim(def_id, ref ty) => + Some(ty::InstanceDef::FnPtrShim(def_id, tcx.lift(ty)?)), + ty::InstanceDef::Virtual(def_id, n) => + Some(ty::InstanceDef::Virtual(def_id, n)), + ty::InstanceDef::ClosureOnceShim { call_once } => + Some(ty::InstanceDef::ClosureOnceShim { call_once }), + ty::InstanceDef::DropGlue(def_id, ref ty) => + Some(ty::InstanceDef::DropGlue(def_id, tcx.lift(ty)?)), + ty::InstanceDef::CloneShim(def_id, ref ty) => + Some(ty::InstanceDef::CloneShim(def_id, tcx.lift(ty)?)), + } + } +} + +BraceStructLiftImpl! { + impl<'a, 'tcx> Lift<'tcx> for ty::Instance<'a> { + type Lifted = ty::Instance<'tcx>; + def, substs + } +} + +BraceStructLiftImpl! { + impl<'a, 'tcx> Lift<'tcx> for interpret::GlobalId<'a> { + type Lifted = interpret::GlobalId<'tcx>; + instance, promoted + } +} + /////////////////////////////////////////////////////////////////////////// // TypeFoldable implementations. // @@ -778,6 +911,74 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::Slice> { } } +impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> { + fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { + use ty::InstanceDef::*; + Self { + substs: self.substs.fold_with(folder), + def: match self.def { + Item(did) => Item(did.fold_with(folder)), + Intrinsic(did) => Intrinsic(did.fold_with(folder)), + FnPtrShim(did, ty) => FnPtrShim( + did.fold_with(folder), + ty.fold_with(folder), + ), + Virtual(did, i) => Virtual( + did.fold_with(folder), + i, + ), + ClosureOnceShim { call_once } => ClosureOnceShim { + call_once: call_once.fold_with(folder), + }, + DropGlue(did, ty) => DropGlue( + did.fold_with(folder), + ty.fold_with(folder), + ), + CloneShim(did, ty) => CloneShim( + did.fold_with(folder), + ty.fold_with(folder), + ), + }, + } + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + use ty::InstanceDef::*; + self.substs.visit_with(visitor) || + match self.def { + Item(did) => did.visit_with(visitor), + Intrinsic(did) => did.visit_with(visitor), + FnPtrShim(did, ty) => { + did.visit_with(visitor) || + ty.visit_with(visitor) + }, + Virtual(did, _) => did.visit_with(visitor), + ClosureOnceShim { call_once } => call_once.visit_with(visitor), + DropGlue(did, ty) => { + did.visit_with(visitor) || + ty.visit_with(visitor) + }, + CloneShim(did, ty) => { + did.visit_with(visitor) || + ty.visit_with(visitor) + }, + } + } +} + +impl<'tcx> TypeFoldable<'tcx> for interpret::GlobalId<'tcx> { + fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { + Self { + instance: self.instance.fold_with(folder), + promoted: self.promoted + } + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.instance.visit_with(visitor) + } +} + impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { let sty = match self.sty { @@ -1243,53 +1444,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::error::TypeError<'tcx> { impl<'tcx> TypeFoldable<'tcx> for ConstVal<'tcx> { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { match *self { - ConstVal::Integral(i) => ConstVal::Integral(i), - ConstVal::Float(f) => ConstVal::Float(f), - ConstVal::Str(s) => ConstVal::Str(s), - ConstVal::ByteStr(b) => ConstVal::ByteStr(b), - ConstVal::Bool(b) => ConstVal::Bool(b), - ConstVal::Char(c) => ConstVal::Char(c), - ConstVal::Variant(def_id) => ConstVal::Variant(def_id), - ConstVal::Function(def_id, substs) => { - ConstVal::Function(def_id, substs.fold_with(folder)) - } - ConstVal::Aggregate(ConstAggregate::Struct(fields)) => { - let new_fields: Vec<_> = fields.iter().map(|&(name, v)| { - (name, v.fold_with(folder)) - }).collect(); - let fields = if new_fields == fields { - fields - } else { - folder.tcx().alloc_name_const_slice(&new_fields) - }; - ConstVal::Aggregate(ConstAggregate::Struct(fields)) - } - ConstVal::Aggregate(ConstAggregate::Tuple(fields)) => { - let new_fields: Vec<_> = fields.iter().map(|v| { - v.fold_with(folder) - }).collect(); - let fields = if new_fields == fields { - fields - } else { - folder.tcx().alloc_const_slice(&new_fields) - }; - ConstVal::Aggregate(ConstAggregate::Tuple(fields)) - } - ConstVal::Aggregate(ConstAggregate::Array(fields)) => { - let new_fields: Vec<_> = fields.iter().map(|v| { - v.fold_with(folder) - }).collect(); - let fields = if new_fields == fields { - fields - } else { - folder.tcx().alloc_const_slice(&new_fields) - }; - ConstVal::Aggregate(ConstAggregate::Array(fields)) - } - ConstVal::Aggregate(ConstAggregate::Repeat(v, count)) => { - let v = v.fold_with(folder); - ConstVal::Aggregate(ConstAggregate::Repeat(v, count)) - } + ConstVal::Value(v) => ConstVal::Value(v), ConstVal::Unevaluated(def_id, substs) => { ConstVal::Unevaluated(def_id, substs.fold_with(folder)) } @@ -1298,24 +1453,7 @@ impl<'tcx> TypeFoldable<'tcx> for ConstVal<'tcx> { fn super_visit_with>(&self, visitor: &mut V) -> bool { match *self { - ConstVal::Integral(_) | - ConstVal::Float(_) | - ConstVal::Str(_) | - ConstVal::ByteStr(_) | - ConstVal::Bool(_) | - ConstVal::Char(_) | - ConstVal::Variant(_) => false, - ConstVal::Function(_, substs) => substs.visit_with(visitor), - ConstVal::Aggregate(ConstAggregate::Struct(fields)) => { - fields.iter().any(|&(_, v)| v.visit_with(visitor)) - } - ConstVal::Aggregate(ConstAggregate::Tuple(fields)) | - ConstVal::Aggregate(ConstAggregate::Array(fields)) => { - fields.iter().any(|v| v.visit_with(visitor)) - } - ConstVal::Aggregate(ConstAggregate::Repeat(v, _)) => { - v.visit_with(visitor) - } + ConstVal::Value(_) => false, ConstVal::Unevaluated(_, substs) => substs.visit_with(visitor), } } diff --git a/src/librustc/ty/trait_def.rs b/src/librustc/ty/trait_def.rs index 62d3c8dc87d..32f0d3384c4 100644 --- a/src/librustc/ty/trait_def.rs +++ b/src/librustc/ty/trait_def.rs @@ -186,9 +186,9 @@ pub(super) fn trait_impls_of_provider<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, }) } -impl<'gcx> HashStable> for TraitImpls { +impl<'a> HashStable> for TraitImpls { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let TraitImpls { ref blanket_impls, diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 785035ee5f4..cbd9a1b8d4f 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -22,58 +22,96 @@ use ty::fold::TypeVisitor; use ty::subst::{Subst, UnpackedKind}; use ty::maps::TyCtxtAt; use ty::TypeVariants::*; +use ty::layout::Integer; use util::common::ErrorReported; use middle::lang_items; +use mir::interpret::{Value, PrimVal}; -use rustc_const_math::{ConstInt, ConstIsize, ConstUsize}; use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult, HashStable}; use rustc_data_structures::fx::FxHashMap; -use std::cmp; +use std::{cmp, fmt}; use std::hash::Hash; use std::intrinsics; use syntax::ast::{self, Name}; use syntax::attr::{self, SignedInt, UnsignedInt}; use syntax_pos::{Span, DUMMY_SP}; -type Disr = ConstInt; +#[derive(Copy, Clone, Debug)] +pub struct Discr<'tcx> { + pub val: u128, + pub ty: Ty<'tcx> +} + +impl<'tcx> fmt::Display for Discr<'tcx> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + if self.ty.is_signed() { + write!(fmt, "{}", self.val as i128) + } else { + write!(fmt, "{}", self.val) + } + } +} + +impl<'tcx> Discr<'tcx> { + /// Adds 1 to the value and wraps around if the maximum for the type is reached + pub fn wrap_incr<'a, 'gcx>(self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Self { + self.checked_add(tcx, 1).0 + } + pub fn checked_add<'a, 'gcx>(self, tcx: TyCtxt<'a, 'gcx, 'tcx>, n: u128) -> (Self, bool) { + let (int, signed) = match self.ty.sty { + TyInt(ity) => (Integer::from_attr(tcx, SignedInt(ity)), true), + TyUint(uty) => (Integer::from_attr(tcx, UnsignedInt(uty)), false), + _ => bug!("non integer discriminant"), + }; + if signed { + let (min, max) = match int { + Integer::I8 => (i8::min_value() as i128, i8::max_value() as i128), + Integer::I16 => (i16::min_value() as i128, i16::max_value() as i128), + Integer::I32 => (i32::min_value() as i128, i32::max_value() as i128), + Integer::I64 => (i64::min_value() as i128, i64::max_value() as i128), + Integer::I128 => (i128::min_value(), i128::max_value()), + }; + let val = self.val as i128; + let n = n as i128; + let oflo = val > max - n; + let val = if oflo { + min + (n - (max - val) - 1) + } else { + val + n + }; + (Self { + val: val as u128, + ty: self.ty, + }, oflo) + } else { + let (min, max) = match int { + Integer::I8 => (u8::min_value() as u128, u8::max_value() as u128), + Integer::I16 => (u16::min_value() as u128, u16::max_value() as u128), + Integer::I32 => (u32::min_value() as u128, u32::max_value() as u128), + Integer::I64 => (u64::min_value() as u128, u64::max_value() as u128), + Integer::I128 => (u128::min_value(), u128::max_value()), + }; + let val = self.val; + let oflo = val > max - n; + let val = if oflo { + min + (n - (max - val) - 1) + } else { + val + n + }; + (Self { + val: val, + ty: self.ty, + }, oflo) + } + } +} pub trait IntTypeExt { fn to_ty<'a, 'gcx, 'tcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx>; - fn disr_incr<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, val: Option) - -> Option; - fn assert_ty_matches(&self, val: Disr); - fn initial_discriminant<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Disr; -} - - -macro_rules! typed_literal { - ($tcx:expr, $ty:expr, $lit:expr) => { - match $ty { - SignedInt(ast::IntTy::I8) => ConstInt::I8($lit), - SignedInt(ast::IntTy::I16) => ConstInt::I16($lit), - SignedInt(ast::IntTy::I32) => ConstInt::I32($lit), - SignedInt(ast::IntTy::I64) => ConstInt::I64($lit), - SignedInt(ast::IntTy::I128) => ConstInt::I128($lit), - SignedInt(ast::IntTy::Isize) => match $tcx.sess.target.isize_ty { - ast::IntTy::I16 => ConstInt::Isize(ConstIsize::Is16($lit)), - ast::IntTy::I32 => ConstInt::Isize(ConstIsize::Is32($lit)), - ast::IntTy::I64 => ConstInt::Isize(ConstIsize::Is64($lit)), - _ => bug!(), - }, - UnsignedInt(ast::UintTy::U8) => ConstInt::U8($lit), - UnsignedInt(ast::UintTy::U16) => ConstInt::U16($lit), - UnsignedInt(ast::UintTy::U32) => ConstInt::U32($lit), - UnsignedInt(ast::UintTy::U64) => ConstInt::U64($lit), - UnsignedInt(ast::UintTy::U128) => ConstInt::U128($lit), - UnsignedInt(ast::UintTy::Usize) => match $tcx.sess.target.usize_ty { - ast::UintTy::U16 => ConstInt::Usize(ConstUsize::Us16($lit)), - ast::UintTy::U32 => ConstInt::Usize(ConstUsize::Us32($lit)), - ast::UintTy::U64 => ConstInt::Usize(ConstUsize::Us64($lit)), - _ => bug!(), - }, - } - } + fn disr_incr<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, val: Option>) + -> Option>; + fn initial_discriminant<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Discr<'tcx>; } impl IntTypeExt for attr::IntType { @@ -94,33 +132,26 @@ impl IntTypeExt for attr::IntType { } } - fn initial_discriminant<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Disr { - typed_literal!(tcx, *self, 0) - } - - fn assert_ty_matches(&self, val: Disr) { - match (*self, val) { - (SignedInt(ast::IntTy::I8), ConstInt::I8(_)) => {}, - (SignedInt(ast::IntTy::I16), ConstInt::I16(_)) => {}, - (SignedInt(ast::IntTy::I32), ConstInt::I32(_)) => {}, - (SignedInt(ast::IntTy::I64), ConstInt::I64(_)) => {}, - (SignedInt(ast::IntTy::I128), ConstInt::I128(_)) => {}, - (SignedInt(ast::IntTy::Isize), ConstInt::Isize(_)) => {}, - (UnsignedInt(ast::UintTy::U8), ConstInt::U8(_)) => {}, - (UnsignedInt(ast::UintTy::U16), ConstInt::U16(_)) => {}, - (UnsignedInt(ast::UintTy::U32), ConstInt::U32(_)) => {}, - (UnsignedInt(ast::UintTy::U64), ConstInt::U64(_)) => {}, - (UnsignedInt(ast::UintTy::U128), ConstInt::U128(_)) => {}, - (UnsignedInt(ast::UintTy::Usize), ConstInt::Usize(_)) => {}, - _ => bug!("disr type mismatch: {:?} vs {:?}", self, val), + fn initial_discriminant<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Discr<'tcx> { + Discr { + val: 0, + ty: self.to_ty(tcx) } } - fn disr_incr<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, val: Option) - -> Option { + fn disr_incr<'a, 'tcx>( + &self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + val: Option>, + ) -> Option> { if let Some(val) = val { - self.assert_ty_matches(val); - (val + typed_literal!(tcx, *self, 1)).ok() + assert_eq!(self.to_ty(tcx), val.ty); + let (new, oflo) = val.checked_add(tcx, 1); + if oflo { + None + } else { + Some(new) + } } else { Some(self.initial_discriminant(tcx)) } @@ -681,31 +712,32 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { }) } - pub fn const_usize(&self, val: u16) -> ConstInt { - match self.sess.target.usize_ty { - ast::UintTy::U16 => ConstInt::Usize(ConstUsize::Us16(val as u16)), - ast::UintTy::U32 => ConstInt::Usize(ConstUsize::Us32(val as u32)), - ast::UintTy::U64 => ConstInt::Usize(ConstUsize::Us64(val as u64)), - _ => bug!(), - } - } - - /// 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 } } } @@ -764,7 +796,7 @@ impl<'a, 'gcx, 'tcx, W> TypeVisitor<'tcx> for TypeIdHasher<'a, 'gcx, 'tcx, W> TyArray(_, n) => { self.hash_discriminant_u8(&n.val); match n.val { - ConstVal::Integral(x) => self.hash(x.to_u64().unwrap()), + ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))) => self.hash(b), ConstVal::Unevaluated(def_id, _) => self.def_id(def_id), _ => bug!("arrays should not have {:?} as length", n) } diff --git a/src/librustc/ty/walk.rs b/src/librustc/ty/walk.rs index 45f0ad1cf1a..722fdfe773a 100644 --- a/src/librustc/ty/walk.rs +++ b/src/librustc/ty/walk.rs @@ -11,7 +11,7 @@ //! An iterator over the type substructure. //! WARNING: this does not keep track of the region depth. -use middle::const_val::{ConstVal, ConstAggregate}; +use middle::const_val::ConstVal; use ty::{self, Ty}; use rustc_data_structures::small_vec::SmallVec; use rustc_data_structures::accumulate_vec::IntoIter as AccIntoIter; @@ -140,30 +140,7 @@ fn push_subtypes<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent_ty: Ty<'tcx>) { fn push_const<'tcx>(stack: &mut TypeWalkerStack<'tcx>, constant: &'tcx ty::Const<'tcx>) { match constant.val { - ConstVal::Integral(_) | - ConstVal::Float(_) | - ConstVal::Str(_) | - ConstVal::ByteStr(_) | - ConstVal::Bool(_) | - ConstVal::Char(_) | - ConstVal::Variant(_) => {} - ConstVal::Function(_, substs) => { - stack.extend(substs.types().rev()); - } - ConstVal::Aggregate(ConstAggregate::Struct(fields)) => { - for &(_, v) in fields.iter().rev() { - push_const(stack, v); - } - } - ConstVal::Aggregate(ConstAggregate::Tuple(fields)) | - ConstVal::Aggregate(ConstAggregate::Array(fields)) => { - for v in fields.iter().rev() { - push_const(stack, v); - } - } - ConstVal::Aggregate(ConstAggregate::Repeat(v, _)) => { - push_const(stack, v); - } + ConstVal::Value(_) => {} ConstVal::Unevaluated(_, substs) => { stack.extend(substs.types().rev()); } diff --git a/src/librustc/ty/wf.rs b/src/librustc/ty/wf.rs index ea99bd39e87..49ae79ae9c9 100644 --- a/src/librustc/ty/wf.rs +++ b/src/librustc/ty/wf.rs @@ -9,7 +9,7 @@ // except according to those terms. use hir::def_id::DefId; -use middle::const_val::{ConstVal, ConstAggregate}; +use middle::const_val::ConstVal; use infer::InferCtxt; use ty::subst::Substs; use traits; @@ -217,28 +217,7 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> { fn compute_const(&mut self, constant: &'tcx ty::Const<'tcx>) { self.require_sized(constant.ty, traits::ConstSized); match constant.val { - ConstVal::Integral(_) | - ConstVal::Float(_) | - ConstVal::Str(_) | - ConstVal::ByteStr(_) | - ConstVal::Bool(_) | - ConstVal::Char(_) | - ConstVal::Variant(_) | - ConstVal::Function(..) => {} - ConstVal::Aggregate(ConstAggregate::Struct(fields)) => { - for &(_, v) in fields { - self.compute_const(v); - } - } - ConstVal::Aggregate(ConstAggregate::Tuple(fields)) | - ConstVal::Aggregate(ConstAggregate::Array(fields)) => { - for v in fields { - self.compute_const(v); - } - } - ConstVal::Aggregate(ConstAggregate::Repeat(v, _)) => { - self.compute_const(v); - } + ConstVal::Value(_) => {} ConstVal::Unevaluated(def_id, substs) => { let obligations = self.nominal_obligations(def_id, substs); self.out.extend(obligations); diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index a2620da4c10..5e2792ee641 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -21,12 +21,12 @@ use ty::{TyClosure, TyGenerator, TyGeneratorWitness, TyForeign, TyProjection, Ty use ty::{TyDynamic, TyInt, TyUint, TyInfer}; use ty::{self, Ty, TyCtxt, TypeFoldable}; use util::nodemap::FxHashSet; +use mir::interpret::{Value, PrimVal}; use std::cell::Cell; use std::fmt; use std::usize; -use rustc_const_math::ConstInt; use rustc_data_structures::indexed_vec::Idx; use syntax::abi::Abi; use syntax::ast::CRATE_NODE_ID; @@ -1165,7 +1165,7 @@ define_print! { TyArray(ty, sz) => { print!(f, cx, write("["), print(ty), write("; "))?; match sz.val { - ConstVal::Integral(ConstInt::Usize(sz)) => { + ConstVal::Value(Value::ByVal(PrimVal::Bytes(sz))) => { write!(f, "{}", sz)?; } ConstVal::Unevaluated(_def_id, substs) => { diff --git a/src/librustc_const_eval/Cargo.toml b/src/librustc_const_eval/Cargo.toml deleted file mode 100644 index 53b8402ab2a..00000000000 --- a/src/librustc_const_eval/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_const_eval" -version = "0.0.0" - -[lib] -name = "rustc_const_eval" -path = "lib.rs" -crate-type = ["dylib"] - -[dependencies] -arena = { path = "../libarena" } -log = "0.4" -rustc = { path = "../librustc" } -rustc_const_math = { path = "../librustc_const_math" } -rustc_data_structures = { path = "../librustc_data_structures" } -rustc_errors = { path = "../librustc_errors" } -syntax = { path = "../libsyntax" } -syntax_pos = { path = "../libsyntax_pos" } diff --git a/src/librustc_const_eval/diagnostics.rs b/src/librustc_const_eval/diagnostics.rs deleted file mode 100644 index d01b3c45f7f..00000000000 --- a/src/librustc_const_eval/diagnostics.rs +++ /dev/null @@ -1,571 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(non_snake_case)] - -// Error messages for EXXXX errors. -// Each message should start and end with a new line, and be wrapped to 80 characters. -// In vim you can `:set tw=80` and use `gq` to wrap paragraphs. Use `:set tw=0` to disable. -register_long_diagnostics! { - -E0001: r##" -#### Note: this error code is no longer emitted by the compiler. - -This error suggests that the expression arm corresponding to the noted pattern -will never be reached as for all possible values of the expression being -matched, one of the preceding patterns will match. - -This means that perhaps some of the preceding patterns are too general, this -one is too specific or the ordering is incorrect. - -For example, the following `match` block has too many arms: - -``` -match Some(0) { - Some(bar) => {/* ... */} - x => {/* ... */} // This handles the `None` case - _ => {/* ... */} // All possible cases have already been handled -} -``` - -`match` blocks have their patterns matched in order, so, for example, putting -a wildcard arm above a more specific arm will make the latter arm irrelevant. - -Ensure the ordering of the match arm is correct and remove any superfluous -arms. -"##, - -E0002: r##" -#### Note: this error code is no longer emitted by the compiler. - -This error indicates that an empty match expression is invalid because the type -it is matching on is non-empty (there exist values of this type). In safe code -it is impossible to create an instance of an empty type, so empty match -expressions are almost never desired. This error is typically fixed by adding -one or more cases to the match expression. - -An example of an empty type is `enum Empty { }`. So, the following will work: - -``` -enum Empty {} - -fn foo(x: Empty) { - match x { - // empty - } -} -``` - -However, this won't: - -```compile_fail -fn foo(x: Option) { - match x { - // empty - } -} -``` -"##, - -E0003: r##" -#### Note: this error code is no longer emitted by the compiler. - -Not-a-Number (NaN) values cannot be compared for equality and hence can never -match the input to a match expression. So, the following will not compile: - -```compile_fail -const NAN: f32 = 0.0 / 0.0; - -let number = 0.1f32; - -match number { - NAN => { /* ... */ }, - _ => {} -} -``` - -To match against NaN values, you should instead use the `is_nan()` method in a -guard, like so: - -``` -let number = 0.1f32; - -match number { - x if x.is_nan() => { /* ... */ } - _ => {} -} -``` -"##, - -E0004: r##" -This error indicates that the compiler cannot guarantee a matching pattern for -one or more possible inputs to a match expression. Guaranteed matches are -required in order to assign values to match expressions, or alternatively, -determine the flow of execution. Erroneous code example: - -```compile_fail,E0004 -enum Terminator { - HastaLaVistaBaby, - TalkToMyHand, -} - -let x = Terminator::HastaLaVistaBaby; - -match x { // error: non-exhaustive patterns: `HastaLaVistaBaby` not covered - Terminator::TalkToMyHand => {} -} -``` - -If you encounter this error you must alter your patterns so that every possible -value of the input type is matched. For types with a small number of variants -(like enums) you should probably cover all cases explicitly. Alternatively, the -underscore `_` wildcard pattern can be added after all other patterns to match -"anything else". Example: - -``` -enum Terminator { - HastaLaVistaBaby, - TalkToMyHand, -} - -let x = Terminator::HastaLaVistaBaby; - -match x { - Terminator::TalkToMyHand => {} - Terminator::HastaLaVistaBaby => {} -} - -// or: - -match x { - Terminator::TalkToMyHand => {} - _ => {} -} -``` -"##, - -E0005: r##" -Patterns used to bind names must be irrefutable, that is, they must guarantee -that a name will be extracted in all cases. Erroneous code example: - -```compile_fail,E0005 -let x = Some(1); -let Some(y) = x; -// error: refutable pattern in local binding: `None` not covered -``` - -If you encounter this error you probably need to use a `match` or `if let` to -deal with the possibility of failure. Example: - -``` -let x = Some(1); - -match x { - Some(y) => { - // do something - }, - None => {} -} - -// or: - -if let Some(y) = x { - // do something -} -``` -"##, - -E0007: r##" -This error indicates that the bindings in a match arm would require a value to -be moved into more than one location, thus violating unique ownership. Code -like the following is invalid as it requires the entire `Option` to be -moved into a variable called `op_string` while simultaneously requiring the -inner `String` to be moved into a variable called `s`. - -```compile_fail,E0007 -let x = Some("s".to_string()); - -match x { - op_string @ Some(s) => {}, // error: cannot bind by-move with sub-bindings - None => {}, -} -``` - -See also the error E0303. -"##, - -E0008: r##" -Names bound in match arms retain their type in pattern guards. As such, if a -name is bound by move in a pattern, it should also be moved to wherever it is -referenced in the pattern guard code. Doing so however would prevent the name -from being available in the body of the match arm. Consider the following: - -```compile_fail,E0008 -match Some("hi".to_string()) { - Some(s) if s.len() == 0 => {}, // use s. - _ => {}, -} -``` - -The variable `s` has type `String`, and its use in the guard is as a variable of -type `String`. The guard code effectively executes in a separate scope to the -body of the arm, so the value would be moved into this anonymous scope and -therefore becomes unavailable in the body of the arm. - -The problem above can be solved by using the `ref` keyword. - -``` -match Some("hi".to_string()) { - Some(ref s) if s.len() == 0 => {}, - _ => {}, -} -``` - -Though this example seems innocuous and easy to solve, the problem becomes clear -when it encounters functions which consume the value: - -```compile_fail,E0008 -struct A{} - -impl A { - fn consume(self) -> usize { - 0 - } -} - -fn main() { - let a = Some(A{}); - match a { - Some(y) if y.consume() > 0 => {} - _ => {} - } -} -``` - -In this situation, even the `ref` keyword cannot solve it, since borrowed -content cannot be moved. This problem cannot be solved generally. If the value -can be cloned, here is a not-so-specific solution: - -``` -#[derive(Clone)] -struct A{} - -impl A { - fn consume(self) -> usize { - 0 - } -} - -fn main() { - let a = Some(A{}); - match a{ - Some(ref y) if y.clone().consume() > 0 => {} - _ => {} - } -} -``` - -If the value will be consumed in the pattern guard, using its clone will not -move its ownership, so the code works. -"##, - -E0009: r##" -In a pattern, all values that don't implement the `Copy` trait have to be bound -the same way. The goal here is to avoid binding simultaneously by-move and -by-ref. - -This limitation may be removed in a future version of Rust. - -Erroneous code example: - -```compile_fail,E0009 -struct X { x: (), } - -let x = Some((X { x: () }, X { x: () })); -match x { - Some((y, ref z)) => {}, // error: cannot bind by-move and by-ref in the - // same pattern - None => panic!() -} -``` - -You have two solutions: - -Solution #1: Bind the pattern's values the same way. - -``` -struct X { x: (), } - -let x = Some((X { x: () }, X { x: () })); -match x { - Some((ref y, ref z)) => {}, - // or Some((y, z)) => {} - None => panic!() -} -``` - -Solution #2: Implement the `Copy` trait for the `X` structure. - -However, please keep in mind that the first solution should be preferred. - -``` -#[derive(Clone, Copy)] -struct X { x: (), } - -let x = Some((X { x: () }, X { x: () })); -match x { - Some((y, ref z)) => {}, - None => panic!() -} -``` -"##, - -E0158: r##" -`const` and `static` mean different things. A `const` is a compile-time -constant, an alias for a literal value. This property means you can match it -directly within a pattern. - -The `static` keyword, on the other hand, guarantees a fixed location in memory. -This does not always mean that the value is constant. For example, a global -mutex can be declared `static` as well. - -If you want to match against a `static`, consider using a guard instead: - -``` -static FORTY_TWO: i32 = 42; - -match Some(42) { - Some(x) if x == FORTY_TWO => {} - _ => {} -} -``` -"##, - -E0162: r##" -An if-let pattern attempts to match the pattern, and enters the body if the -match was successful. If the match is irrefutable (when it cannot fail to -match), use a regular `let`-binding instead. For instance: - -```compile_fail,E0162 -struct Irrefutable(i32); -let irr = Irrefutable(0); - -// This fails to compile because the match is irrefutable. -if let Irrefutable(x) = irr { - // This body will always be executed. - // ... -} -``` - -Try this instead: - -``` -struct Irrefutable(i32); -let irr = Irrefutable(0); - -let Irrefutable(x) = irr; -println!("{}", x); -``` -"##, - -E0165: r##" -A while-let pattern attempts to match the pattern, and enters the body if the -match was successful. If the match is irrefutable (when it cannot fail to -match), use a regular `let`-binding inside a `loop` instead. For instance: - -```compile_fail,E0165 -struct Irrefutable(i32); -let irr = Irrefutable(0); - -// This fails to compile because the match is irrefutable. -while let Irrefutable(x) = irr { - // ... -} -``` - -Try this instead: - -```no_run -struct Irrefutable(i32); -let irr = Irrefutable(0); - -loop { - let Irrefutable(x) = irr; - // ... -} -``` -"##, - -E0170: r##" -Enum variants are qualified by default. For example, given this type: - -``` -enum Method { - GET, - POST, -} -``` - -You would match it using: - -``` -enum Method { - GET, - POST, -} - -let m = Method::GET; - -match m { - Method::GET => {}, - Method::POST => {}, -} -``` - -If you don't qualify the names, the code will bind new variables named "GET" and -"POST" instead. This behavior is likely not what you want, so `rustc` warns when -that happens. - -Qualified names are good practice, and most code works well with them. But if -you prefer them unqualified, you can import the variants into scope: - -``` -use Method::*; -enum Method { GET, POST } -# fn main() {} -``` - -If you want others to be able to import variants from your module directly, use -`pub use`: - -``` -pub use Method::*; -pub enum Method { GET, POST } -# fn main() {} -``` -"##, - - -E0297: r##" -#### Note: this error code is no longer emitted by the compiler. - -Patterns used to bind names must be irrefutable. That is, they must guarantee -that a name will be extracted in all cases. Instead of pattern matching the -loop variable, consider using a `match` or `if let` inside the loop body. For -instance: - -```compile_fail,E0005 -let xs : Vec> = vec![Some(1), None]; - -// This fails because `None` is not covered. -for Some(x) in xs { - // ... -} -``` - -Match inside the loop instead: - -``` -let xs : Vec> = vec![Some(1), None]; - -for item in xs { - match item { - Some(x) => {}, - None => {}, - } -} -``` - -Or use `if let`: - -``` -let xs : Vec> = vec![Some(1), None]; - -for item in xs { - if let Some(x) = item { - // ... - } -} -``` -"##, - -E0301: r##" -Mutable borrows are not allowed in pattern guards, because matching cannot have -side effects. Side effects could alter the matched object or the environment -on which the match depends in such a way, that the match would not be -exhaustive. For instance, the following would not match any arm if mutable -borrows were allowed: - -```compile_fail,E0301 -match Some(()) { - None => { }, - option if option.take().is_none() => { - /* impossible, option is `Some` */ - }, - Some(_) => { } // When the previous match failed, the option became `None`. -} -``` -"##, - -E0302: r##" -Assignments are not allowed in pattern guards, because matching cannot have -side effects. Side effects could alter the matched object or the environment -on which the match depends in such a way, that the match would not be -exhaustive. For instance, the following would not match any arm if assignments -were allowed: - -```compile_fail,E0302 -match Some(()) { - None => { }, - option if { option = None; false } => { }, - Some(_) => { } // When the previous match failed, the option became `None`. -} -``` -"##, - -E0303: r##" -In certain cases it is possible for sub-bindings to violate memory safety. -Updates to the borrow checker in a future version of Rust may remove this -restriction, but for now patterns must be rewritten without sub-bindings. - -Before: - -```compile_fail,E0303 -match Some("hi".to_string()) { - ref op_string_ref @ Some(s) => {}, - None => {}, -} -``` - -After: - -``` -match Some("hi".to_string()) { - Some(ref s) => { - let op_string_ref = &Some(s); - // ... - }, - None => {}, -} -``` - -The `op_string_ref` binding has type `&Option<&String>` in both cases. - -See also https://github.com/rust-lang/rust/issues/14587 -"##, - -} - - -register_diagnostics! { -// E0298, // cannot compare constants -// E0299, // mismatched types between arms -// E0471, // constant evaluation error (in pattern) -} diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs deleted file mode 100644 index 2a571fa8264..00000000000 --- a/src/librustc_const_eval/eval.rs +++ /dev/null @@ -1,687 +0,0 @@ -// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use rustc::middle::const_val::ConstVal::*; -use rustc::middle::const_val::ConstAggregate::*; -use rustc::middle::const_val::ErrKind::*; -use rustc::middle::const_val::{ByteArray, ConstVal, ConstEvalErr, EvalResult, ErrKind}; - -use rustc::hir::map::blocks::FnLikeNode; -use rustc::hir::def::{Def, CtorKind}; -use rustc::hir::def_id::DefId; -use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::util::IntTypeExt; -use rustc::ty::subst::{Substs, Subst}; -use rustc::util::common::ErrorReported; -use rustc::util::nodemap::NodeMap; - -use syntax::abi::Abi; -use syntax::ast; -use syntax::attr; -use rustc::hir::{self, Expr}; -use syntax_pos::Span; - -use std::cmp::Ordering; - -use rustc_const_math::*; -macro_rules! signal { - ($e:expr, $exn:expr) => { - return Err(ConstEvalErr { span: $e.span, kind: $exn }) - } -} - -macro_rules! math { - ($e:expr, $op:expr) => { - match $op { - Ok(val) => val, - Err(e) => signal!($e, ErrKind::from(e)), - } - } -} - -/// * `DefId` is the id of the constant. -/// * `Substs` is the monomorphized substitutions for the expression. -pub fn lookup_const_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - key: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>) - -> Option<(DefId, &'tcx Substs<'tcx>)> { - ty::Instance::resolve( - tcx, - key.param_env, - key.value.0, - key.value.1, - ).map(|instance| (instance.def_id(), instance.substs)) -} - -pub struct ConstContext<'a, 'tcx: 'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, - tables: &'a ty::TypeckTables<'tcx>, - param_env: ty::ParamEnv<'tcx>, - substs: &'tcx Substs<'tcx>, - fn_args: Option>> -} - -impl<'a, 'tcx> ConstContext<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, - param_env_and_substs: ty::ParamEnvAnd<'tcx, &'tcx Substs<'tcx>>, - tables: &'a ty::TypeckTables<'tcx>) - -> Self { - ConstContext { - tcx, - param_env: param_env_and_substs.param_env, - tables, - substs: param_env_and_substs.value, - fn_args: None - } - } - - /// Evaluate a constant expression in a context where the expression isn't - /// guaranteed to be evaluable. - pub fn eval(&self, e: &'tcx Expr) -> EvalResult<'tcx> { - if self.tables.tainted_by_errors { - signal!(e, TypeckError); - } - eval_const_expr_partial(self, e) - } -} - -type CastResult<'tcx> = Result, ErrKind<'tcx>>; - -fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, - e: &'tcx Expr) -> EvalResult<'tcx> { - trace!("eval_const_expr_partial: {:?}", e); - let tcx = cx.tcx; - let ty = cx.tables.expr_ty(e).subst(tcx, cx.substs); - let mk_const = |val| tcx.mk_const(ty::Const { val, ty }); - - let result = match e.node { - hir::ExprUnary(hir::UnNeg, ref inner) => { - // unary neg literals already got their sign during creation - if let hir::ExprLit(ref lit) = inner.node { - use syntax::ast::*; - use syntax::ast::LitIntType::*; - const I8_OVERFLOW: u128 = i8::min_value() as u8 as u128; - const I16_OVERFLOW: u128 = i16::min_value() as u16 as u128; - const I32_OVERFLOW: u128 = i32::min_value() as u32 as u128; - const I64_OVERFLOW: u128 = i64::min_value() as u64 as u128; - const I128_OVERFLOW: u128 = i128::min_value() as u128; - let negated = match (&lit.node, &ty.sty) { - (&LitKind::Int(I8_OVERFLOW, _), &ty::TyInt(IntTy::I8)) | - (&LitKind::Int(I8_OVERFLOW, Signed(IntTy::I8)), _) => { - Some(I8(i8::min_value())) - }, - (&LitKind::Int(I16_OVERFLOW, _), &ty::TyInt(IntTy::I16)) | - (&LitKind::Int(I16_OVERFLOW, Signed(IntTy::I16)), _) => { - Some(I16(i16::min_value())) - }, - (&LitKind::Int(I32_OVERFLOW, _), &ty::TyInt(IntTy::I32)) | - (&LitKind::Int(I32_OVERFLOW, Signed(IntTy::I32)), _) => { - Some(I32(i32::min_value())) - }, - (&LitKind::Int(I64_OVERFLOW, _), &ty::TyInt(IntTy::I64)) | - (&LitKind::Int(I64_OVERFLOW, Signed(IntTy::I64)), _) => { - Some(I64(i64::min_value())) - }, - (&LitKind::Int(I128_OVERFLOW, _), &ty::TyInt(IntTy::I128)) | - (&LitKind::Int(I128_OVERFLOW, Signed(IntTy::I128)), _) => { - Some(I128(i128::min_value())) - }, - (&LitKind::Int(n, _), &ty::TyInt(IntTy::Isize)) | - (&LitKind::Int(n, Signed(IntTy::Isize)), _) => { - match tcx.sess.target.isize_ty { - IntTy::I16 => if n == I16_OVERFLOW { - Some(Isize(Is16(i16::min_value()))) - } else { - None - }, - IntTy::I32 => if n == I32_OVERFLOW { - Some(Isize(Is32(i32::min_value()))) - } else { - None - }, - IntTy::I64 => if n == I64_OVERFLOW { - Some(Isize(Is64(i64::min_value()))) - } else { - None - }, - _ => span_bug!(e.span, "typeck error") - } - }, - _ => None - }; - if let Some(i) = negated { - return Ok(mk_const(Integral(i))); - } - } - mk_const(match cx.eval(inner)?.val { - Float(f) => Float(-f), - Integral(i) => Integral(math!(e, -i)), - _ => signal!(e, TypeckError) - }) - } - hir::ExprUnary(hir::UnNot, ref inner) => { - mk_const(match cx.eval(inner)?.val { - Integral(i) => Integral(math!(e, !i)), - Bool(b) => Bool(!b), - _ => signal!(e, TypeckError) - }) - } - hir::ExprUnary(hir::UnDeref, _) => signal!(e, UnimplementedConstVal("deref operation")), - hir::ExprBinary(op, ref a, ref b) => { - // technically, if we don't have type hints, but integral eval - // gives us a type through a type-suffix, cast or const def type - // we need to re-eval the other value of the BinOp if it was - // not inferred - mk_const(match (cx.eval(a)?.val, cx.eval(b)?.val) { - (Float(a), Float(b)) => { - use std::cmp::Ordering::*; - match op.node { - hir::BiAdd => Float(math!(e, a + b)), - hir::BiSub => Float(math!(e, a - b)), - hir::BiMul => Float(math!(e, a * b)), - hir::BiDiv => Float(math!(e, a / b)), - hir::BiRem => Float(math!(e, a % b)), - hir::BiEq => Bool(math!(e, a.try_cmp(b)) == Equal), - hir::BiLt => Bool(math!(e, a.try_cmp(b)) == Less), - hir::BiLe => Bool(math!(e, a.try_cmp(b)) != Greater), - hir::BiNe => Bool(math!(e, a.try_cmp(b)) != Equal), - hir::BiGe => Bool(math!(e, a.try_cmp(b)) != Less), - hir::BiGt => Bool(math!(e, a.try_cmp(b)) == Greater), - _ => span_bug!(e.span, "typeck error"), - } - } - (Integral(a), Integral(b)) => { - use std::cmp::Ordering::*; - match op.node { - hir::BiAdd => Integral(math!(e, a + b)), - hir::BiSub => Integral(math!(e, a - b)), - hir::BiMul => Integral(math!(e, a * b)), - hir::BiDiv => Integral(math!(e, a / b)), - hir::BiRem => Integral(math!(e, a % b)), - hir::BiBitAnd => Integral(math!(e, a & b)), - hir::BiBitOr => Integral(math!(e, a | b)), - hir::BiBitXor => Integral(math!(e, a ^ b)), - hir::BiShl => Integral(math!(e, a << b)), - hir::BiShr => Integral(math!(e, a >> b)), - hir::BiEq => Bool(math!(e, a.try_cmp(b)) == Equal), - hir::BiLt => Bool(math!(e, a.try_cmp(b)) == Less), - hir::BiLe => Bool(math!(e, a.try_cmp(b)) != Greater), - hir::BiNe => Bool(math!(e, a.try_cmp(b)) != Equal), - hir::BiGe => Bool(math!(e, a.try_cmp(b)) != Less), - hir::BiGt => Bool(math!(e, a.try_cmp(b)) == Greater), - _ => span_bug!(e.span, "typeck error"), - } - } - (Bool(a), Bool(b)) => { - Bool(match op.node { - hir::BiAnd => a && b, - hir::BiOr => a || b, - hir::BiBitXor => a ^ b, - hir::BiBitAnd => a & b, - hir::BiBitOr => a | b, - hir::BiEq => a == b, - hir::BiNe => a != b, - hir::BiLt => a < b, - hir::BiLe => a <= b, - hir::BiGe => a >= b, - hir::BiGt => a > b, - _ => span_bug!(e.span, "typeck error"), - }) - } - (Char(a), Char(b)) => { - Bool(match op.node { - hir::BiEq => a == b, - hir::BiNe => a != b, - hir::BiLt => a < b, - hir::BiLe => a <= b, - hir::BiGe => a >= b, - hir::BiGt => a > b, - _ => span_bug!(e.span, "typeck error"), - }) - } - - _ => signal!(e, MiscBinaryOp), - }) - } - hir::ExprCast(ref base, _) => { - let base_val = cx.eval(base)?; - let base_ty = cx.tables.expr_ty(base).subst(tcx, cx.substs); - if ty == base_ty { - base_val - } else { - match cast_const(tcx, base_val.val, ty) { - Ok(val) => mk_const(val), - Err(kind) => signal!(e, kind), - } - } - } - hir::ExprPath(ref qpath) => { - let substs = cx.tables.node_substs(e.hir_id).subst(tcx, cx.substs); - match cx.tables.qpath_def(qpath, e.hir_id) { - Def::Const(def_id) | - Def::AssociatedConst(def_id) => { - let substs = tcx.normalize_associated_type_in_env(&substs, cx.param_env); - match tcx.at(e.span).const_eval(cx.param_env.and((def_id, substs))) { - Ok(val) => val, - Err(ConstEvalErr { kind: TypeckError, .. }) => { - signal!(e, TypeckError); - } - Err(err) => { - debug!("bad reference: {:?}, {:?}", err.description(), err.span); - signal!(e, ErroneousReferencedConstant(box err)) - }, - } - }, - Def::VariantCtor(variant_def, CtorKind::Const) => { - mk_const(Variant(variant_def)) - } - Def::VariantCtor(_, CtorKind::Fn) => { - signal!(e, UnimplementedConstVal("enum variants")); - } - Def::StructCtor(_, CtorKind::Const) => { - mk_const(Aggregate(Struct(&[]))) - } - Def::StructCtor(_, CtorKind::Fn) => { - signal!(e, UnimplementedConstVal("tuple struct constructors")) - } - Def::Local(id) => { - debug!("Def::Local({:?}): {:?}", id, cx.fn_args); - if let Some(&val) = cx.fn_args.as_ref().and_then(|args| args.get(&id)) { - val - } else { - signal!(e, NonConstPath); - } - }, - Def::Method(id) | Def::Fn(id) => mk_const(Function(id, substs)), - Def::Err => span_bug!(e.span, "typeck error"), - _ => signal!(e, NonConstPath), - } - } - hir::ExprCall(ref callee, ref args) => { - let (def_id, substs) = match cx.eval(callee)?.val { - Function(def_id, substs) => (def_id, substs), - _ => signal!(e, TypeckError), - }; - - if tcx.fn_sig(def_id).abi() == Abi::RustIntrinsic { - let layout_of = |ty: Ty<'tcx>| { - let ty = tcx.erase_regions(&ty); - tcx.at(e.span).layout_of(cx.param_env.and(ty)).map_err(|err| { - ConstEvalErr { span: e.span, kind: LayoutError(err) } - }) - }; - match &tcx.item_name(def_id)[..] { - "size_of" => { - let size = layout_of(substs.type_at(0))?.size.bytes(); - return Ok(mk_const(Integral(Usize(ConstUsize::new(size, - tcx.sess.target.usize_ty).unwrap())))); - } - "min_align_of" => { - let align = layout_of(substs.type_at(0))?.align.abi(); - return Ok(mk_const(Integral(Usize(ConstUsize::new(align, - tcx.sess.target.usize_ty).unwrap())))); - } - "type_id" => { - let type_id = tcx.type_id_hash(substs.type_at(0)); - return Ok(mk_const(Integral(U64(type_id)))); - } - _ => signal!(e, TypeckError) - } - } - - let body = if let Some(node_id) = tcx.hir.as_local_node_id(def_id) { - if let Some(fn_like) = FnLikeNode::from_node(tcx.hir.get(node_id)) { - if fn_like.constness() == hir::Constness::Const { - tcx.hir.body(fn_like.body()) - } else { - signal!(e, TypeckError) - } - } else { - signal!(e, TypeckError) - } - } else { - if tcx.is_const_fn(def_id) { - tcx.extern_const_body(def_id).body - } else { - signal!(e, TypeckError) - } - }; - - let arg_ids = body.arguments.iter().map(|arg| match arg.pat.node { - hir::PatKind::Binding(_, canonical_id, _, _) => Some(canonical_id), - _ => None - }).collect::>(); - assert_eq!(arg_ids.len(), args.len()); - - let mut call_args = NodeMap(); - for (arg, arg_expr) in arg_ids.into_iter().zip(args.iter()) { - let arg_val = cx.eval(arg_expr)?; - debug!("const call arg: {:?}", arg); - if let Some(id) = arg { - assert!(call_args.insert(id, arg_val).is_none()); - } - } - debug!("const call({:?})", call_args); - let callee_cx = ConstContext { - tcx, - param_env: cx.param_env, - tables: tcx.typeck_tables_of(def_id), - substs, - fn_args: Some(call_args) - }; - callee_cx.eval(&body.value)? - }, - hir::ExprLit(ref lit) => match lit_to_const(&lit.node, tcx, ty) { - Ok(val) => mk_const(val), - Err(err) => signal!(e, err), - }, - hir::ExprBlock(ref block) => { - match block.expr { - Some(ref expr) => cx.eval(expr)?, - None => mk_const(Aggregate(Tuple(&[]))), - } - } - hir::ExprType(ref e, _) => cx.eval(e)?, - hir::ExprTup(ref fields) => { - let values = fields.iter().map(|e| cx.eval(e)).collect::, _>>()?; - mk_const(Aggregate(Tuple(tcx.alloc_const_slice(&values)))) - } - hir::ExprStruct(_, ref fields, _) => { - mk_const(Aggregate(Struct(tcx.alloc_name_const_slice(&fields.iter().map(|f| { - cx.eval(&f.expr).map(|v| (f.name.node, v)) - }).collect::, _>>()?)))) - } - hir::ExprIndex(ref arr, ref idx) => { - if !tcx.features().const_indexing { - signal!(e, IndexOpFeatureGated); - } - let arr = cx.eval(arr)?; - let idx = match cx.eval(idx)?.val { - Integral(Usize(i)) => i.as_u64(), - _ => signal!(idx, IndexNotUsize), - }; - assert_eq!(idx as usize as u64, idx); - match arr.val { - Aggregate(Array(v)) => { - if let Some(&elem) = v.get(idx as usize) { - elem - } else { - let n = v.len() as u64; - signal!(e, IndexOutOfBounds { len: n, index: idx }) - } - } - - Aggregate(Repeat(.., n)) if idx >= n => { - signal!(e, IndexOutOfBounds { len: n, index: idx }) - } - Aggregate(Repeat(elem, _)) => elem, - - ByteStr(b) if idx >= b.data.len() as u64 => { - signal!(e, IndexOutOfBounds { len: b.data.len() as u64, index: idx }) - } - ByteStr(b) => { - mk_const(Integral(U8(b.data[idx as usize]))) - }, - - _ => signal!(e, IndexedNonVec), - } - } - hir::ExprArray(ref v) => { - let values = v.iter().map(|e| cx.eval(e)).collect::, _>>()?; - mk_const(Aggregate(Array(tcx.alloc_const_slice(&values)))) - } - hir::ExprRepeat(ref elem, _) => { - let n = match ty.sty { - ty::TyArray(_, n) => n.val.to_const_int().unwrap().to_u64().unwrap(), - _ => span_bug!(e.span, "typeck error") - }; - mk_const(Aggregate(Repeat(cx.eval(elem)?, n))) - }, - hir::ExprTupField(ref base, index) => { - if let Aggregate(Tuple(fields)) = cx.eval(base)?.val { - fields[index.node] - } else { - signal!(base, ExpectedConstTuple); - } - } - hir::ExprField(ref base, field_name) => { - if let Aggregate(Struct(fields)) = cx.eval(base)?.val { - if let Some(&(_, f)) = fields.iter().find(|&&(name, _)| name == field_name.node) { - f - } else { - signal!(e, MissingStructField); - } - } else { - signal!(base, ExpectedConstStruct); - } - } - hir::ExprAddrOf(..) => signal!(e, UnimplementedConstVal("address operator")), - _ => signal!(e, MiscCatchAll) - }; - - Ok(result) -} - -fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - val: ConstInt, - ty: Ty<'tcx>) - -> CastResult<'tcx> { - let v = val.to_u128_unchecked(); - match ty.sty { - ty::TyBool if v == 0 => Ok(Bool(false)), - ty::TyBool if v == 1 => Ok(Bool(true)), - ty::TyInt(ast::IntTy::I8) => Ok(Integral(I8(v as i128 as i8))), - ty::TyInt(ast::IntTy::I16) => Ok(Integral(I16(v as i128 as i16))), - ty::TyInt(ast::IntTy::I32) => Ok(Integral(I32(v as i128 as i32))), - ty::TyInt(ast::IntTy::I64) => Ok(Integral(I64(v as i128 as i64))), - ty::TyInt(ast::IntTy::I128) => Ok(Integral(I128(v as i128))), - ty::TyInt(ast::IntTy::Isize) => { - Ok(Integral(Isize(ConstIsize::new_truncating(v as i128, tcx.sess.target.isize_ty)))) - }, - ty::TyUint(ast::UintTy::U8) => Ok(Integral(U8(v as u8))), - ty::TyUint(ast::UintTy::U16) => Ok(Integral(U16(v as u16))), - ty::TyUint(ast::UintTy::U32) => Ok(Integral(U32(v as u32))), - ty::TyUint(ast::UintTy::U64) => Ok(Integral(U64(v as u64))), - ty::TyUint(ast::UintTy::U128) => Ok(Integral(U128(v as u128))), - ty::TyUint(ast::UintTy::Usize) => { - Ok(Integral(Usize(ConstUsize::new_truncating(v, tcx.sess.target.usize_ty)))) - }, - ty::TyFloat(fty) => { - if let Some(i) = val.to_u128() { - Ok(Float(ConstFloat::from_u128(i, fty))) - } else { - // The value must be negative, go through signed integers. - let i = val.to_u128_unchecked() as i128; - Ok(Float(ConstFloat::from_i128(i, fty))) - } - } - ty::TyRawPtr(_) => Err(ErrKind::UnimplementedConstVal("casting an address to a raw ptr")), - ty::TyChar => match val { - U8(u) => Ok(Char(u as char)), - _ => bug!(), - }, - _ => Err(CannotCast), - } -} - -fn cast_const_float<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - val: ConstFloat, - ty: Ty<'tcx>) -> CastResult<'tcx> { - let int_width = |ty| { - ty::layout::Integer::from_attr(tcx, ty).size().bits() as usize - }; - match ty.sty { - ty::TyInt(ity) => { - if let Some(i) = val.to_i128(int_width(attr::SignedInt(ity))) { - cast_const_int(tcx, I128(i), ty) - } else { - Err(CannotCast) - } - } - ty::TyUint(uty) => { - if let Some(i) = val.to_u128(int_width(attr::UnsignedInt(uty))) { - cast_const_int(tcx, U128(i), ty) - } else { - Err(CannotCast) - } - } - ty::TyFloat(fty) => Ok(Float(val.convert(fty))), - _ => Err(CannotCast), - } -} - -fn cast_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - val: ConstVal<'tcx>, - ty: Ty<'tcx>) - -> CastResult<'tcx> { - match val { - Integral(i) => cast_const_int(tcx, i, ty), - Bool(b) => cast_const_int(tcx, U8(b as u8), ty), - Float(f) => cast_const_float(tcx, f, ty), - Char(c) => cast_const_int(tcx, U32(c as u32), ty), - Variant(v) => { - let adt = tcx.adt_def(tcx.parent_def_id(v).unwrap()); - let idx = adt.variant_index_with_id(v); - cast_const_int(tcx, adt.discriminant_for_variant(tcx, idx), ty) - } - Function(..) => Err(UnimplementedConstVal("casting fn pointers")), - ByteStr(b) => match ty.sty { - ty::TyRawPtr(_) => { - Err(ErrKind::UnimplementedConstVal("casting a bytestr to a raw ptr")) - }, - ty::TyRef(_, ty::TypeAndMut { ref ty, mutbl: hir::MutImmutable }) => match ty.sty { - ty::TyArray(ty, n) => { - let n = n.val.to_const_int().unwrap().to_u64().unwrap(); - if ty == tcx.types.u8 && n == b.data.len() as u64 { - Ok(val) - } else { - Err(CannotCast) - } - } - ty::TySlice(_) => { - Err(ErrKind::UnimplementedConstVal("casting a bytestr to slice")) - }, - _ => Err(CannotCast), - }, - _ => Err(CannotCast), - }, - Str(s) => match ty.sty { - ty::TyRawPtr(_) => Err(ErrKind::UnimplementedConstVal("casting a str to a raw ptr")), - ty::TyRef(_, ty::TypeAndMut { ref ty, mutbl: hir::MutImmutable }) => match ty.sty { - ty::TyStr => Ok(Str(s)), - _ => Err(CannotCast), - }, - _ => Err(CannotCast), - }, - _ => Err(CannotCast), - } -} - -fn lit_to_const<'a, 'tcx>(lit: &'tcx ast::LitKind, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - mut ty: Ty<'tcx>) - -> Result, ErrKind<'tcx>> { - use syntax::ast::*; - use syntax::ast::LitIntType::*; - - if let ty::TyAdt(adt, _) = ty.sty { - if adt.is_enum() { - ty = adt.repr.discr_type().to_ty(tcx) - } - } - - match *lit { - LitKind::Str(ref s, _) => Ok(Str(s.as_str())), - LitKind::ByteStr(ref data) => Ok(ByteStr(ByteArray { data })), - LitKind::Byte(n) => Ok(Integral(U8(n))), - LitKind::Int(n, hint) => { - match (&ty.sty, hint) { - (&ty::TyInt(ity), _) | - (_, Signed(ity)) => { - Ok(Integral(ConstInt::new_signed_truncating(n as i128, - ity, tcx.sess.target.isize_ty))) - } - (&ty::TyUint(uty), _) | - (_, Unsigned(uty)) => { - Ok(Integral(ConstInt::new_unsigned_truncating(n as u128, - uty, tcx.sess.target.usize_ty))) - } - _ => bug!() - } - } - LitKind::Float(n, fty) => { - parse_float(&n.as_str(), fty).map(Float) - } - LitKind::FloatUnsuffixed(n) => { - let fty = match ty.sty { - ty::TyFloat(fty) => fty, - _ => bug!() - }; - parse_float(&n.as_str(), fty).map(Float) - } - LitKind::Bool(b) => Ok(Bool(b)), - LitKind::Char(c) => Ok(Char(c)), - } -} - -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)") - }) -} - -pub fn compare_const_vals(tcx: TyCtxt, span: Span, a: &ConstVal, b: &ConstVal) - -> Result -{ - let result = match (a, b) { - (&Integral(a), &Integral(b)) => a.try_cmp(b).ok(), - (&Float(a), &Float(b)) => a.try_cmp(b).ok(), - (&Str(ref a), &Str(ref b)) => Some(a.cmp(b)), - (&Bool(a), &Bool(b)) => Some(a.cmp(&b)), - (&ByteStr(a), &ByteStr(b)) => Some(a.data.cmp(b.data)), - (&Char(a), &Char(b)) => Some(a.cmp(&b)), - _ => None, - }; - - match result { - Some(result) => Ok(result), - None => { - // FIXME: can this ever be reached? - tcx.sess.delay_span_bug(span, - &format!("type mismatch comparing {:?} and {:?}", a, b)); - Err(ErrorReported) - } - } -} - -impl<'a, 'tcx> ConstContext<'a, 'tcx> { - pub fn compare_lit_exprs(&self, - span: Span, - a: &'tcx Expr, - b: &'tcx Expr) -> Result { - let tcx = self.tcx; - let a = match self.eval(a) { - Ok(a) => a, - Err(e) => { - e.report(tcx, a.span, "expression"); - return Err(ErrorReported); - } - }; - let b = match self.eval(b) { - Ok(b) => b, - Err(e) => { - e.report(tcx, b.span, "expression"); - return Err(ErrorReported); - } - }; - compare_const_vals(tcx, span, &a.val, &b.val) - } -} diff --git a/src/librustc_const_math/int.rs b/src/librustc_const_math/int.rs deleted file mode 100644 index 4ec27d7ade5..00000000000 --- a/src/librustc_const_math/int.rs +++ /dev/null @@ -1,590 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use std::cmp::Ordering; -use syntax::attr::IntType; -use syntax::ast::{IntTy, UintTy}; - -use super::isize::*; -use super::usize::*; -use super::err::*; - -#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, Hash, Eq, PartialEq)] -pub enum ConstInt { - I8(i8), - I16(i16), - I32(i32), - I64(i64), - I128(i128), - Isize(ConstIsize), - U8(u8), - U16(u16), - U32(u32), - U64(u64), - U128(u128), - Usize(ConstUsize), -} -pub use self::ConstInt::*; - - -macro_rules! bounds { - ($ct: ty, $($t:ident $min:ident $max:ident)*) => { - $( - pub const $min: $ct = $t::min_value() as $ct; - pub const $max: $ct = $t::max_value() as $ct; - )* - }; - ($ct: ty: $min_val: expr, $($t:ident $min:ident $max:ident)*) => { - $( - pub const $min: $ct = $min_val; - pub const $max: $ct = $t::max_value() as $ct; - )* - } -} - -mod ubounds { - #![allow(dead_code)] - bounds!{u128: 0, - i8 I8MIN I8MAX i16 I16MIN I16MAX i32 I32MIN I32MAX i64 I64MIN I64MAX i128 I128MIN I128MAX - u8 U8MIN U8MAX u16 U16MIN U16MAX u32 U32MIN U32MAX u64 U64MIN U64MAX u128 U128MIN U128MAX - // do not add constants for isize/usize, because these are guaranteed to be wrong for - // arbitrary host/target combinations - } -} - -mod ibounds { - #![allow(dead_code)] - bounds!(i128, u64 U64MIN U64MAX); - - pub const U128MIN: i128 = 0; - pub const U128MAX: i128 = i128::max_value(); - - bounds!{i128, - i8 I8MIN I8MAX i16 I16MIN I16MAX i32 I32MIN I32MAX i64 I64MIN I64MAX i128 I128MIN I128MAX - u8 U8MIN U8MAX u16 U16MIN U16MAX u32 U32MIN U32MAX - // do not add constants for isize/usize, because these are guaranteed to be wrong for - // arbitrary host/target combinations - } -} - -impl ConstInt { - /// Creates a new unsigned ConstInt with matching type while also checking that overflow does - /// not happen. - pub fn new_unsigned(val: u128, ty: UintTy, usize_ty: UintTy) -> Option { - match ty { - UintTy::U8 if val <= ubounds::U8MAX => Some(U8(val as u8)), - UintTy::U16 if val <= ubounds::U16MAX => Some(U16(val as u16)), - UintTy::U32 if val <= ubounds::U32MAX => Some(U32(val as u32)), - UintTy::U64 if val <= ubounds::U64MAX => Some(U64(val as u64)), - UintTy::Usize if val <= ubounds::U64MAX => ConstUsize::new(val as u64, usize_ty).ok() - .map(Usize), - UintTy::U128 => Some(U128(val)), - _ => None - } - } - - /// Creates a new signed ConstInt with matching type while also checking that overflow does - /// not happen. - pub fn new_signed(val: i128, ty: IntTy, isize_ty: IntTy) -> Option { - match ty { - IntTy::I8 if val <= ibounds::I8MAX => Some(I8(val as i8)), - IntTy::I16 if val <= ibounds::I16MAX => Some(I16(val as i16)), - IntTy::I32 if val <= ibounds::I32MAX => Some(I32(val as i32)), - IntTy::I64 if val <= ibounds::I64MAX => Some(I64(val as i64)), - IntTy::Isize if val <= ibounds::I64MAX => ConstIsize::new(val as i64, isize_ty).ok() - .map(Isize), - IntTy::I128 => Some(I128(val)), - _ => None - } - } - - /// Creates a new unsigned ConstInt with matching type. - pub fn new_unsigned_truncating(val: u128, ty: UintTy, usize_ty: UintTy) -> ConstInt { - match ty { - UintTy::U8 => U8(val as u8), - UintTy::U16 => U16(val as u16), - UintTy::U32 => U32(val as u32), - UintTy::U64 => U64(val as u64), - UintTy::Usize => Usize(ConstUsize::new_truncating(val, usize_ty)), - UintTy::U128 => U128(val) - } - } - - /// Creates a new signed ConstInt with matching type. - pub fn new_signed_truncating(val: i128, ty: IntTy, isize_ty: IntTy) -> ConstInt { - match ty { - IntTy::I8 => I8(val as i8), - IntTy::I16 => I16(val as i16), - IntTy::I32 => I32(val as i32), - IntTy::I64 => I64(val as i64), - IntTy::Isize => Isize(ConstIsize::new_truncating(val, isize_ty)), - IntTy::I128 => I128(val) - } - } - - /// Description of the type, not the value - pub fn description(&self) -> &'static str { - match *self { - I8(_) => "i8", - I16(_) => "i16", - I32(_) => "i32", - I64(_) => "i64", - I128(_) => "i128", - Isize(_) => "isize", - U8(_) => "u8", - U16(_) => "u16", - U32(_) => "u32", - U64(_) => "u64", - U128(_) => "u128", - Usize(_) => "usize", - } - } - - /// Erases the type and returns a u128. - /// This is not the same as `-5i8 as u128` but as `-5i8 as i128 as u128` - pub fn to_u128_unchecked(self) -> u128 { - match self { - I8(i) => i as i128 as u128, - I16(i) => i as i128 as u128, - I32(i) => i as i128 as u128, - I64(i) => i as i128 as u128, - I128(i) => i as i128 as u128, - Isize(Is16(i)) => i as i128 as u128, - Isize(Is32(i)) => i as i128 as u128, - Isize(Is64(i)) => i as i128 as u128, - U8(i) => i as u128, - U16(i) => i as u128, - U32(i) => i as u128, - U64(i) => i as u128, - U128(i) => i as u128, - Usize(Us16(i)) => i as u128, - Usize(Us32(i)) => i as u128, - Usize(Us64(i)) => i as u128, - } - } - - /// Converts the value to a `u32` if it's in the range 0...std::u32::MAX - pub fn to_u32(&self) -> Option { - self.to_u128().and_then(|v| if v <= u32::max_value() as u128 { - Some(v as u32) - } else { - None - }) - } - - /// Converts the value to a `u64` if it's in the range 0...std::u64::MAX - pub fn to_u64(&self) -> Option { - self.to_u128().and_then(|v| if v <= u64::max_value() as u128 { - Some(v as u64) - } else { - None - }) - } - - /// Converts the value to a `u128` if it's in the range 0...std::u128::MAX - pub fn to_u128(&self) -> Option { - match *self { - I8(v) if v >= 0 => Some(v as u128), - I16(v) if v >= 0 => Some(v as u128), - I32(v) if v >= 0 => Some(v as u128), - I64(v) if v >= 0 => Some(v as u128), - I128(v) if v >= 0 => Some(v as u128), - Isize(Is16(v)) if v >= 0 => Some(v as u128), - Isize(Is32(v)) if v >= 0 => Some(v as u128), - Isize(Is64(v)) if v >= 0 => Some(v as u128), - U8(v) => Some(v as u128), - U16(v) => Some(v as u128), - U32(v) => Some(v as u128), - U64(v) => Some(v as u128), - U128(v) => Some(v as u128), - Usize(Us16(v)) => Some(v as u128), - Usize(Us32(v)) => Some(v as u128), - Usize(Us64(v)) => Some(v as u128), - _ => None, - } - } - - pub fn is_negative(&self) -> bool { - match *self { - I8(v) => v < 0, - I16(v) => v < 0, - I32(v) => v < 0, - I64(v) => v < 0, - I128(v) => v < 0, - Isize(Is16(v)) => v < 0, - Isize(Is32(v)) => v < 0, - Isize(Is64(v)) => v < 0, - _ => false, - } - } - - /// Compares the values if they are of the same type - pub fn try_cmp(self, rhs: Self) -> Result<::std::cmp::Ordering, ConstMathErr> { - match (self, rhs) { - (I8(a), I8(b)) => Ok(a.cmp(&b)), - (I16(a), I16(b)) => Ok(a.cmp(&b)), - (I32(a), I32(b)) => Ok(a.cmp(&b)), - (I64(a), I64(b)) => Ok(a.cmp(&b)), - (I128(a), I128(b)) => Ok(a.cmp(&b)), - (Isize(Is16(a)), Isize(Is16(b))) => Ok(a.cmp(&b)), - (Isize(Is32(a)), Isize(Is32(b))) => Ok(a.cmp(&b)), - (Isize(Is64(a)), Isize(Is64(b))) => Ok(a.cmp(&b)), - (U8(a), U8(b)) => Ok(a.cmp(&b)), - (U16(a), U16(b)) => Ok(a.cmp(&b)), - (U32(a), U32(b)) => Ok(a.cmp(&b)), - (U64(a), U64(b)) => Ok(a.cmp(&b)), - (U128(a), U128(b)) => Ok(a.cmp(&b)), - (Usize(Us16(a)), Usize(Us16(b))) => Ok(a.cmp(&b)), - (Usize(Us32(a)), Usize(Us32(b))) => Ok(a.cmp(&b)), - (Usize(Us64(a)), Usize(Us64(b))) => Ok(a.cmp(&b)), - _ => Err(CmpBetweenUnequalTypes), - } - } - - /// Adds 1 to the value and wraps around if the maximum for the type is reached - pub fn wrap_incr(self) -> Self { - macro_rules! add1 { - ($e:expr) => { ($e).wrapping_add(1) } - } - match self { - ConstInt::I8(i) => ConstInt::I8(add1!(i)), - ConstInt::I16(i) => ConstInt::I16(add1!(i)), - ConstInt::I32(i) => ConstInt::I32(add1!(i)), - ConstInt::I64(i) => ConstInt::I64(add1!(i)), - ConstInt::I128(i) => ConstInt::I128(add1!(i)), - ConstInt::Isize(ConstIsize::Is16(i)) => ConstInt::Isize(ConstIsize::Is16(add1!(i))), - ConstInt::Isize(ConstIsize::Is32(i)) => ConstInt::Isize(ConstIsize::Is32(add1!(i))), - ConstInt::Isize(ConstIsize::Is64(i)) => ConstInt::Isize(ConstIsize::Is64(add1!(i))), - ConstInt::U8(i) => ConstInt::U8(add1!(i)), - ConstInt::U16(i) => ConstInt::U16(add1!(i)), - ConstInt::U32(i) => ConstInt::U32(add1!(i)), - ConstInt::U64(i) => ConstInt::U64(add1!(i)), - ConstInt::U128(i) => ConstInt::U128(add1!(i)), - ConstInt::Usize(ConstUsize::Us16(i)) => ConstInt::Usize(ConstUsize::Us16(add1!(i))), - ConstInt::Usize(ConstUsize::Us32(i)) => ConstInt::Usize(ConstUsize::Us32(add1!(i))), - ConstInt::Usize(ConstUsize::Us64(i)) => ConstInt::Usize(ConstUsize::Us64(add1!(i))), - } - } - - pub fn int_type(self) -> IntType { - match self { - ConstInt::I8(_) => IntType::SignedInt(IntTy::I8), - ConstInt::I16(_) => IntType::SignedInt(IntTy::I16), - ConstInt::I32(_) => IntType::SignedInt(IntTy::I32), - ConstInt::I64(_) => IntType::SignedInt(IntTy::I64), - ConstInt::I128(_) => IntType::SignedInt(IntTy::I128), - ConstInt::Isize(_) => IntType::SignedInt(IntTy::Isize), - ConstInt::U8(_) => IntType::UnsignedInt(UintTy::U8), - ConstInt::U16(_) => IntType::UnsignedInt(UintTy::U16), - ConstInt::U32(_) => IntType::UnsignedInt(UintTy::U32), - ConstInt::U64(_) => IntType::UnsignedInt(UintTy::U64), - ConstInt::U128(_) => IntType::UnsignedInt(UintTy::U128), - ConstInt::Usize(_) => IntType::UnsignedInt(UintTy::Usize), - } - } -} - -impl ::std::cmp::PartialOrd for ConstInt { - fn partial_cmp(&self, other: &Self) -> Option { - self.try_cmp(*other).ok() - } -} - -impl ::std::cmp::Ord for ConstInt { - fn cmp(&self, other: &Self) -> Ordering { - self.try_cmp(*other).unwrap() - } -} - -impl ::std::fmt::Display for ConstInt { - fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { - match *self { - I8(i) => write!(fmt, "{}i8", i), - I16(i) => write!(fmt, "{}i16", i), - I32(i) => write!(fmt, "{}i32", i), - I64(i) => write!(fmt, "{}i64", i), - I128(i) => write!(fmt, "{}i128", i), - Isize(i) => write!(fmt, "{}isize", i), - U8(i) => write!(fmt, "{}u8", i), - U16(i) => write!(fmt, "{}u16", i), - U32(i) => write!(fmt, "{}u32", i), - U64(i) => write!(fmt, "{}u64", i), - U128(i) => write!(fmt, "{}u128", i), - Usize(i) => write!(fmt, "{}usize", i), - } - } -} - -macro_rules! overflowing { - ($e:expr, $err:expr) => {{ - if $e.1 { - return Err(Overflow($err)); - } else { - $e.0 - } - }} -} - -macro_rules! impl_binop { - ($op:ident, $func:ident, $checked_func:ident) => { - impl ::std::ops::$op for ConstInt { - type Output = Result; - fn $func(self, rhs: Self) -> Result { - match (self, rhs) { - (I8(a), I8(b)) => a.$checked_func(b).map(I8), - (I16(a), I16(b)) => a.$checked_func(b).map(I16), - (I32(a), I32(b)) => a.$checked_func(b).map(I32), - (I64(a), I64(b)) => a.$checked_func(b).map(I64), - (I128(a), I128(b)) => a.$checked_func(b).map(I128), - (Isize(Is16(a)), Isize(Is16(b))) => a.$checked_func(b).map(Is16).map(Isize), - (Isize(Is32(a)), Isize(Is32(b))) => a.$checked_func(b).map(Is32).map(Isize), - (Isize(Is64(a)), Isize(Is64(b))) => a.$checked_func(b).map(Is64).map(Isize), - (U8(a), U8(b)) => a.$checked_func(b).map(U8), - (U16(a), U16(b)) => a.$checked_func(b).map(U16), - (U32(a), U32(b)) => a.$checked_func(b).map(U32), - (U64(a), U64(b)) => a.$checked_func(b).map(U64), - (U128(a), U128(b)) => a.$checked_func(b).map(U128), - (Usize(Us16(a)), Usize(Us16(b))) => a.$checked_func(b).map(Us16).map(Usize), - (Usize(Us32(a)), Usize(Us32(b))) => a.$checked_func(b).map(Us32).map(Usize), - (Usize(Us64(a)), Usize(Us64(b))) => a.$checked_func(b).map(Us64).map(Usize), - _ => return Err(UnequalTypes(Op::$op)), - }.ok_or(Overflow(Op::$op)) - } - } - } -} - -macro_rules! derive_binop { - ($op:ident, $func:ident) => { - impl ::std::ops::$op for ConstInt { - type Output = Result; - fn $func(self, rhs: Self) -> Result { - match (self, rhs) { - (I8(a), I8(b)) => Ok(I8(a.$func(b))), - (I16(a), I16(b)) => Ok(I16(a.$func(b))), - (I32(a), I32(b)) => Ok(I32(a.$func(b))), - (I64(a), I64(b)) => Ok(I64(a.$func(b))), - (I128(a), I128(b)) => Ok(I128(a.$func(b))), - (Isize(Is16(a)), Isize(Is16(b))) => Ok(Isize(Is16(a.$func(b)))), - (Isize(Is32(a)), Isize(Is32(b))) => Ok(Isize(Is32(a.$func(b)))), - (Isize(Is64(a)), Isize(Is64(b))) => Ok(Isize(Is64(a.$func(b)))), - (U8(a), U8(b)) => Ok(U8(a.$func(b))), - (U16(a), U16(b)) => Ok(U16(a.$func(b))), - (U32(a), U32(b)) => Ok(U32(a.$func(b))), - (U64(a), U64(b)) => Ok(U64(a.$func(b))), - (U128(a), U128(b)) => Ok(U128(a.$func(b))), - (Usize(Us16(a)), Usize(Us16(b))) => Ok(Usize(Us16(a.$func(b)))), - (Usize(Us32(a)), Usize(Us32(b))) => Ok(Usize(Us32(a.$func(b)))), - (Usize(Us64(a)), Usize(Us64(b))) => Ok(Usize(Us64(a.$func(b)))), - _ => Err(UnequalTypes(Op::$op)), - } - } - } - } -} - -impl_binop!(Add, add, checked_add); -impl_binop!(Sub, sub, checked_sub); -impl_binop!(Mul, mul, checked_mul); -derive_binop!(BitAnd, bitand); -derive_binop!(BitOr, bitor); -derive_binop!(BitXor, bitxor); - -const I128_MIN: i128 = ::std::i128::MIN; - -fn check_division( - lhs: ConstInt, - rhs: ConstInt, - op: Op, - zerr: ConstMathErr, -) -> Result<(), ConstMathErr> { - match (lhs, rhs) { - (I8(_), I8(0)) => Err(zerr), - (I16(_), I16(0)) => Err(zerr), - (I32(_), I32(0)) => Err(zerr), - (I64(_), I64(0)) => Err(zerr), - (I128(_), I128(0)) => Err(zerr), - (Isize(_), Isize(Is16(0))) => Err(zerr), - (Isize(_), Isize(Is32(0))) => Err(zerr), - (Isize(_), Isize(Is64(0))) => Err(zerr), - - (U8(_), U8(0)) => Err(zerr), - (U16(_), U16(0)) => Err(zerr), - (U32(_), U32(0)) => Err(zerr), - (U64(_), U64(0)) => Err(zerr), - (U128(_), U128(0)) => Err(zerr), - (Usize(_), Usize(Us16(0))) => Err(zerr), - (Usize(_), Usize(Us32(0))) => Err(zerr), - (Usize(_), Usize(Us64(0))) => Err(zerr), - - (I8(::std::i8::MIN), I8(-1)) => Err(Overflow(op)), - (I16(::std::i16::MIN), I16(-1)) => Err(Overflow(op)), - (I32(::std::i32::MIN), I32(-1)) => Err(Overflow(op)), - (I64(::std::i64::MIN), I64(-1)) => Err(Overflow(op)), - (I128(I128_MIN), I128(-1)) => Err(Overflow(op)), - (Isize(Is16(::std::i16::MIN)), Isize(Is16(-1))) => Err(Overflow(op)), - (Isize(Is32(::std::i32::MIN)), Isize(Is32(-1))) => Err(Overflow(op)), - (Isize(Is64(::std::i64::MIN)), Isize(Is64(-1))) => Err(Overflow(op)), - - _ => Ok(()), - } -} - -impl ::std::ops::Div for ConstInt { - type Output = Result; - fn div(self, rhs: Self) -> Result { - let (lhs, rhs) = (self, rhs); - check_division(lhs, rhs, Op::Div, DivisionByZero)?; - match (lhs, rhs) { - (I8(a), I8(b)) => Ok(I8(a/b)), - (I16(a), I16(b)) => Ok(I16(a/b)), - (I32(a), I32(b)) => Ok(I32(a/b)), - (I64(a), I64(b)) => Ok(I64(a/b)), - (I128(a), I128(b)) => Ok(I128(a/b)), - (Isize(Is16(a)), Isize(Is16(b))) => Ok(Isize(Is16(a/b))), - (Isize(Is32(a)), Isize(Is32(b))) => Ok(Isize(Is32(a/b))), - (Isize(Is64(a)), Isize(Is64(b))) => Ok(Isize(Is64(a/b))), - - (U8(a), U8(b)) => Ok(U8(a/b)), - (U16(a), U16(b)) => Ok(U16(a/b)), - (U32(a), U32(b)) => Ok(U32(a/b)), - (U64(a), U64(b)) => Ok(U64(a/b)), - (U128(a), U128(b)) => Ok(U128(a/b)), - (Usize(Us16(a)), Usize(Us16(b))) => Ok(Usize(Us16(a/b))), - (Usize(Us32(a)), Usize(Us32(b))) => Ok(Usize(Us32(a/b))), - (Usize(Us64(a)), Usize(Us64(b))) => Ok(Usize(Us64(a/b))), - - _ => Err(UnequalTypes(Op::Div)), - } - } -} - -impl ::std::ops::Rem for ConstInt { - type Output = Result; - fn rem(self, rhs: Self) -> Result { - let (lhs, rhs) = (self, rhs); - // should INT_MIN%-1 be zero or an error? - check_division(lhs, rhs, Op::Rem, RemainderByZero)?; - match (lhs, rhs) { - (I8(a), I8(b)) => Ok(I8(a%b)), - (I16(a), I16(b)) => Ok(I16(a%b)), - (I32(a), I32(b)) => Ok(I32(a%b)), - (I64(a), I64(b)) => Ok(I64(a%b)), - (I128(a), I128(b)) => Ok(I128(a%b)), - (Isize(Is16(a)), Isize(Is16(b))) => Ok(Isize(Is16(a%b))), - (Isize(Is32(a)), Isize(Is32(b))) => Ok(Isize(Is32(a%b))), - (Isize(Is64(a)), Isize(Is64(b))) => Ok(Isize(Is64(a%b))), - - (U8(a), U8(b)) => Ok(U8(a%b)), - (U16(a), U16(b)) => Ok(U16(a%b)), - (U32(a), U32(b)) => Ok(U32(a%b)), - (U64(a), U64(b)) => Ok(U64(a%b)), - (U128(a), U128(b)) => Ok(U128(a%b)), - (Usize(Us16(a)), Usize(Us16(b))) => Ok(Usize(Us16(a%b))), - (Usize(Us32(a)), Usize(Us32(b))) => Ok(Usize(Us32(a%b))), - (Usize(Us64(a)), Usize(Us64(b))) => Ok(Usize(Us64(a%b))), - - _ => Err(UnequalTypes(Op::Rem)), - } - } -} - -impl ::std::ops::Shl for ConstInt { - type Output = Result; - fn shl(self, rhs: Self) -> Result { - let b = rhs.to_u32().ok_or(ShiftNegative)?; - match self { - I8(a) => Ok(I8(overflowing!(a.overflowing_shl(b), Op::Shl))), - I16(a) => Ok(I16(overflowing!(a.overflowing_shl(b), Op::Shl))), - I32(a) => Ok(I32(overflowing!(a.overflowing_shl(b), Op::Shl))), - I64(a) => Ok(I64(overflowing!(a.overflowing_shl(b), Op::Shl))), - I128(a) => Ok(I128(overflowing!(a.overflowing_shl(b), Op::Shl))), - Isize(Is16(a)) => Ok(Isize(Is16(overflowing!(a.overflowing_shl(b), Op::Shl)))), - Isize(Is32(a)) => Ok(Isize(Is32(overflowing!(a.overflowing_shl(b), Op::Shl)))), - Isize(Is64(a)) => Ok(Isize(Is64(overflowing!(a.overflowing_shl(b), Op::Shl)))), - U8(a) => Ok(U8(overflowing!(a.overflowing_shl(b), Op::Shl))), - U16(a) => Ok(U16(overflowing!(a.overflowing_shl(b), Op::Shl))), - U32(a) => Ok(U32(overflowing!(a.overflowing_shl(b), Op::Shl))), - U64(a) => Ok(U64(overflowing!(a.overflowing_shl(b), Op::Shl))), - U128(a) => Ok(U128(overflowing!(a.overflowing_shl(b), Op::Shl))), - Usize(Us16(a)) => Ok(Usize(Us16(overflowing!(a.overflowing_shl(b), Op::Shl)))), - Usize(Us32(a)) => Ok(Usize(Us32(overflowing!(a.overflowing_shl(b), Op::Shl)))), - Usize(Us64(a)) => Ok(Usize(Us64(overflowing!(a.overflowing_shl(b), Op::Shl)))), - } - } -} - -impl ::std::ops::Shr for ConstInt { - type Output = Result; - fn shr(self, rhs: Self) -> Result { - let b = rhs.to_u32().ok_or(ShiftNegative)?; - match self { - I8(a) => Ok(I8(overflowing!(a.overflowing_shr(b), Op::Shr))), - I16(a) => Ok(I16(overflowing!(a.overflowing_shr(b), Op::Shr))), - I32(a) => Ok(I32(overflowing!(a.overflowing_shr(b), Op::Shr))), - I64(a) => Ok(I64(overflowing!(a.overflowing_shr(b), Op::Shr))), - I128(a) => Ok(I128(overflowing!(a.overflowing_shr(b), Op::Shr))), - Isize(Is16(a)) => Ok(Isize(Is16(overflowing!(a.overflowing_shr(b), Op::Shr)))), - Isize(Is32(a)) => Ok(Isize(Is32(overflowing!(a.overflowing_shr(b), Op::Shr)))), - Isize(Is64(a)) => Ok(Isize(Is64(overflowing!(a.overflowing_shr(b), Op::Shr)))), - U8(a) => Ok(U8(overflowing!(a.overflowing_shr(b), Op::Shr))), - U16(a) => Ok(U16(overflowing!(a.overflowing_shr(b), Op::Shr))), - U32(a) => Ok(U32(overflowing!(a.overflowing_shr(b), Op::Shr))), - U64(a) => Ok(U64(overflowing!(a.overflowing_shr(b), Op::Shr))), - U128(a) => Ok(U128(overflowing!(a.overflowing_shr(b), Op::Shr))), - Usize(Us16(a)) => Ok(Usize(Us16(overflowing!(a.overflowing_shr(b), Op::Shr)))), - Usize(Us32(a)) => Ok(Usize(Us32(overflowing!(a.overflowing_shr(b), Op::Shr)))), - Usize(Us64(a)) => Ok(Usize(Us64(overflowing!(a.overflowing_shr(b), Op::Shr)))), - } - } -} - -impl ::std::ops::Neg for ConstInt { - type Output = Result; - fn neg(self) -> Result { - match self { - I8(a) => Ok(I8(overflowing!(a.overflowing_neg(), Op::Neg))), - I16(a) => Ok(I16(overflowing!(a.overflowing_neg(), Op::Neg))), - I32(a) => Ok(I32(overflowing!(a.overflowing_neg(), Op::Neg))), - I64(a) => Ok(I64(overflowing!(a.overflowing_neg(), Op::Neg))), - I128(a) => Ok(I128(overflowing!(a.overflowing_neg(), Op::Neg))), - Isize(Is16(a)) => Ok(Isize(Is16(overflowing!(a.overflowing_neg(), Op::Neg)))), - Isize(Is32(a)) => Ok(Isize(Is32(overflowing!(a.overflowing_neg(), Op::Neg)))), - Isize(Is64(a)) => Ok(Isize(Is64(overflowing!(a.overflowing_neg(), Op::Neg)))), - a@U8(0) | a@U16(0) | a@U32(0) | a@U64(0) | a@U128(0) | - a@Usize(Us16(0)) | a@Usize(Us32(0)) | a@Usize(Us64(0)) => Ok(a), - U8(_) | U16(_) | U32(_) | U64(_) | U128(_) | Usize(_) => Err(UnsignedNegation), - } - } -} - -impl ::std::ops::Not for ConstInt { - type Output = Result; - fn not(self) -> Result { - match self { - I8(a) => Ok(I8(!a)), - I16(a) => Ok(I16(!a)), - I32(a) => Ok(I32(!a)), - I64(a) => Ok(I64(!a)), - I128(a) => Ok(I128(!a)), - Isize(Is16(a)) => Ok(Isize(Is16(!a))), - Isize(Is32(a)) => Ok(Isize(Is32(!a))), - Isize(Is64(a)) => Ok(Isize(Is64(!a))), - U8(a) => Ok(U8(!a)), - U16(a) => Ok(U16(!a)), - U32(a) => Ok(U32(!a)), - U64(a) => Ok(U64(!a)), - U128(a) => Ok(U128(!a)), - Usize(Us16(a)) => Ok(Usize(Us16(!a))), - Usize(Us32(a)) => Ok(Usize(Us32(!a))), - Usize(Us64(a)) => Ok(Usize(Us64(!a))), - } - } -} diff --git a/src/librustc_const_math/isize.rs b/src/librustc_const_math/isize.rs deleted file mode 100644 index 18acc782775..00000000000 --- a/src/librustc_const_math/isize.rs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use syntax::ast; -use super::err::*; - -/// Depending on the target only one variant is ever used in a compilation. -/// Anything else is an error. This invariant is checked at several locations -#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, Hash, Eq, PartialEq)] -pub enum ConstIsize { - Is16(i16), - Is32(i32), - Is64(i64), -} -pub use self::ConstIsize::*; - -impl ::std::fmt::Display for ConstIsize { - fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { - write!(fmt, "{}", self.as_i64()) - } -} - -impl ConstIsize { - pub fn as_i64(self) -> i64 { - match self { - Is16(i) => i as i64, - Is32(i) => i as i64, - Is64(i) => i, - } - } - pub fn new(i: i64, isize_ty: ast::IntTy) -> Result { - match isize_ty { - ast::IntTy::I16 if i as i16 as i64 == i => Ok(Is16(i as i16)), - ast::IntTy::I16 => Err(LitOutOfRange(ast::IntTy::Isize)), - ast::IntTy::I32 if i as i32 as i64 == i => Ok(Is32(i as i32)), - ast::IntTy::I32 => Err(LitOutOfRange(ast::IntTy::Isize)), - ast::IntTy::I64 => Ok(Is64(i)), - _ => unreachable!(), - } - } - pub fn new_truncating(i: i128, isize_ty: ast::IntTy) -> Self { - match isize_ty { - ast::IntTy::I16 => Is16(i as i16), - ast::IntTy::I32 => Is32(i as i32), - ast::IntTy::I64 => Is64(i as i64), - _ => unreachable!(), - } - } -} diff --git a/src/librustc_const_math/lib.rs b/src/librustc_const_math/lib.rs index 2d98bc48d28..5555e727a95 100644 --- a/src/librustc_const_math/lib.rs +++ b/src/librustc_const_math/lib.rs @@ -29,13 +29,7 @@ extern crate syntax; extern crate serialize as rustc_serialize; // used by deriving mod float; -mod int; -mod usize; -mod isize; mod err; pub use float::*; -pub use int::*; -pub use usize::*; -pub use isize::*; pub use err::{ConstMathErr, Op}; diff --git a/src/librustc_const_math/usize.rs b/src/librustc_const_math/usize.rs deleted file mode 100644 index 56995f08f05..00000000000 --- a/src/librustc_const_math/usize.rs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use syntax::ast; -use super::err::*; - -/// Depending on the target only one variant is ever used in a compilation. -/// Anything else is an error. This invariant is checked at several locations -#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, Hash, Eq, PartialEq)] -pub enum ConstUsize { - Us16(u16), - Us32(u32), - Us64(u64), -} -pub use self::ConstUsize::*; - -impl ::std::fmt::Display for ConstUsize { - fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { - write!(fmt, "{}", self.as_u64()) - } -} - -impl ConstUsize { - pub fn as_u64(self) -> u64 { - match self { - Us16(i) => i as u64, - Us32(i) => i as u64, - Us64(i) => i, - } - } - pub fn new(i: u64, usize_ty: ast::UintTy) -> Result { - match usize_ty { - ast::UintTy::U16 if i as u16 as u64 == i => Ok(Us16(i as u16)), - ast::UintTy::U16 => Err(ULitOutOfRange(ast::UintTy::Usize)), - ast::UintTy::U32 if i as u32 as u64 == i => Ok(Us32(i as u32)), - ast::UintTy::U32 => Err(ULitOutOfRange(ast::UintTy::Usize)), - ast::UintTy::U64 => Ok(Us64(i)), - _ => unreachable!(), - } - } - pub fn new_truncating(i: u128, usize_ty: ast::UintTy) -> Self { - match usize_ty { - ast::UintTy::U16 => Us16(i as u16), - ast::UintTy::U32 => Us32(i as u32), - ast::UintTy::U64 => Us64(i as u64), - _ => unreachable!(), - } - } -} diff --git a/src/librustc_data_structures/stable_hasher.rs b/src/librustc_data_structures/stable_hasher.rs index d82b712b5b1..70733bc6aed 100644 --- a/src/librustc_data_structures/stable_hasher.rs +++ b/src/librustc_data_structures/stable_hasher.rs @@ -259,6 +259,14 @@ impl HashStable for f64 { } } +impl HashStable for ::std::cmp::Ordering { + fn hash_stable(&self, + ctx: &mut CTX, + hasher: &mut StableHasher) { + (*self as i8).hash_stable(ctx, hasher); + } +} + impl, CTX> HashStable for (T1,) { fn hash_stable(&self, ctx: &mut CTX, diff --git a/src/librustc_driver/Cargo.toml b/src/librustc_driver/Cargo.toml index 802fe61d6f3..6a1d9e56534 100644 --- a/src/librustc_driver/Cargo.toml +++ b/src/librustc_driver/Cargo.toml @@ -17,7 +17,6 @@ rustc = { path = "../librustc" } rustc_allocator = { path = "../librustc_allocator" } rustc_back = { path = "../librustc_back" } rustc_borrowck = { path = "../librustc_borrowck" } -rustc_const_eval = { path = "../librustc_const_eval" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_errors = { path = "../librustc_errors" } rustc_incremental = { path = "../librustc_incremental" } diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 571cc46bc64..c9cf3f3b81f 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -36,8 +36,7 @@ use rustc_typeck as typeck; use rustc_privacy; use rustc_plugin::registry::Registry; use rustc_plugin as plugin; -use rustc_passes::{self, ast_validation, loops, consts, hir_stats}; -use rustc_const_eval::{self, check_match}; +use rustc_passes::{self, ast_validation, loops, rvalue_promotion, hir_stats}; use super::Compilation; use serialize::json; @@ -942,7 +941,6 @@ pub fn default_provide(providers: &mut ty::maps::Providers) { ty::provide(providers); traits::provide(providers); reachable::provide(providers); - rustc_const_eval::provide(providers); rustc_passes::provide(providers); middle::region::provide(providers); cstore::provide(providers); @@ -1038,8 +1036,8 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(trans: &TransCrate, } time(time_passes, - "const checking", - || consts::check_crate(tcx)); + "rvalue promotion", + || rvalue_promotion::check_crate(tcx)); analysis.access_levels = time(time_passes, "privacy checking", || rustc_privacy::check_crate(tcx)); @@ -1050,7 +1048,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(trans: &TransCrate, time(time_passes, "match checking", - || check_match::check_crate(tcx)); + || mir::matchck_crate(tcx)); // this must run before MIR dump, because // "not all control paths return a value" is reported here. diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 4b496fe3db6..8c0e89716cf 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -35,7 +35,6 @@ extern crate rustc; extern crate rustc_allocator; extern crate rustc_back; extern crate rustc_borrowck; -extern crate rustc_const_eval; extern crate rustc_data_structures; extern crate rustc_errors as errors; extern crate rustc_passes; @@ -1566,7 +1565,6 @@ pub fn diagnostics_registry() -> errors::registry::Registry { // FIXME: need to figure out a way to get these back in here // all_errors.extend_from_slice(get_trans(sess).diagnostics()); all_errors.extend_from_slice(&rustc_trans_utils::DIAGNOSTICS); - all_errors.extend_from_slice(&rustc_const_eval::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_metadata::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_passes::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_plugin::DIAGNOSTICS); @@ -1576,8 +1574,14 @@ pub fn diagnostics_registry() -> errors::registry::Registry { Registry::new(&all_errors) } -pub fn main() { +/// This allows tools to enable rust logging without having to magically match rustc's +/// log crate version +pub fn init_rustc_env_logger() { env_logger::init(); +} + +pub fn main() { + init_rustc_env_logger(); let result = run(|| { let args = env::args_os().enumerate() .map(|(i, arg)| arg.into_string().unwrap_or_else(|arg| { diff --git a/src/librustc_lint/Cargo.toml b/src/librustc_lint/Cargo.toml index 9fee2d54e47..5ff891202db 100644 --- a/src/librustc_lint/Cargo.toml +++ b/src/librustc_lint/Cargo.toml @@ -12,6 +12,6 @@ test = false [dependencies] log = "0.4" rustc = { path = "../librustc" } -rustc_const_eval = { path = "../librustc_const_eval" } +rustc_mir = { path = "../librustc_mir"} syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 62ac898337c..831d4fc755f 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -682,78 +682,6 @@ impl EarlyLintPass for DeprecatedAttr { } } -declare_lint! { - pub ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, - Warn, - "floating-point literals cannot be used in patterns" -} - -/// Checks for floating point literals in patterns. -#[derive(Clone)] -pub struct IllegalFloatLiteralPattern; - -impl LintPass for IllegalFloatLiteralPattern { - fn get_lints(&self) -> LintArray { - lint_array!(ILLEGAL_FLOATING_POINT_LITERAL_PATTERN) - } -} - -fn fl_lit_check_expr(cx: &EarlyContext, expr: &ast::Expr) { - use self::ast::{ExprKind, LitKind}; - match expr.node { - ExprKind::Lit(ref l) => { - match l.node { - LitKind::FloatUnsuffixed(..) | - LitKind::Float(..) => { - cx.span_lint(ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, - l.span, - "floating-point literals cannot be used in patterns"); - }, - _ => (), - } - } - // These may occur in patterns - // and can maybe contain float literals - ExprKind::Unary(_, ref f) => fl_lit_check_expr(cx, f), - // Other kinds of exprs can't occur in patterns so we don't have to check them - // (ast_validation will emit an error if they occur) - _ => (), - } -} - -impl EarlyLintPass for IllegalFloatLiteralPattern { - fn check_pat(&mut self, cx: &EarlyContext, pat: &ast::Pat) { - use self::ast::PatKind; - pat.walk(&mut |p| { - match p.node { - // Wildcard patterns and paths are uninteresting for the lint - PatKind::Wild | - PatKind::Path(..) => (), - - // The walk logic recurses inside these - PatKind::Ident(..) | - PatKind::Struct(..) | - PatKind::Tuple(..) | - PatKind::TupleStruct(..) | - PatKind::Ref(..) | - PatKind::Box(..) | - PatKind::Paren(..) | - PatKind::Slice(..) => (), - - // Extract the expressions and check them - PatKind::Lit(ref e) => fl_lit_check_expr(cx, e), - PatKind::Range(ref st, ref en, _) => { - fl_lit_check_expr(cx, st); - fl_lit_check_expr(cx, en); - }, - - PatKind::Mac(_) => bug!("lint must run post-expansion"), - } - true - }); - } -} - declare_lint! { pub UNUSED_DOC_COMMENT, Warn, diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 9dc6d977851..e941f2e4e1c 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -39,11 +39,10 @@ extern crate syntax; extern crate rustc; #[macro_use] extern crate log; -extern crate rustc_const_eval; +extern crate rustc_mir; extern crate syntax_pos; use rustc::lint; -use rustc::middle; use rustc::session; use rustc::util; @@ -107,7 +106,6 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { UnusedParens, UnusedImportBraces, AnonymousParameters, - IllegalFloatLiteralPattern, UnusedDocComment, ); diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 1c4bd0ff4c2..266f322e397 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -14,8 +14,6 @@ use rustc::hir::map as hir_map; use rustc::ty::subst::Substs; use rustc::ty::{self, AdtKind, Ty, TyCtxt}; use rustc::ty::layout::{self, LayoutOf}; -use middle::const_val::ConstVal; -use rustc_const_eval::ConstContext; use util::nodemap::FxHashSet; use lint::{LateContext, LintContext, LintArray}; use lint::{LintPass, LateLintPass}; @@ -23,7 +21,7 @@ use lint::{LintPass, LateLintPass}; use std::cmp; use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64}; -use syntax::ast; +use syntax::{ast, attr}; use syntax::abi::Abi; use syntax_pos::Span; use syntax::codemap; @@ -42,12 +40,6 @@ declare_lint! { "literal out of range for its type" } -declare_lint! { - EXCEEDING_BITSHIFTS, - Deny, - "shift exceeds the type's number of bits" -} - declare_lint! { VARIANT_SIZE_DIFFERENCES, Allow, @@ -69,8 +61,7 @@ impl TypeLimits { impl LintPass for TypeLimits { fn get_lints(&self) -> LintArray { lint_array!(UNUSED_COMPARISONS, - OVERFLOWING_LITERALS, - EXCEEDING_BITSHIFTS) + OVERFLOWING_LITERALS) } } @@ -89,49 +80,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { e.span, "comparison is useless due to type limits"); } - - if binop.node.is_shift() { - let opt_ty_bits = match cx.tables.node_id_to_type(l.hir_id).sty { - ty::TyInt(t) => Some(int_ty_bits(t, cx.sess().target.isize_ty)), - ty::TyUint(t) => Some(uint_ty_bits(t, cx.sess().target.usize_ty)), - _ => None, - }; - - if let Some(bits) = opt_ty_bits { - let exceeding = if let hir::ExprLit(ref lit) = r.node { - if let ast::LitKind::Int(shift, _) = lit.node { - shift as u64 >= bits - } else { - false - } - } else { - // HACK(eddyb) This might be quite inefficient. - // This would be better left to MIR constant propagation, - // perhaps even at trans time (like is the case already - // when the value being shifted is *also* constant). - let parent_item = cx.tcx.hir.get_parent(e.id); - let parent_def_id = cx.tcx.hir.local_def_id(parent_item); - let substs = Substs::identity_for_item(cx.tcx, parent_def_id); - let const_cx = ConstContext::new(cx.tcx, - cx.param_env.and(substs), - cx.tables); - match const_cx.eval(&r) { - Ok(&ty::Const { val: ConstVal::Integral(i), .. }) => { - i.is_negative() || - i.to_u64() - .map(|i| i >= bits) - .unwrap_or(true) - } - _ => false, - } - }; - if exceeding { - cx.span_lint(EXCEEDING_BITSHIFTS, - e.span, - "bitshift exceeds the type's number of bits"); - } - }; - } } hir::ExprLit(ref lit) => { match cx.tables.node_id_to_type(e.hir_id).sty { @@ -290,28 +238,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { } } - fn int_ty_bits(int_ty: ast::IntTy, isize_ty: ast::IntTy) -> u64 { - match int_ty { - ast::IntTy::Isize => int_ty_bits(isize_ty, isize_ty), - ast::IntTy::I8 => 8, - ast::IntTy::I16 => 16 as u64, - ast::IntTy::I32 => 32, - ast::IntTy::I64 => 64, - ast::IntTy::I128 => 128, - } - } - - fn uint_ty_bits(uint_ty: ast::UintTy, usize_ty: ast::UintTy) -> u64 { - match uint_ty { - ast::UintTy::Usize => uint_ty_bits(usize_ty, usize_ty), - ast::UintTy::U8 => 8, - ast::UintTy::U16 => 16, - ast::UintTy::U32 => 32, - ast::UintTy::U64 => 64, - ast::UintTy::U128 => 128, - } - } - fn check_limits(cx: &LateContext, binop: hir::BinOp, l: &hir::Expr, @@ -439,12 +365,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { ) { let (t, actually) = match ty { ty::TyInt(t) => { - let bits = int_ty_bits(t, cx.sess().target.isize_ty); + let ity = attr::IntType::SignedInt(t); + let bits = layout::Integer::from_attr(cx.tcx, ity).size().bits(); let actually = (val << (128 - bits)) as i128 >> (128 - bits); (format!("{:?}", t), actually.to_string()) } ty::TyUint(t) => { - let bits = uint_ty_bits(t, cx.sess().target.usize_ty); + let ity = attr::IntType::UnsignedInt(t); + let bits = layout::Integer::from_attr(cx.tcx, ity).size().bits(); let actually = (val << (128 - bits)) >> (128 - bits); (format!("{:?}", t), actually.to_string()) } 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 60a0d4e03b5..f44703b9335 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -21,14 +21,15 @@ use rustc::middle::cstore::{LinkagePreference, ExternConstBody, use rustc::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel}; use rustc::hir::def::{self, Def, CtorKind}; use rustc::hir::def_id::{CrateNum, DefId, DefIndex, - CRATE_DEF_INDEX, LOCAL_CRATE}; + CRATE_DEF_INDEX, LOCAL_CRATE, LocalDefId}; use rustc::ich::Fingerprint; use rustc::middle::lang_items; -use rustc::mir; +use rustc::mir::{self, interpret}; use rustc::session::Session; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::codec::TyDecoder; use rustc::mir::Mir; +use rustc::util::nodemap::FxHashMap; use std::cell::Ref; use std::collections::BTreeMap; @@ -54,6 +55,9 @@ pub struct DecodeContext<'a, 'tcx: 'a> { last_filemap_index: usize, lazy_state: LazyState, + + // interpreter allocation cache + interpret_alloc_cache: FxHashMap, } /// Abstract over the various ways one can create metadata decoders. @@ -72,6 +76,7 @@ pub trait Metadata<'a, 'tcx>: Copy { tcx, last_filemap_index: 0, lazy_state: LazyState::NoNode, + interpret_alloc_cache: FxHashMap::default(), } } } @@ -268,6 +273,58 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { } } +impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { + #[inline] + fn specialized_decode(&mut self) -> Result { + self.specialized_decode().map(|i| LocalDefId::from_def_id(i)) + } +} + +impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { + fn specialized_decode(&mut self) -> Result { + const MAX1: usize = usize::max_value() - 1; + let tcx = self.tcx.unwrap(); + let pos = self.position(); + match usize::decode(self)? { + ::std::usize::MAX => { + let alloc_id = tcx.interpret_interner.reserve(); + trace!("creating alloc id {:?} at {}", alloc_id, pos); + // insert early to allow recursive allocs + self.interpret_alloc_cache.insert(pos, alloc_id); + + let allocation = interpret::Allocation::decode(self)?; + trace!("decoded alloc {:?} {:#?}", alloc_id, allocation); + let allocation = self.tcx.unwrap().intern_const_alloc(allocation); + tcx.interpret_interner.intern_at_reserved(alloc_id, allocation); + + if let Some(glob) = Option::::decode(self)? { + tcx.interpret_interner.cache(glob, alloc_id); + } + + Ok(alloc_id) + }, + MAX1 => { + trace!("creating fn alloc id at {}", pos); + let instance = ty::Instance::decode(self)?; + trace!("decoded fn alloc instance: {:?}", instance); + let id = tcx.interpret_interner.create_fn_alloc(instance); + trace!("created fn alloc id: {:?}", id); + self.interpret_alloc_cache.insert(pos, id); + Ok(id) + }, + shorthand => { + trace!("loading shorthand {}", shorthand); + if let Some(&alloc_id) = self.interpret_alloc_cache.get(&shorthand) { + return Ok(alloc_id); + } + trace!("shorthand {} not cached, loading entire allocation", shorthand); + // need to load allocation + self.with_position(shorthand, |this| interpret::AllocId::decode(this)) + }, + } + } +} + impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { fn specialized_decode(&mut self) -> Result { let tag = u8::decode(self)?; diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 830121b446f..d9594824174 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -16,14 +16,14 @@ use schema::*; use rustc::middle::cstore::{LinkMeta, LinkagePreference, NativeLibrary, EncodedMetadata}; use rustc::hir::def::CtorKind; -use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefIndex, DefId, LOCAL_CRATE}; +use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefIndex, DefId, LocalDefId, LOCAL_CRATE}; use rustc::hir::map::definitions::DefPathTable; use rustc::ich::Fingerprint; use rustc::middle::dependency_format::Linkage; use rustc::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel, metadata_symbol_name}; use rustc::middle::lang_items; -use rustc::mir; +use rustc::mir::{self, interpret}; use rustc::traits::specialization_graph; use rustc::ty::{self, Ty, TyCtxt, ReprOptions, SymbolName}; use rustc::ty::codec::{self as ty_codec, TyEncoder}; @@ -59,6 +59,7 @@ pub struct EncodeContext<'a, 'tcx: 'a> { lazy_state: LazyState, type_shorthands: FxHashMap, usize>, predicate_shorthands: FxHashMap, usize>, + interpret_alloc_shorthands: FxHashMap, // This is used to speed up Span encoding. filemap_cache: Lrc, @@ -180,12 +181,48 @@ impl<'a, 'tcx> SpecializedEncoder for EncodeContext<'a, 'tcx> { } } +impl<'a, 'tcx> SpecializedEncoder for EncodeContext<'a, 'tcx> { + #[inline] + fn specialized_encode(&mut self, def_id: &LocalDefId) -> Result<(), Self::Error> { + self.specialized_encode(&def_id.to_def_id()) + } +} + impl<'a, 'tcx> SpecializedEncoder> for EncodeContext<'a, 'tcx> { fn specialized_encode(&mut self, ty: &Ty<'tcx>) -> Result<(), Self::Error> { ty_codec::encode_with_shorthand(self, ty, |ecx| &mut ecx.type_shorthands) } } +impl<'a, 'tcx> SpecializedEncoder for EncodeContext<'a, 'tcx> { + fn specialized_encode(&mut self, alloc_id: &interpret::AllocId) -> Result<(), Self::Error> { + trace!("encoding {:?} at {}", alloc_id, self.position()); + if let Some(shorthand) = self.interpret_alloc_shorthands.get(alloc_id).cloned() { + trace!("encoding {:?} as shorthand to {}", alloc_id, shorthand); + return shorthand.encode(self); + } + let start = self.position(); + // cache the allocation shorthand now, because the allocation itself might recursively + // point to itself. + self.interpret_alloc_shorthands.insert(*alloc_id, start); + if let Some(alloc) = self.tcx.interpret_interner.get_alloc(*alloc_id) { + trace!("encoding {:?} with {:#?}", alloc_id, alloc); + usize::max_value().encode(self)?; + alloc.encode(self)?; + self.tcx.interpret_interner + .get_corresponding_static_def_id(*alloc_id) + .encode(self)?; + } else if let Some(fn_instance) = self.tcx.interpret_interner.get_fn(*alloc_id) { + trace!("encoding {:?} with {:#?}", alloc_id, fn_instance); + (usize::max_value() - 1).encode(self)?; + fn_instance.encode(self)?; + } else { + bug!("alloc id without corresponding allocation: {}", alloc_id); + } + Ok(()) + } +} + impl<'a, 'tcx> SpecializedEncoder> for EncodeContext<'a, 'tcx> { fn specialized_encode(&mut self, predicates: &ty::GenericPredicates<'tcx>) @@ -1117,7 +1154,7 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { _ => None, }, mir: match item.node { - hir::ItemStatic(..) if self.tcx.sess.opts.debugging_opts.always_encode_mir => { + hir::ItemStatic(..) => { self.encode_optimized_mir(def_id) } hir::ItemConst(..) => self.encode_optimized_mir(def_id), @@ -1699,6 +1736,7 @@ pub fn encode_metadata<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, type_shorthands: Default::default(), predicate_shorthands: Default::default(), filemap_cache: tcx.sess.codemap().files()[0].clone(), + interpret_alloc_shorthands: Default::default(), }; // Encode the rustc version string in a predictable location. diff --git a/src/librustc_metadata/schema.rs b/src/librustc_metadata/schema.rs index ce94e4f912f..593f08e90bb 100644 --- a/src/librustc_metadata/schema.rs +++ b/src/librustc_metadata/schema.rs @@ -227,9 +227,9 @@ pub struct TraitImpls { pub impls: LazySeq, } -impl<'gcx> HashStable> for TraitImpls { +impl<'a, 'gcx> HashStable> for TraitImpls { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let TraitImpls { trait_id: (krate, def_index), @@ -310,9 +310,9 @@ pub enum EntryKind<'tcx> { AssociatedConst(AssociatedContainer, u8), } -impl<'gcx> HashStable> for EntryKind<'gcx> { +impl<'a, 'gcx> HashStable> for EntryKind<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { diff --git a/src/librustc_mir/Cargo.toml b/src/librustc_mir/Cargo.toml index ea05a513f7e..90a0f18aba3 100644 --- a/src/librustc_mir/Cargo.toml +++ b/src/librustc_mir/Cargo.toml @@ -9,13 +9,13 @@ path = "lib.rs" crate-type = ["dylib"] [dependencies] +arena = { path = "../libarena" } bitflags = "1.0" graphviz = { path = "../libgraphviz" } log = "0.4" log_settings = "0.1.1" rustc = { path = "../librustc" } rustc_back = { path = "../librustc_back" } -rustc_const_eval = { path = "../librustc_const_eval" } rustc_const_math = { path = "../librustc_const_math" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_errors = { path = "../librustc_errors" } diff --git a/src/librustc_mir/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/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index bbf4357e5b0..36e173dd5d6 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -24,7 +24,6 @@ use rustc::traits::{self, FulfillmentContext}; use rustc::ty::error::TypeError; use rustc::ty::fold::TypeFoldable; use rustc::ty::{self, ToPolyTraitRef, Ty, TyCtxt, TypeVariants}; -use rustc::middle::const_val::ConstVal; use rustc::mir::*; use rustc::mir::tcx::PlaceTy; use rustc::mir::visit::{PlaceContext, Visitor}; @@ -258,7 +257,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { // constraints on `'a` and `'b`. These constraints // would be lost if we just look at the normalized // value. - if let ConstVal::Function(def_id, ..) = value.val { + if let ty::TyFnDef(def_id, substs) = value.ty.sty { let tcx = self.tcx(); let type_checker = &mut self.cx; @@ -271,17 +270,6 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { // are transitioning to the miri-based system, we // don't have a handy function for that, so for // now we just ignore `value.val` regions. - let substs = match value.ty.sty { - ty::TyFnDef(ty_def_id, substs) => { - assert_eq!(def_id, ty_def_id); - substs - } - _ => span_bug!( - self.last_span, - "unexpected type for constant function: {:?}", - value.ty - ), - }; let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, substs); @@ -436,7 +424,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { ProjectionElem::Subslice { from, to } => PlaceTy::Ty { ty: match base_ty.sty { ty::TyArray(inner, size) => { - let size = size.val.to_const_int().unwrap().to_u64().unwrap(); + let size = size.val.unwrap_u64(); let min_size = (from as u64) + (to as u64); if let Some(rest_size) = size.checked_sub(min_size) { tcx.mk_array(inner, rest_size) @@ -1013,19 +1001,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } fn is_box_free(&self, operand: &Operand<'tcx>) -> bool { - match operand { - &Operand::Constant(box Constant { - literal: - Literal::Value { - value: - &ty::Const { - val: ConstVal::Function(def_id, _), - .. - }, - .. - }, - .. - }) => Some(def_id) == self.tcx().lang_items().box_free_fn(), + match *operand { + Operand::Constant(ref c) => match c.ty.sty { + ty::TyFnDef(ty_def_id, _) => { + Some(ty_def_id) == self.tcx().lang_items().box_free_fn() + } + _ => false, + }, _ => false, } } @@ -1284,7 +1266,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { self.check_aggregate_rvalue(mir, rvalue, ak, ops, location) } - Rvalue::Repeat(operand, const_usize) => if const_usize.as_u64() > 1 { + Rvalue::Repeat(operand, len) => if *len > 1 { let operand_ty = operand.ty(mir, tcx); let trait_ref = ty::TraitRef { diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index d3cc9527590..1a9064aab1b 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -10,8 +10,6 @@ //! See docs in build/expr/mod.rs -use std; - use rustc_const_math::{ConstMathErr, Op}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::indexed_vec::Idx; @@ -19,12 +17,11 @@ use rustc_data_structures::indexed_vec::Idx; use build::{BlockAnd, BlockAndExtension, Builder}; use build::expr::category::{Category, RvalueFunc}; use hair::*; -use rustc_const_math::{ConstInt, ConstIsize}; use rustc::middle::const_val::ConstVal; use rustc::middle::region; use rustc::ty::{self, Ty}; use rustc::mir::*; -use syntax::ast; +use rustc::mir::interpret::{Value, PrimVal}; use syntax_pos::Span; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { @@ -203,7 +200,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { ty: this.hir.tcx().types.u32, literal: Literal::Value { value: this.hir.tcx().mk_const(ty::Const { - val: ConstVal::Integral(ConstInt::U32(0)), + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(0))), ty: this.hir.tcx().types.u32 }), }, @@ -384,31 +381,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // Helper to get a `-1` value of the appropriate type fn neg_1_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> { - let literal = match ty.sty { - ty::TyInt(ity) => { - let val = match ity { - ast::IntTy::I8 => ConstInt::I8(-1), - ast::IntTy::I16 => ConstInt::I16(-1), - ast::IntTy::I32 => ConstInt::I32(-1), - ast::IntTy::I64 => ConstInt::I64(-1), - ast::IntTy::I128 => ConstInt::I128(-1), - ast::IntTy::Isize => { - let int_ty = self.hir.tcx().sess.target.isize_ty; - let val = ConstIsize::new(-1, int_ty).unwrap(); - ConstInt::Isize(val) - } - }; - - Literal::Value { - value: self.hir.tcx().mk_const(ty::Const { - val: ConstVal::Integral(val), - ty - }) - } - } - _ => { - span_bug!(span, "Invalid type for neg_1_literal: `{:?}`", ty) - } + let bits = self.hir.integer_bit_width(ty); + let n = (!0u128) >> (128 - bits); + let literal = Literal::Value { + value: self.hir.tcx().mk_const(ty::Const { + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(n))), + ty + }) }; self.literal_operand(span, ty, literal) @@ -416,37 +395,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // Helper to get the minimum value of the appropriate type fn minval_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> { - let literal = match ty.sty { - ty::TyInt(ity) => { - let val = match ity { - ast::IntTy::I8 => ConstInt::I8(i8::min_value()), - ast::IntTy::I16 => ConstInt::I16(i16::min_value()), - ast::IntTy::I32 => ConstInt::I32(i32::min_value()), - ast::IntTy::I64 => ConstInt::I64(i64::min_value()), - ast::IntTy::I128 => ConstInt::I128(i128::min_value()), - ast::IntTy::Isize => { - let int_ty = self.hir.tcx().sess.target.isize_ty; - let min = match int_ty { - ast::IntTy::I16 => std::i16::MIN as i64, - ast::IntTy::I32 => std::i32::MIN as i64, - ast::IntTy::I64 => std::i64::MIN, - _ => unreachable!() - }; - let val = ConstIsize::new(min, int_ty).unwrap(); - ConstInt::Isize(val) - } - }; - - Literal::Value { - value: self.hir.tcx().mk_const(ty::Const { - val: ConstVal::Integral(val), - ty - }) - } - } - _ => { - span_bug!(span, "Invalid type for minval_literal: `{:?}`", ty) - } + assert!(ty.is_signed()); + let bits = self.hir.integer_bit_width(ty); + let n = 1 << (bits - 1); + let literal = Literal::Value { + value: self.hir.tcx().mk_const(ty::Const { + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(n))), + ty + }) }; self.literal_operand(span, ty, literal) diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 58ce572ae8d..229e33dcd78 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -354,7 +354,7 @@ enum TestKind<'tcx> { // test the branches of enum SwitchInt { switch_ty: Ty<'tcx>, - options: Vec<&'tcx ty::Const<'tcx>>, + options: Vec, indices: FxHashMap<&'tcx ty::Const<'tcx>, usize>, }, diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index bdcbfc0bdd8..09579eaecb2 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -20,11 +20,10 @@ use build::matches::{Candidate, MatchPair, Test, TestKind}; use hair::*; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::bitvec::BitVector; -use rustc::middle::const_val::ConstVal; use rustc::ty::{self, Ty}; use rustc::ty::util::IntTypeExt; use rustc::mir::*; -use rustc::hir::RangeEnd; +use rustc::hir::{RangeEnd, Mutability}; use syntax_pos::Span; use std::cmp::Ordering; @@ -112,7 +111,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { test_place: &Place<'tcx>, candidate: &Candidate<'pat, 'tcx>, switch_ty: Ty<'tcx>, - options: &mut Vec<&'tcx ty::Const<'tcx>>, + options: &mut Vec, indices: &mut FxHashMap<&'tcx ty::Const<'tcx>, usize>) -> bool { @@ -128,7 +127,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { indices.entry(value) .or_insert_with(|| { - options.push(value); + options.push(value.val.to_raw_bits().expect("switching on int")); options.len() - 1 }); true @@ -174,39 +173,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } } - /// Convert a byte array or byte slice to a byte slice. - fn to_slice_operand(&mut self, - block: BasicBlock, - source_info: SourceInfo, - operand: Operand<'tcx>) - -> Operand<'tcx> - { - let tcx = self.hir.tcx(); - let ty = operand.ty(&self.local_decls, tcx); - debug!("to_slice_operand({:?}, {:?}: {:?})", block, operand, ty); - match ty.sty { - ty::TyRef(region, mt) => match mt.ty.sty { - ty::TyArray(ety, _) => { - let ty = tcx.mk_imm_ref(region, tcx.mk_slice(ety)); - let temp = self.temp(ty, source_info.span); - self.cfg.push_assign(block, source_info, &temp, - Rvalue::Cast(CastKind::Unsize, operand, ty)); - Operand::Move(temp) - } - ty::TySlice(_) => operand, - _ => { - span_bug!(source_info.span, - "bad operand {:?}: {:?} to `to_slice_operand`", operand, ty) - } - } - _ => { - span_bug!(source_info.span, - "bad operand {:?}: {:?} to `to_slice_operand`", operand, ty) - } - } - - } - /// Generates the code to perform a test. pub fn perform_test(&mut self, block: BasicBlock, @@ -231,7 +197,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let tcx = self.hir.tcx(); for (idx, discr) in adt_def.discriminants(tcx).enumerate() { target_blocks.place_back() <- if variants.contains(idx) { - values.push(discr); + values.push(discr.val); *(targets.place_back() <- self.cfg.start_new_block()) } else { if otherwise_block.is_none() { @@ -266,9 +232,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { assert!(options.len() > 0 && options.len() <= 2); let (true_bb, false_bb) = (self.cfg.start_new_block(), self.cfg.start_new_block()); - let ret = match options[0].val { - ConstVal::Bool(true) => vec![true_bb, false_bb], - ConstVal::Bool(false) => vec![false_bb, true_bb], + let ret = match options[0] { + 1 => vec![true_bb, false_bb], + 0 => vec![false_bb, true_bb], v => span_bug!(test.span, "expected boolean value but got {:?}", v) }; (ret, TerminatorKind::if_(self.hir.tcx(), Operand::Copy(place.clone()), @@ -282,13 +248,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { .map(|_| self.cfg.start_new_block()) .chain(Some(otherwise)) .collect(); - let values: Vec<_> = options.iter().map(|v| - v.val.to_const_int().expect("switching on integral") - ).collect(); (targets.clone(), TerminatorKind::SwitchInt { discr: Operand::Copy(place.clone()), switch_ty, - values: From::from(values), + values: options.clone().into(), targets, }) }; @@ -296,41 +259,88 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { ret } - TestKind::Eq { value, ty } => { - let tcx = self.hir.tcx(); + TestKind::Eq { value, mut ty } => { let mut val = Operand::Copy(place.clone()); - - // If we're using b"..." as a pattern, we need to insert an - // unsizing coercion, as the byte string has the type &[u8; N]. - // - // We want to do this even when the scrutinee is a reference to an - // array, so we can call `<[u8]>::eq` rather than having to find an - // `<[u8; N]>::eq`. - let (expect, val) = if let ConstVal::ByteStr(bytes) = value.val { - let array_ty = tcx.mk_array(tcx.types.u8, bytes.data.len() as u64); - let array_ref = tcx.mk_imm_ref(tcx.types.re_static, array_ty); - let array = self.literal_operand(test.span, array_ref, Literal::Value { - value - }); - - let val = self.to_slice_operand(block, source_info, val); - let slice = self.to_slice_operand(block, source_info, array); - (slice, val) - } else { - (self.literal_operand(test.span, ty, Literal::Value { - value - }), val) - }; - - // Use PartialEq::eq for &str and &[u8] slices, instead of BinOp::Eq. + let mut expect = self.literal_operand(test.span, ty, Literal::Value { + value + }); + // Use PartialEq::eq instead of BinOp::Eq + // (the binop can only handle primitives) let fail = self.cfg.start_new_block(); - let ty = expect.ty(&self.local_decls, tcx); - if let ty::TyRef(_, mt) = ty.sty { - assert!(ty.is_slice()); + if !ty.is_scalar() { + // If we're using b"..." as a pattern, we need to insert an + // unsizing coercion, as the byte string has the type &[u8; N]. + // + // We want to do this even when the scrutinee is a reference to an + // array, so we can call `<[u8]>::eq` rather than having to find an + // `<[u8; N]>::eq`. + let unsize = |ty: Ty<'tcx>| match ty.sty { + ty::TyRef(region, tam) => match tam.ty.sty { + ty::TyArray(inner_ty, n) => Some((region, inner_ty, n)), + _ => None, + }, + _ => None, + }; + let opt_ref_ty = unsize(ty); + let opt_ref_test_ty = unsize(value.ty); + let mut place = place.clone(); + match (opt_ref_ty, opt_ref_test_ty) { + // nothing to do, neither is an array + (None, None) => {}, + (Some((region, elem_ty, _)), _) | + (None, Some((region, elem_ty, _))) => { + let tcx = self.hir.tcx(); + // make both a slice + ty = tcx.mk_imm_ref(region, tcx.mk_slice(elem_ty)); + if opt_ref_ty.is_some() { + place = self.temp(ty, test.span); + self.cfg.push_assign(block, source_info, &place, + Rvalue::Cast(CastKind::Unsize, val, ty)); + } + if opt_ref_test_ty.is_some() { + let array = self.literal_operand( + test.span, + value.ty, + Literal::Value { + value + }, + ); + + let slice = self.temp(ty, test.span); + self.cfg.push_assign(block, source_info, &slice, + Rvalue::Cast(CastKind::Unsize, array, ty)); + expect = Operand::Move(slice); + } + }, + } let eq_def_id = self.hir.tcx().lang_items().eq_trait().unwrap(); - let ty = mt.ty; let (mty, method) = self.hir.trait_method(eq_def_id, "eq", ty, &[ty]); + // take the argument by reference + let region_scope = self.topmost_scope(); + let region = self.hir.tcx().mk_region(ty::ReScope(region_scope)); + let tam = ty::TypeAndMut { + ty, + mutbl: Mutability::MutImmutable, + }; + let ref_ty = self.hir.tcx().mk_ref(region, tam); + + // let lhs_ref_place = &lhs; + let ref_rvalue = Rvalue::Ref(region, BorrowKind::Shared, place.clone()); + let lhs_ref_place = self.temp(ref_ty, test.span); + self.cfg.push_assign(block, source_info, &lhs_ref_place, ref_rvalue); + let val = Operand::Move(lhs_ref_place); + + // let rhs_place = rhs; + let rhs_place = self.temp(ty, test.span); + self.cfg.push_assign(block, source_info, &rhs_place, Rvalue::Use(expect)); + + // let rhs_ref_place = &rhs_place; + let ref_rvalue = Rvalue::Ref(region, BorrowKind::Shared, rhs_place); + let rhs_ref_place = self.temp(ref_ty, test.span); + self.cfg.push_assign(block, source_info, &rhs_ref_place, ref_rvalue); + let expect = Operand::Move(rhs_ref_place); + let bool_ty = self.hir.bool_ty(); let eq_result = self.temp(bool_ty, test.span); let eq_block = self.cfg.start_new_block(); diff --git a/src/librustc_mir/build/misc.rs b/src/librustc_mir/build/misc.rs index a3350cb1671..6e10c2307c8 100644 --- a/src/librustc_mir/build/misc.rs +++ b/src/librustc_mir/build/misc.rs @@ -13,12 +13,11 @@ use build::Builder; -use rustc_const_math::{ConstInt, ConstUsize, ConstIsize}; use rustc::middle::const_val::ConstVal; use rustc::ty::{self, Ty}; +use rustc::mir::interpret::{Value, PrimVal}; use rustc::mir::*; -use syntax::ast; use syntax_pos::{Span, DUMMY_SP}; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { @@ -55,63 +54,20 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // Returns a zero literal operand for the appropriate type, works for // bool, char and integers. pub fn zero_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> { - let literal = match ty.sty { - ty::TyBool => { - self.hir.false_literal() - } - ty::TyChar => { - Literal::Value { - value: self.hir.tcx().mk_const(ty::Const { - val: ConstVal::Char('\0'), - ty - }) - } - } - ty::TyUint(ity) => { - let val = match ity { - ast::UintTy::U8 => ConstInt::U8(0), - ast::UintTy::U16 => ConstInt::U16(0), - ast::UintTy::U32 => ConstInt::U32(0), - ast::UintTy::U64 => ConstInt::U64(0), - ast::UintTy::U128 => ConstInt::U128(0), - ast::UintTy::Usize => { - let uint_ty = self.hir.tcx().sess.target.usize_ty; - let val = ConstUsize::new(0, uint_ty).unwrap(); - ConstInt::Usize(val) - } - }; - - Literal::Value { - value: self.hir.tcx().mk_const(ty::Const { - val: ConstVal::Integral(val), - ty - }) - } - } - ty::TyInt(ity) => { - let val = match ity { - ast::IntTy::I8 => ConstInt::I8(0), - ast::IntTy::I16 => ConstInt::I16(0), - ast::IntTy::I32 => ConstInt::I32(0), - ast::IntTy::I64 => ConstInt::I64(0), - ast::IntTy::I128 => ConstInt::I128(0), - ast::IntTy::Isize => { - let int_ty = self.hir.tcx().sess.target.isize_ty; - let val = ConstIsize::new(0, int_ty).unwrap(); - ConstInt::Isize(val) - } - }; - - Literal::Value { - value: self.hir.tcx().mk_const(ty::Const { - val: ConstVal::Integral(val), - ty - }) - } - } + match ty.sty { + ty::TyBool | + ty::TyChar | + ty::TyUint(_) | + ty::TyInt(_) => {} _ => { span_bug!(span, "Invalid type for zero_literal: `{:?}`", ty) } + } + let literal = Literal::Value { + value: self.hir.tcx().mk_const(ty::Const { + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(0))), + ty + }) }; self.literal_operand(span, ty, literal) diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index a325cfe3eaa..23c5499bb63 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -11,7 +11,7 @@ use build; use hair::cx::Cx; -use hair::LintLevel; +use hair::{LintLevel, BindingMode, PatternKind}; use rustc::hir; use rustc::hir::def_id::{DefId, LocalDefId}; use rustc::middle::region; @@ -21,7 +21,6 @@ use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::subst::Substs; use rustc::util::nodemap::NodeMap; use rustc_back::PanicStrategy; -use rustc_const_eval::pattern::{BindingMode, PatternKind}; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; use shim; use std::mem; 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/diagnostics.rs b/src/librustc_mir/diagnostics.rs index 3491faf9cda..2000ebea25d 100644 --- a/src/librustc_mir/diagnostics.rs +++ b/src/librustc_mir/diagnostics.rs @@ -12,6 +12,541 @@ register_long_diagnostics! { + +E0001: r##" +#### Note: this error code is no longer emitted by the compiler. + +This error suggests that the expression arm corresponding to the noted pattern +will never be reached as for all possible values of the expression being +matched, one of the preceding patterns will match. + +This means that perhaps some of the preceding patterns are too general, this +one is too specific or the ordering is incorrect. + +For example, the following `match` block has too many arms: + +``` +match Some(0) { + Some(bar) => {/* ... */} + x => {/* ... */} // This handles the `None` case + _ => {/* ... */} // All possible cases have already been handled +} +``` + +`match` blocks have their patterns matched in order, so, for example, putting +a wildcard arm above a more specific arm will make the latter arm irrelevant. + +Ensure the ordering of the match arm is correct and remove any superfluous +arms. +"##, + +E0002: r##" +#### Note: this error code is no longer emitted by the compiler. + +This error indicates that an empty match expression is invalid because the type +it is matching on is non-empty (there exist values of this type). In safe code +it is impossible to create an instance of an empty type, so empty match +expressions are almost never desired. This error is typically fixed by adding +one or more cases to the match expression. + +An example of an empty type is `enum Empty { }`. So, the following will work: + +``` +enum Empty {} + +fn foo(x: Empty) { + match x { + // empty + } +} +``` + +However, this won't: + +```compile_fail +fn foo(x: Option) { + match x { + // empty + } +} +``` +"##, + +E0004: r##" +This error indicates that the compiler cannot guarantee a matching pattern for +one or more possible inputs to a match expression. Guaranteed matches are +required in order to assign values to match expressions, or alternatively, +determine the flow of execution. Erroneous code example: + +```compile_fail,E0004 +enum Terminator { + HastaLaVistaBaby, + TalkToMyHand, +} + +let x = Terminator::HastaLaVistaBaby; + +match x { // error: non-exhaustive patterns: `HastaLaVistaBaby` not covered + Terminator::TalkToMyHand => {} +} +``` + +If you encounter this error you must alter your patterns so that every possible +value of the input type is matched. For types with a small number of variants +(like enums) you should probably cover all cases explicitly. Alternatively, the +underscore `_` wildcard pattern can be added after all other patterns to match +"anything else". Example: + +``` +enum Terminator { + HastaLaVistaBaby, + TalkToMyHand, +} + +let x = Terminator::HastaLaVistaBaby; + +match x { + Terminator::TalkToMyHand => {} + Terminator::HastaLaVistaBaby => {} +} + +// or: + +match x { + Terminator::TalkToMyHand => {} + _ => {} +} +``` +"##, + +E0005: r##" +Patterns used to bind names must be irrefutable, that is, they must guarantee +that a name will be extracted in all cases. Erroneous code example: + +```compile_fail,E0005 +let x = Some(1); +let Some(y) = x; +// error: refutable pattern in local binding: `None` not covered +``` + +If you encounter this error you probably need to use a `match` or `if let` to +deal with the possibility of failure. Example: + +``` +let x = Some(1); + +match x { + Some(y) => { + // do something + }, + None => {} +} + +// or: + +if let Some(y) = x { + // do something +} +``` +"##, + +E0007: r##" +This error indicates that the bindings in a match arm would require a value to +be moved into more than one location, thus violating unique ownership. Code +like the following is invalid as it requires the entire `Option` to be +moved into a variable called `op_string` while simultaneously requiring the +inner `String` to be moved into a variable called `s`. + +```compile_fail,E0007 +let x = Some("s".to_string()); + +match x { + op_string @ Some(s) => {}, // error: cannot bind by-move with sub-bindings + None => {}, +} +``` + +See also the error E0303. +"##, + +E0008: r##" +Names bound in match arms retain their type in pattern guards. As such, if a +name is bound by move in a pattern, it should also be moved to wherever it is +referenced in the pattern guard code. Doing so however would prevent the name +from being available in the body of the match arm. Consider the following: + +```compile_fail,E0008 +match Some("hi".to_string()) { + Some(s) if s.len() == 0 => {}, // use s. + _ => {}, +} +``` + +The variable `s` has type `String`, and its use in the guard is as a variable of +type `String`. The guard code effectively executes in a separate scope to the +body of the arm, so the value would be moved into this anonymous scope and +therefore becomes unavailable in the body of the arm. + +The problem above can be solved by using the `ref` keyword. + +``` +match Some("hi".to_string()) { + Some(ref s) if s.len() == 0 => {}, + _ => {}, +} +``` + +Though this example seems innocuous and easy to solve, the problem becomes clear +when it encounters functions which consume the value: + +```compile_fail,E0008 +struct A{} + +impl A { + fn consume(self) -> usize { + 0 + } +} + +fn main() { + let a = Some(A{}); + match a { + Some(y) if y.consume() > 0 => {} + _ => {} + } +} +``` + +In this situation, even the `ref` keyword cannot solve it, since borrowed +content cannot be moved. This problem cannot be solved generally. If the value +can be cloned, here is a not-so-specific solution: + +``` +#[derive(Clone)] +struct A{} + +impl A { + fn consume(self) -> usize { + 0 + } +} + +fn main() { + let a = Some(A{}); + match a{ + Some(ref y) if y.clone().consume() > 0 => {} + _ => {} + } +} +``` + +If the value will be consumed in the pattern guard, using its clone will not +move its ownership, so the code works. +"##, + +E0009: r##" +In a pattern, all values that don't implement the `Copy` trait have to be bound +the same way. The goal here is to avoid binding simultaneously by-move and +by-ref. + +This limitation may be removed in a future version of Rust. + +Erroneous code example: + +```compile_fail,E0009 +struct X { x: (), } + +let x = Some((X { x: () }, X { x: () })); +match x { + Some((y, ref z)) => {}, // error: cannot bind by-move and by-ref in the + // same pattern + None => panic!() +} +``` + +You have two solutions: + +Solution #1: Bind the pattern's values the same way. + +``` +struct X { x: (), } + +let x = Some((X { x: () }, X { x: () })); +match x { + Some((ref y, ref z)) => {}, + // or Some((y, z)) => {} + None => panic!() +} +``` + +Solution #2: Implement the `Copy` trait for the `X` structure. + +However, please keep in mind that the first solution should be preferred. + +``` +#[derive(Clone, Copy)] +struct X { x: (), } + +let x = Some((X { x: () }, X { x: () })); +match x { + Some((y, ref z)) => {}, + None => panic!() +} +``` +"##, + +E0030: r##" +When matching against a range, the compiler verifies that the range is +non-empty. Range patterns include both end-points, so this is equivalent to +requiring the start of the range to be less than or equal to the end of the +range. + +For example: + +```compile_fail +match 5u32 { + // This range is ok, albeit pointless. + 1 ... 1 => {} + // This range is empty, and the compiler can tell. + 1000 ... 5 => {} +} +``` +"##, + +E0158: r##" +`const` and `static` mean different things. A `const` is a compile-time +constant, an alias for a literal value. This property means you can match it +directly within a pattern. + +The `static` keyword, on the other hand, guarantees a fixed location in memory. +This does not always mean that the value is constant. For example, a global +mutex can be declared `static` as well. + +If you want to match against a `static`, consider using a guard instead: + +``` +static FORTY_TWO: i32 = 42; + +match Some(42) { + Some(x) if x == FORTY_TWO => {} + _ => {} +} +``` +"##, + +E0162: r##" +An if-let pattern attempts to match the pattern, and enters the body if the +match was successful. If the match is irrefutable (when it cannot fail to +match), use a regular `let`-binding instead. For instance: + +```compile_fail,E0162 +struct Irrefutable(i32); +let irr = Irrefutable(0); + +// This fails to compile because the match is irrefutable. +if let Irrefutable(x) = irr { + // This body will always be executed. + // ... +} +``` + +Try this instead: + +``` +struct Irrefutable(i32); +let irr = Irrefutable(0); + +let Irrefutable(x) = irr; +println!("{}", x); +``` +"##, + +E0165: r##" +A while-let pattern attempts to match the pattern, and enters the body if the +match was successful. If the match is irrefutable (when it cannot fail to +match), use a regular `let`-binding inside a `loop` instead. For instance: + +```compile_fail,E0165 +struct Irrefutable(i32); +let irr = Irrefutable(0); + +// This fails to compile because the match is irrefutable. +while let Irrefutable(x) = irr { + // ... +} +``` + +Try this instead: + +```no_run +struct Irrefutable(i32); +let irr = Irrefutable(0); + +loop { + let Irrefutable(x) = irr; + // ... +} +``` +"##, + +E0170: r##" +Enum variants are qualified by default. For example, given this type: + +``` +enum Method { + GET, + POST, +} +``` + +You would match it using: + +``` +enum Method { + GET, + POST, +} + +let m = Method::GET; + +match m { + Method::GET => {}, + Method::POST => {}, +} +``` + +If you don't qualify the names, the code will bind new variables named "GET" and +"POST" instead. This behavior is likely not what you want, so `rustc` warns when +that happens. + +Qualified names are good practice, and most code works well with them. But if +you prefer them unqualified, you can import the variants into scope: + +``` +use Method::*; +enum Method { GET, POST } +# fn main() {} +``` + +If you want others to be able to import variants from your module directly, use +`pub use`: + +``` +pub use Method::*; +pub enum Method { GET, POST } +# fn main() {} +``` +"##, + + +E0297: r##" +#### Note: this error code is no longer emitted by the compiler. + +Patterns used to bind names must be irrefutable. That is, they must guarantee +that a name will be extracted in all cases. Instead of pattern matching the +loop variable, consider using a `match` or `if let` inside the loop body. For +instance: + +```compile_fail,E0005 +let xs : Vec> = vec![Some(1), None]; + +// This fails because `None` is not covered. +for Some(x) in xs { + // ... +} +``` + +Match inside the loop instead: + +``` +let xs : Vec> = vec![Some(1), None]; + +for item in xs { + match item { + Some(x) => {}, + None => {}, + } +} +``` + +Or use `if let`: + +``` +let xs : Vec> = vec![Some(1), None]; + +for item in xs { + if let Some(x) = item { + // ... + } +} +``` +"##, + +E0301: r##" +Mutable borrows are not allowed in pattern guards, because matching cannot have +side effects. Side effects could alter the matched object or the environment +on which the match depends in such a way, that the match would not be +exhaustive. For instance, the following would not match any arm if mutable +borrows were allowed: + +```compile_fail,E0301 +match Some(()) { + None => { }, + option if option.take().is_none() => { + /* impossible, option is `Some` */ + }, + Some(_) => { } // When the previous match failed, the option became `None`. +} +``` +"##, + +E0302: r##" +Assignments are not allowed in pattern guards, because matching cannot have +side effects. Side effects could alter the matched object or the environment +on which the match depends in such a way, that the match would not be +exhaustive. For instance, the following would not match any arm if assignments +were allowed: + +```compile_fail,E0302 +match Some(()) { + None => { }, + option if { option = None; false } => { }, + Some(_) => { } // When the previous match failed, the option became `None`. +} +``` +"##, + +E0303: r##" +In certain cases it is possible for sub-bindings to violate memory safety. +Updates to the borrow checker in a future version of Rust may remove this +restriction, but for now patterns must be rewritten without sub-bindings. + +Before: + +```compile_fail,E0303 +match Some("hi".to_string()) { + ref op_string_ref @ Some(s) => {}, + None => {}, +} +``` + +After: + +``` +match Some("hi".to_string()) { + Some(ref s) => { + let op_string_ref = &Some(s); + // ... + }, + None => {}, +} +``` + +The `op_string_ref` binding has type `&Option<&String>` in both cases. + +See also https://github.com/rust-lang/rust/issues/14587 +"##, + E0010: r##" The value of statics and constants must be known at compile time, and they live for the entire lifetime of a program. Creating a boxed value allocates memory on @@ -1613,6 +2148,24 @@ fn main() { ``` "##, +E0579: r##" +When matching against an exclusive range, the compiler verifies that the range +is non-empty. Exclusive range patterns include the start point but not the end +point, so this is equivalent to requiring the start of the range to be less +than the end of the range. + +For example: + +```compile_fail +match 5u32 { + // This range is ok, albeit pointless. + 1 .. 2 => {} + // This range is empty, and the compiler can tell. + 5 .. 5 => {} +} +``` +"##, + E0595: r##" Closures cannot mutate immutable captured variables. @@ -1771,6 +2324,9 @@ b.resume(); } register_diagnostics! { +// E0298, // cannot compare constants +// E0299, // mismatched types between arms +// E0471, // constant evaluation error (in pattern) // E0385, // {} in an aliasable location E0493, // destructors cannot be evaluated at compile-time E0524, // two closures require unique access to `..` at the same time diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index 00ab2e45995..da25969bf11 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -10,12 +10,12 @@ use hair::*; use rustc_data_structures::indexed_vec::Idx; -use rustc_const_math::ConstInt; use hair::cx::Cx; use hair::cx::block; use hair::cx::to_ref::ToRef; use rustc::hir::def::{Def, CtorKind}; use rustc::middle::const_val::ConstVal; +use rustc::mir::interpret::{GlobalId, Value, PrimVal}; use rustc::ty::{self, AdtKind, VariantDef, Ty}; use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow, AutoBorrowMutability}; use rustc::ty::cast::CastKind as TyCastKind; @@ -100,7 +100,7 @@ fn apply_adjustment<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, ExprKind::Deref { arg: expr.to_ref() } } Adjust::Deref(Some(deref)) => { - let call = deref.method_call(cx.tcx, expr.ty); + let call = deref.method_call(cx.tcx(), expr.ty); expr = Expr { temp_lifetime, @@ -314,7 +314,9 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, } } - hir::ExprLit(..) => ExprKind::Literal { literal: cx.const_eval_literal(expr) }, + hir::ExprLit(ref lit) => ExprKind::Literal { + literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, false), + }, hir::ExprBinary(op, ref lhs, ref rhs) => { if cx.tables().is_method_call(expr) { @@ -400,9 +402,10 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, if cx.tables().is_method_call(expr) { overloaded_operator(cx, expr, vec![arg.to_ref()]) } else { - // FIXME runtime-overflow - if let hir::ExprLit(_) = arg.node { - ExprKind::Literal { literal: cx.const_eval_literal(expr) } + if let hir::ExprLit(ref lit) = arg.node { + ExprKind::Literal { + literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, true), + } } else { ExprKind::Unary { op: UnOp::Neg, @@ -508,10 +511,22 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, let c = &cx.tcx.hir.body(count).value; let def_id = cx.tcx.hir.body_owner_def_id(count); let substs = Substs::identity_for_item(cx.tcx.global_tcx(), def_id); - let count = match cx.tcx.at(c.span).const_eval(cx.param_env.and((def_id, substs))) { - Ok(&ty::Const { val: ConstVal::Integral(ConstInt::Usize(u)), .. }) => u, - Ok(other) => bug!("constant evaluation of repeat count yielded {:?}", other), - Err(s) => cx.fatal_const_eval_err(&s, c.span, "expression") + let instance = ty::Instance::resolve( + cx.tcx.global_tcx(), + cx.param_env, + def_id, + substs, + ).unwrap(); + let global_id = GlobalId { + instance, + promoted: None + }; + let count = match cx.tcx.at(c.span).const_eval(cx.param_env.and(global_id)) { + Ok(cv) => cv.val.unwrap_u64(), + Err(e) => { + e.report(cx.tcx, cx.tcx.def_span(def_id), "array length"); + 0 + }, }; ExprKind::Repeat { @@ -634,8 +649,8 @@ fn method_callee<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, span: expr.span, kind: ExprKind::Literal { literal: Literal::Value { - value: cx.tcx.mk_const(ty::Const { - val: ConstVal::Function(def_id, substs), + value: cx.tcx().mk_const(ty::Const { + val: ConstVal::Value(Value::ByVal(PrimVal::Undef)), ty }), }, @@ -682,13 +697,13 @@ fn convert_path_expr<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, let substs = cx.tables().node_substs(expr.hir_id); match def { // A regular function, constructor function or a constant. - Def::Fn(def_id) | - Def::Method(def_id) | - Def::StructCtor(def_id, CtorKind::Fn) | - Def::VariantCtor(def_id, CtorKind::Fn) => ExprKind::Literal { + Def::Fn(_) | + Def::Method(_) | + Def::StructCtor(_, CtorKind::Fn) | + Def::VariantCtor(_, CtorKind::Fn) => ExprKind::Literal { literal: Literal::Value { value: cx.tcx.mk_const(ty::Const { - val: ConstVal::Function(def_id, substs), + val: ConstVal::Value(Value::ByVal(PrimVal::Undef)), ty: cx.tables().node_id_to_type(expr.hir_id) }), }, diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index 44c41356117..d8d5f5073ab 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -16,22 +16,22 @@ use hair::*; -use rustc::middle::const_val::{ConstEvalErr, ConstVal}; -use rustc_const_eval::ConstContext; +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; use rustc::middle::region; use rustc::infer::InferCtxt; use rustc::ty::subst::Subst; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::{self, Ty, TyCtxt, layout}; use rustc::ty::subst::Substs; -use syntax::ast; +use syntax::ast::{self, LitKind}; use syntax::attr; use syntax::symbol::Symbol; use rustc::hir; -use rustc_const_math::{ConstInt, ConstUsize}; +use rustc_const_math::ConstFloat; use rustc_data_structures::sync::Lrc; +use rustc::mir::interpret::{Value, PrimVal}; #[derive(Clone)] pub struct Cx<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { @@ -115,16 +115,11 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { } pub fn usize_literal(&mut self, value: u64) -> Literal<'tcx> { - match ConstUsize::new(value, self.tcx.sess.target.usize_ty) { - Ok(val) => { - Literal::Value { - value: self.tcx.mk_const(ty::Const { - val: ConstVal::Integral(ConstInt::Usize(val)), - ty: self.tcx.types.usize - }) - } - } - Err(_) => bug!("usize literal out of range for target"), + Literal::Value { + value: self.tcx.mk_const(ty::Const { + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(value as u128))), + ty: self.tcx.types.usize + }) } } @@ -139,7 +134,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { pub fn true_literal(&mut self) -> Literal<'tcx> { Literal::Value { value: self.tcx.mk_const(ty::Const { - val: ConstVal::Bool(true), + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(1))), ty: self.tcx.types.bool }) } @@ -148,20 +143,104 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { pub fn false_literal(&mut self) -> Literal<'tcx> { Literal::Value { value: self.tcx.mk_const(ty::Const { - val: ConstVal::Bool(false), + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(0))), ty: self.tcx.types.bool }) } } - pub fn const_eval_literal(&mut self, e: &hir::Expr) -> Literal<'tcx> { + pub fn integer_bit_width( + &self, + ty: Ty, + ) -> u64 { + let ty = match ty.sty { + ty::TyInt(ity) => attr::IntType::SignedInt(ity), + ty::TyUint(uty) => attr::IntType::UnsignedInt(uty), + _ => bug!("{} is not an integer", ty), + }; + layout::Integer::from_attr(self.tcx, ty).size().bits() + } + + pub fn const_eval_literal( + &mut self, + lit: &'tcx ast::LitKind, + ty: Ty<'tcx>, + sp: Span, + neg: bool, + ) -> Literal<'tcx> { + trace!("const_eval_literal: {:#?}, {:?}, {:?}, {:?}", lit, ty, sp, neg); let tcx = self.tcx.global_tcx(); - let const_cx = ConstContext::new(tcx, - self.param_env.and(self.identity_substs), - self.tables()); - match const_cx.eval(tcx.hir.expect_expr(e.id)) { - Ok(value) => Literal::Value { value }, - Err(s) => self.fatal_const_eval_err(&s, e.span, "expression") + + let parse_float = |num: &str, fty| -> ConstFloat { + ConstFloat::from_str(num, fty).unwrap_or_else(|_| { + // FIXME(#31407) this is only necessary because float parsing is buggy + tcx.sess.span_fatal(sp, "could not evaluate float literal (see issue #31407)"); + }) + }; + + let clamp = |n| { + let size = self.integer_bit_width(ty); + trace!("clamp {} with size {} and amt {}", n, size, 128 - size); + let amt = 128 - size; + let result = (n << amt) >> amt; + trace!("clamp result: {}", result); + result + }; + + use rustc::mir::interpret::*; + let lit = match *lit { + LitKind::Str(ref s, _) => { + let s = s.as_str(); + let id = self.tcx.allocate_cached(s.as_bytes()); + let ptr = MemoryPointer::new(id, 0); + Value::ByValPair( + PrimVal::Ptr(ptr), + PrimVal::from_u128(s.len() as u128), + ) + }, + LitKind::ByteStr(ref data) => { + let id = self.tcx.allocate_cached(data); + let ptr = MemoryPointer::new(id, 0); + Value::ByVal(PrimVal::Ptr(ptr)) + }, + LitKind::Byte(n) => Value::ByVal(PrimVal::Bytes(n as u128)), + LitKind::Int(n, _) if neg => { + let n = n as i128; + let n = n.overflowing_neg().0; + let n = clamp(n as u128); + Value::ByVal(PrimVal::Bytes(n)) + }, + LitKind::Int(n, _) => Value::ByVal(PrimVal::Bytes(clamp(n))), + LitKind::Float(n, fty) => { + let n = n.as_str(); + let mut f = parse_float(&n, fty); + if neg { + f = -f; + } + let bits = f.bits; + Value::ByVal(PrimVal::Bytes(bits)) + } + LitKind::FloatUnsuffixed(n) => { + let fty = match ty.sty { + ty::TyFloat(fty) => fty, + _ => bug!() + }; + let n = n.as_str(); + let mut f = parse_float(&n, fty); + if neg { + f = -f; + } + let bits = f.bits; + Value::ByVal(PrimVal::Bytes(bits)) + } + LitKind::Bool(b) => Value::ByVal(PrimVal::Bytes(b as u128)), + LitKind::Char(c) => Value::ByVal(PrimVal::Bytes(c as u128)), + }; + Literal::Value { + value: self.tcx.mk_const(ty::Const { + val: ConstVal::Value(lit), + ty, + }), } } @@ -177,17 +256,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, @@ -203,7 +271,8 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { return (method_ty, Literal::Value { value: self.tcx.mk_const(ty::Const { - val: ConstVal::Function(item.def_id, substs), + // ZST function type + val: ConstVal::Value(Value::ByVal(PrimVal::Undef)), ty: method_ty }), }); diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs index 09a31f9ab8f..5f60a134fb1 100644 --- a/src/librustc_mir/hair/mod.rs +++ b/src/librustc_mir/hair/mod.rs @@ -14,7 +14,6 @@ //! unit-tested and separated from the Rust source and compiler data //! structures. -use rustc_const_math::ConstUsize; use rustc::mir::{BinOp, BorrowKind, Field, Literal, UnOp}; use rustc::hir::def_id::DefId; use rustc::middle::region; @@ -27,7 +26,8 @@ use self::cx::Cx; pub mod cx; -pub use rustc_const_eval::pattern::{BindingMode, Pattern, PatternKind, FieldPattern}; +pub mod pattern; +pub use self::pattern::{BindingMode, Pattern, PatternKind, FieldPattern}; #[derive(Copy, Clone, Debug)] pub enum LintLevel { @@ -246,7 +246,7 @@ pub enum ExprKind<'tcx> { }, Repeat { value: ExprRef<'tcx>, - count: ConstUsize, + count: u64, }, Array { fields: Vec>, diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_mir/hair/pattern/_match.rs similarity index 86% rename from src/librustc_const_eval/_match.rs rename to src/librustc_mir/hair/pattern/_match.rs index 8e3b99f2dbf..a3295aac801 100644 --- a/src/librustc_const_eval/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -13,21 +13,19 @@ use self::Usefulness::*; use self::WitnessPreference::*; use rustc::middle::const_val::ConstVal; -use eval::{compare_const_vals}; - -use rustc_const_math::ConstInt; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::indexed_vec::Idx; -use pattern::{FieldPattern, Pattern, PatternKind}; -use pattern::{PatternFoldable, PatternFolder}; +use super::{FieldPattern, Pattern, PatternKind}; +use super::{PatternFoldable, PatternFolder, compare_const_vals}; use rustc::hir::def_id::DefId; use rustc::hir::RangeEnd; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc::mir::Field; +use rustc::mir::interpret::{Value, PrimVal}; use rustc::util::common::ErrorReported; use syntax_pos::{Span, DUMMY_SP}; @@ -182,18 +180,38 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { self.byte_array_map.entry(pat).or_insert_with(|| { match pat.kind { box PatternKind::Constant { - value: &ty::Const { val: ConstVal::ByteStr(b), .. } + value: &ty::Const { val: ConstVal::Value(b), ty } } => { - b.data.iter().map(|&b| &*pattern_arena.alloc(Pattern { - ty: tcx.types.u8, - span: pat.span, - kind: box PatternKind::Constant { - value: tcx.mk_const(ty::Const { - val: ConstVal::Integral(ConstInt::U8(b)), - ty: tcx.types.u8 - }) - } - })).collect() + match b { + Value::ByVal(PrimVal::Ptr(ptr)) => { + let is_array_ptr = ty + .builtin_deref(true) + .and_then(|t| t.ty.builtin_index()) + .map_or(false, |t| t == tcx.types.u8); + assert!(is_array_ptr); + let alloc = tcx + .interpret_interner + .get_alloc(ptr.alloc_id) + .unwrap(); + assert_eq!(ptr.offset, 0); + // FIXME: check length + alloc.bytes.iter().map(|b| { + &*pattern_arena.alloc(Pattern { + ty: tcx.types.u8, + span: pat.span, + kind: box PatternKind::Constant { + value: tcx.mk_const(ty::Const { + val: ConstVal::Value(Value::ByVal( + PrimVal::Bytes(*b as u128), + )), + ty: tcx.types.u8 + }) + } + }) + }).collect() + }, + _ => bug!("not a byte str: {:?}", b), + } } _ => span_bug!(pat.span, "unexpected byte array pattern {:?}", pat) } @@ -422,13 +440,13 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, ty::TyBool => { [true, false].iter().map(|&b| { ConstantValue(cx.tcx.mk_const(ty::Const { - val: ConstVal::Bool(b), + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b as u128))), ty: cx.tcx.types.bool })) }).collect() } - ty::TyArray(ref sub_ty, len) if len.val.to_const_int().is_some() => { - let len = len.val.to_const_int().unwrap().to_u64().unwrap(); + ty::TyArray(ref sub_ty, len) if len.val.to_raw_bits().is_some() => { + let len = len.val.unwrap_u64(); if len != 0 && cx.is_uninhabited(sub_ty) { vec![] } else { @@ -461,7 +479,7 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, } fn max_slice_length<'p, 'a: 'p, 'tcx: 'a, I>( - _cx: &mut MatchCheckCtxt<'a, 'tcx>, + cx: &mut MatchCheckCtxt<'a, 'tcx>, patterns: I) -> u64 where I: Iterator> { @@ -535,8 +553,23 @@ fn max_slice_length<'p, 'a: 'p, 'tcx: 'a, I>( for row in patterns { match *row.kind { - PatternKind::Constant { value: &ty::Const { val: ConstVal::ByteStr(b), .. } } => { - max_fixed_len = cmp::max(max_fixed_len, b.data.len() as u64); + PatternKind::Constant { + value: &ty::Const { + val: ConstVal::Value(Value::ByVal(PrimVal::Ptr(ptr))), + ty, + } + } => { + let is_array_ptr = ty + .builtin_deref(true) + .and_then(|t| t.ty.builtin_index()) + .map_or(false, |t| t == cx.tcx.types.u8); + if is_array_ptr { + let alloc = cx.tcx + .interpret_interner + .get_alloc(ptr.alloc_id) + .unwrap(); + max_fixed_len = cmp::max(max_fixed_len, alloc.bytes.len() as u64); + } } PatternKind::Slice { ref prefix, slice: None, ref suffix } => { let fixed_len = prefix.len() as u64 + suffix.len() as u64; @@ -581,7 +614,7 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, witness: WitnessPreference) -> Usefulness<'tcx> { let &Matrix(ref rows) = matrix; - debug!("is_useful({:?}, {:?})", matrix, v); + debug!("is_useful({:#?}, {:#?})", matrix, v); // The base case. We are pattern-matching on () and the return value is // based on whether our matrix has a row or not. @@ -626,10 +659,10 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, max_slice_length: max_slice_length(cx, rows.iter().map(|r| r[0]).chain(Some(v[0]))) }; - debug!("is_useful_expand_first_col: pcx={:?}, expanding {:?}", pcx, v[0]); + debug!("is_useful_expand_first_col: pcx={:#?}, expanding {:#?}", pcx, v[0]); if let Some(constructors) = pat_constructors(cx, v[0], pcx) { - debug!("is_useful - expanding constructors: {:?}", constructors); + debug!("is_useful - expanding constructors: {:#?}", constructors); constructors.into_iter().map(|c| is_useful_specialized(cx, matrix, v, c.clone(), pcx.ty, witness) ).find(|result| result.is_useful()).unwrap_or(NotUseful) @@ -639,9 +672,9 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, let used_ctors: Vec = rows.iter().flat_map(|row| { pat_constructors(cx, row[0], pcx).unwrap_or(vec![]) }).collect(); - debug!("used_ctors = {:?}", used_ctors); + debug!("used_ctors = {:#?}", used_ctors); let all_ctors = all_constructors(cx, pcx); - debug!("all_ctors = {:?}", all_ctors); + debug!("all_ctors = {:#?}", all_ctors); let missing_ctors: Vec = all_ctors.iter().filter(|c| { !used_ctors.contains(*c) }).cloned().collect(); @@ -669,7 +702,7 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, all_ctors.is_empty() && !cx.is_uninhabited(pcx.ty); let is_declared_nonexhaustive = cx.is_non_exhaustive_enum(pcx.ty) && !cx.is_local(pcx.ty); - debug!("missing_ctors={:?} is_privately_empty={:?} is_declared_nonexhaustive={:?}", + debug!("missing_ctors={:#?} is_privately_empty={:#?} is_declared_nonexhaustive={:#?}", missing_ctors, is_privately_empty, is_declared_nonexhaustive); // For privately empty and non-exhaustive enums, we work as if there were an "extra" @@ -769,7 +802,7 @@ fn is_useful_specialized<'p, 'a:'p, 'tcx: 'a>( lty: Ty<'tcx>, witness: WitnessPreference) -> Usefulness<'tcx> { - debug!("is_useful_specialized({:?}, {:?}, {:?})", v, ctor, lty); + debug!("is_useful_specialized({:#?}, {:#?}, {:?})", v, ctor, lty); let sub_pat_tys = constructor_sub_pattern_tys(cx, &ctor, lty); let wild_patterns_owned: Vec<_> = sub_pat_tys.iter().map(|ty| { Pattern { @@ -821,7 +854,7 @@ fn pat_constructors<'tcx>(_cx: &mut MatchCheckCtxt, Some(vec![ConstantRange(lo, hi, end)]), PatternKind::Array { .. } => match pcx.ty.sty { ty::TyArray(_, length) => Some(vec![ - Slice(length.val.to_const_int().unwrap().to_u64().unwrap()) + Slice(length.val.unwrap_u64()) ]), _ => span_bug!(pat.span, "bad ty {:?} for array pattern", pcx.ty) }, @@ -842,7 +875,7 @@ fn pat_constructors<'tcx>(_cx: &mut MatchCheckCtxt, /// For instance, a tuple pattern (_, 42, Some([])) has the arity of 3. /// A struct pattern's arity is the number of fields it contains, etc. fn constructor_arity(_cx: &MatchCheckCtxt, ctor: &Constructor, ty: Ty) -> u64 { - debug!("constructor_arity({:?}, {:?})", ctor, ty); + debug!("constructor_arity({:#?}, {:?})", ctor, ty); match ty.sty { ty::TyTuple(ref fs, _) => fs.len() as u64, ty::TySlice(..) | ty::TyArray(..) => match *ctor { @@ -866,7 +899,7 @@ fn constructor_sub_pattern_tys<'a, 'tcx: 'a>(cx: &MatchCheckCtxt<'a, 'tcx>, ctor: &Constructor, ty: Ty<'tcx>) -> Vec> { - debug!("constructor_sub_pattern_tys({:?}, {:?})", ctor, ty); + debug!("constructor_sub_pattern_tys({:#?}, {:?})", ctor, ty); match ty.sty { ty::TyTuple(ref fs, _) => fs.into_iter().map(|t| *t).collect(), ty::TySlice(ty) | ty::TyArray(ty, _) => match *ctor { @@ -901,14 +934,28 @@ fn constructor_sub_pattern_tys<'a, 'tcx: 'a>(cx: &MatchCheckCtxt<'a, 'tcx>, } } -fn slice_pat_covered_by_constructor(_tcx: TyCtxt, _span: Span, +fn slice_pat_covered_by_constructor(tcx: TyCtxt, _span: Span, ctor: &Constructor, prefix: &[Pattern], slice: &Option, suffix: &[Pattern]) -> Result { - let data = match *ctor { - ConstantValue(&ty::Const { val: ConstVal::ByteStr(b), .. }) => b.data, + let data: &[u8] = match *ctor { + ConstantValue(&ty::Const { val: ConstVal::Value( + Value::ByVal(PrimVal::Ptr(ptr)) + ), ty }) => { + let is_array_ptr = ty + .builtin_deref(true) + .and_then(|t| t.ty.builtin_index()) + .map_or(false, |t| t == tcx.types.u8); + assert!(is_array_ptr); + tcx + .interpret_interner + .get_alloc(ptr.alloc_id) + .unwrap() + .bytes + .as_ref() + } _ => bug!() }; @@ -923,11 +970,12 @@ fn slice_pat_covered_by_constructor(_tcx: TyCtxt, _span: Span, { match pat.kind { box PatternKind::Constant { value } => match value.val { - ConstVal::Integral(ConstInt::U8(u)) => { - if u != *ch { + ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))) => { + assert_eq!(b as u8 as u128, b); + if b as u8 != *ch { return Ok(false); } - }, + } _ => span_bug!(pat.span, "bad const u8 {:?}", value) }, _ => {} @@ -937,31 +985,41 @@ fn slice_pat_covered_by_constructor(_tcx: TyCtxt, _span: Span, Ok(true) } -fn constructor_covered_by_range(tcx: TyCtxt, span: Span, - ctor: &Constructor, +fn constructor_covered_by_range(ctor: &Constructor, from: &ConstVal, to: &ConstVal, - end: RangeEnd) + end: RangeEnd, + ty: Ty) -> Result { - let cmp_from = |c_from| Ok(compare_const_vals(tcx, span, c_from, from)? != Ordering::Less); - let cmp_to = |c_to| compare_const_vals(tcx, span, c_to, to); + trace!("constructor_covered_by_range {:#?}, {:#?}, {:#?}, {}", ctor, from, to, ty); + let cmp_from = |c_from| compare_const_vals(c_from, from, ty) + .map(|res| res != Ordering::Less); + let cmp_to = |c_to| compare_const_vals(c_to, to, ty); + macro_rules! some_or_ok { + ($e:expr) => { + match $e { + Some(to) => to, + None => return Ok(false), // not char or int + } + }; + } match *ctor { ConstantValue(value) => { - let to = cmp_to(&value.val)?; + let to = some_or_ok!(cmp_to(&value.val)); let end = (to == Ordering::Less) || (end == RangeEnd::Included && to == Ordering::Equal); - Ok(cmp_from(&value.val)? && end) + Ok(some_or_ok!(cmp_from(&value.val)) && end) }, ConstantRange(from, to, RangeEnd::Included) => { - let to = cmp_to(&to.val)?; + let to = some_or_ok!(cmp_to(&to.val)); let end = (to == Ordering::Less) || (end == RangeEnd::Included && to == Ordering::Equal); - Ok(cmp_from(&from.val)? && end) + Ok(some_or_ok!(cmp_from(&from.val)) && end) }, ConstantRange(from, to, RangeEnd::Excluded) => { - let to = cmp_to(&to.val)?; + let to = some_or_ok!(cmp_to(&to.val)); let end = (to == Ordering::Less) || (end == RangeEnd::Excluded && to == Ordering::Equal); - Ok(cmp_from(&from.val)? && end) + Ok(some_or_ok!(cmp_from(&from.val)) && end) } Single => Ok(true), _ => bug!(), @@ -979,7 +1037,7 @@ fn patterns_for_variant<'p, 'a: 'p, 'tcx: 'a>( result[subpat.field.index()] = &subpat.pattern; } - debug!("patterns_for_variant({:?}, {:?}) = {:?}", subpatterns, wild_patterns, result); + debug!("patterns_for_variant({:#?}, {:#?}) = {:#?}", subpatterns, wild_patterns, result); result } @@ -994,7 +1052,7 @@ fn patterns_for_variant<'p, 'a: 'p, 'tcx: 'a>( fn specialize<'p, 'a: 'p, 'tcx: 'a>( cx: &mut MatchCheckCtxt<'a, 'tcx>, r: &[&'p Pattern<'tcx>], - constructor: &Constructor, + constructor: &Constructor<'tcx>, wild_patterns: &[&'p Pattern<'tcx>]) -> Option>> { @@ -1024,8 +1082,19 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>( PatternKind::Constant { value } => { match *constructor { Slice(..) => match value.val { - ConstVal::ByteStr(b) => { - if wild_patterns.len() == b.data.len() { + ConstVal::Value(Value::ByVal(PrimVal::Ptr(ptr))) => { + let is_array_ptr = value.ty + .builtin_deref(true) + .and_then(|t| t.ty.builtin_index()) + .map_or(false, |t| t == cx.tcx.types.u8); + assert!(is_array_ptr); + let data_len = cx.tcx + .interpret_interner + .get_alloc(ptr.alloc_id) + .unwrap() + .bytes + .len(); + if wild_patterns.len() == data_len { Some(cx.lower_byte_str_pattern(pat)) } else { None @@ -1036,7 +1105,8 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>( }, _ => { match constructor_covered_by_range( - cx.tcx, pat.span, constructor, &value.val, &value.val, RangeEnd::Included + constructor, &value.val, &value.val, RangeEnd::Included, + value.ty, ) { Ok(true) => Some(vec![]), Ok(false) => None, @@ -1048,7 +1118,7 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>( PatternKind::Range { lo, hi, ref end } => { match constructor_covered_by_range( - cx.tcx, pat.span, constructor, &lo.val, &hi.val, end.clone() + constructor, &lo.val, &hi.val, end.clone(), lo.ty, ) { Ok(true) => Some(vec![]), Ok(false) => None, @@ -1092,7 +1162,7 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>( } } }; - debug!("specialize({:?}, {:?}) = {:?}", r[0], wild_patterns, head); + debug!("specialize({:#?}, {:#?}) = {:#?}", r[0], wild_patterns, head); head.map(|mut head| { head.extend_from_slice(&r[1 ..]); diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs similarity index 96% rename from src/librustc_const_eval/check_match.rs rename to src/librustc_mir/hair/pattern/check_match.rs index 6f7143c185c..69ed4e6064f 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -8,11 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use _match::{MatchCheckCtxt, Matrix, expand_pattern, is_useful}; -use _match::Usefulness::*; -use _match::WitnessPreference::*; +use super::_match::{MatchCheckCtxt, Matrix, expand_pattern, is_useful}; +use super::_match::Usefulness::*; +use super::_match::WitnessPreference::*; -use pattern::{Pattern, PatternContext, PatternError, PatternKind}; +use super::{Pattern, PatternContext, PatternError, PatternKind}; use rustc::middle::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor}; use rustc::middle::expr_use_visitor::{LoanCause, MutateMode}; @@ -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_const_eval/pattern.rs b/src/librustc_mir/hair/pattern/mod.rs similarity index 64% rename from src/librustc_const_eval/pattern.rs rename to src/librustc_mir/hair/pattern/mod.rs index bdb1001124d..1774c95af0f 100644 --- a/src/librustc_const_eval/pattern.rs +++ b/src/librustc_mir/hair/pattern/mod.rs @@ -8,10 +8,19 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use eval; +//! Code to validate patterns/matches -use rustc::middle::const_val::{ConstEvalErr, ConstVal}; +mod _match; +mod check_match; + +pub use self::check_match::check_crate; +pub(crate) use self::check_match::check_match; + +use interpret::{const_val_field, const_discr}; + +use rustc::middle::const_val::ConstVal; use rustc::mir::{Field, BorrowKind, Mutability}; +use rustc::mir::interpret::{GlobalId, Value, PrimVal}; use rustc::ty::{self, TyCtxt, AdtDef, Ty, Region}; use rustc::ty::subst::{Substs, Kind}; use rustc::hir::{self, PatKind, RangeEnd}; @@ -19,17 +28,20 @@ use rustc::hir::def::{Def, CtorKind}; use rustc::hir::pat_util::EnumerateAndAdjustIterator; use rustc_data_structures::indexed_vec::Idx; +use rustc_const_math::ConstFloat; +use std::cmp::Ordering; use std::fmt; use syntax::ast; use syntax::ptr::P; use syntax_pos::Span; #[derive(Clone, Debug)] -pub enum PatternError<'tcx> { +pub enum PatternError { AssociatedConstInPattern(Span), StaticInPattern(Span), - ConstEval(ConstEvalErr<'tcx>), + FloatBug, + NonConstPath(Span), } #[derive(Copy, Clone, Debug)] @@ -110,21 +122,26 @@ pub enum PatternKind<'tcx> { }, } -fn print_const_val(value: &ConstVal, f: &mut fmt::Formatter) -> fmt::Result { - match *value { - ConstVal::Float(ref x) => write!(f, "{}", x), - ConstVal::Integral(ref i) => write!(f, "{}", i), - ConstVal::Str(ref s) => write!(f, "{:?}", &s[..]), - ConstVal::ByteStr(b) => write!(f, "{:?}", b.data), - ConstVal::Bool(b) => write!(f, "{:?}", b), - ConstVal::Char(c) => write!(f, "{:?}", c), - ConstVal::Variant(_) | - ConstVal::Function(..) | - ConstVal::Aggregate(_) | +fn print_const_val(value: &ty::Const, f: &mut fmt::Formatter) -> fmt::Result { + match value.val { + ConstVal::Value(v) => print_miri_value(v, value.ty, f), ConstVal::Unevaluated(..) => bug!("{:?} not printable in a pattern", value) } } +fn print_miri_value(value: Value, ty: Ty, f: &mut fmt::Formatter) -> fmt::Result { + use rustc::ty::TypeVariants::*; + match (value, &ty.sty) { + (Value::ByVal(PrimVal::Bytes(0)), &TyBool) => write!(f, "false"), + (Value::ByVal(PrimVal::Bytes(1)), &TyBool) => write!(f, "true"), + (Value::ByVal(PrimVal::Bytes(n)), &TyUint(..)) => write!(f, "{:?}", n), + (Value::ByVal(PrimVal::Bytes(n)), &TyInt(..)) => write!(f, "{:?}", n as i128), + (Value::ByVal(PrimVal::Bytes(n)), &TyChar) => + write!(f, "{:?}", ::std::char::from_u32(n as u32).unwrap()), + _ => bug!("{:?}: {} not printable in a pattern", value, ty), + } +} + impl<'tcx> fmt::Display for Pattern<'tcx> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self.kind { @@ -232,15 +249,15 @@ impl<'tcx> fmt::Display for Pattern<'tcx> { write!(f, "{}", subpattern) } PatternKind::Constant { value } => { - print_const_val(&value.val, f) + print_const_val(value, f) } PatternKind::Range { lo, hi, end } => { - print_const_val(&lo.val, f)?; + print_const_val(lo, f)?; match end { RangeEnd::Included => write!(f, "...")?, RangeEnd::Excluded => write!(f, "..")?, } - print_const_val(&hi.val, f) + print_const_val(hi, f) } PatternKind::Slice { ref prefix, ref slice, ref suffix } | PatternKind::Array { ref prefix, ref slice, ref suffix } => { @@ -272,7 +289,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> { @@ -350,18 +367,53 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { PatKind::Lit(ref value) => self.lower_lit(value), - PatKind::Range(ref lo, ref hi, end) => { - match (self.lower_lit(lo), self.lower_lit(hi)) { + PatKind::Range(ref lo_expr, ref hi_expr, end) => { + match (self.lower_lit(lo_expr), self.lower_lit(hi_expr)) { (PatternKind::Constant { value: lo }, PatternKind::Constant { value: hi }) => { - PatternKind::Range { lo, hi, end } + use std::cmp::Ordering; + match (end, compare_const_vals(&lo.val, &hi.val, ty).unwrap()) { + (RangeEnd::Excluded, Ordering::Less) => + PatternKind::Range { lo, hi, end }, + (RangeEnd::Excluded, _) => { + span_err!( + self.tcx.sess, + lo_expr.span, + E0579, + "lower range bound must be less than upper", + ); + PatternKind::Wild + }, + (RangeEnd::Included, Ordering::Greater) => { + let mut err = struct_span_err!( + self.tcx.sess, + lo_expr.span, + E0030, + "lower range bound must be less than or equal to upper" + ); + err.span_label( + lo_expr.span, + "lower bound larger than upper bound", + ); + if self.tcx.sess.teach(&err.get_code().unwrap()) { + err.note("When matching against a range, the compiler \ + verifies that the range is non-empty. Range \ + patterns include both end-points, so this is \ + equivalent to requiring the start of the range \ + to be less than or equal to the end of the range."); + } + err.emit(); + PatternKind::Wild + }, + (RangeEnd::Included, _) => PatternKind::Range { lo, hi, end }, + } } _ => PatternKind::Wild } } PatKind::Path(ref qpath) => { - return self.lower_path(qpath, pat.hir_id, pat.id, pat.span); + return self.lower_path(qpath, pat.hir_id, pat.span); } PatKind::Ref(ref subpattern, _) | @@ -471,7 +523,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { pattern: self.lower_pattern(field), }) .collect(); - self.lower_variant_or_leaf(def, ty, subpatterns) + self.lower_variant_or_leaf(def, pat.span, ty, subpatterns) } PatKind::Struct(ref qpath, ref fields, _) => { @@ -503,7 +555,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { }) .collect(); - self.lower_variant_or_leaf(def, ty, subpatterns) + self.lower_variant_or_leaf(def, pat.span, ty, subpatterns) } }; @@ -580,7 +632,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { ty::TyArray(_, len) => { // fixed-length array - let len = len.val.to_const_int().unwrap().to_u64().unwrap(); + let len = len.val.unwrap_u64(); assert!(len >= prefix.len() as u64 + suffix.len() as u64); PatternKind::Array { prefix: prefix, slice: slice, suffix: suffix } } @@ -594,6 +646,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { fn lower_variant_or_leaf( &mut self, def: Def, + span: Span, ty: Ty<'tcx>, subpatterns: Vec>) -> PatternKind<'tcx> @@ -624,14 +677,19 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { PatternKind::Leaf { subpatterns: subpatterns } } - _ => bug!() + _ => { + self.errors.push(PatternError::NonConstPath(span)); + PatternKind::Wild + } } } + /// Takes a HIR Path. If the path is a constant, evaluates it and feeds + /// it to `const_to_pat`. Any other path (like enum variants without fields) + /// is converted to the corresponding pattern via `lower_variant_or_leaf` fn lower_path(&mut self, qpath: &hir::QPath, id: hir::HirId, - pat_id: ast::NodeId, span: Span) -> Pattern<'tcx> { let ty = self.tables.node_id_to_type(id); @@ -643,23 +701,27 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { let kind = match def { Def::Const(def_id) | Def::AssociatedConst(def_id) => { let substs = self.tables.node_substs(id); - match eval::lookup_const_by_id(self.tcx, self.param_env.and((def_id, substs))) { - Some((def_id, substs)) => { - // Enter the inlined constant's tables&substs temporarily. - let old_tables = self.tables; - let old_substs = self.substs; - self.tables = self.tcx.typeck_tables_of(def_id); - self.substs = substs; - let body = if let Some(id) = self.tcx.hir.as_local_node_id(def_id) { - self.tcx.hir.body(self.tcx.hir.body_owned_by(id)) - } else { - self.tcx.extern_const_body(def_id).body + match ty::Instance::resolve( + self.tcx, + self.param_env, + def_id, + substs, + ) { + Some(instance) => { + let cid = GlobalId { + instance, + promoted: None, }; - let pat = self.lower_const_expr(&body.value, pat_id, span); - self.tables = old_tables; - self.substs = old_substs; - return pat; - } + match self.tcx.at(span).const_eval(self.param_env.and(cid)) { + Ok(value) => { + return self.const_to_pat(instance, value, id, span) + }, + Err(err) => { + err.report(self.tcx, span, "pattern"); + PatternKind::Wild + }, + } + }, None => { self.errors.push(if is_associated_const { PatternError::AssociatedConstInPattern(span) @@ -667,10 +729,10 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { PatternError::StaticInPattern(span) }); PatternKind::Wild - } + }, } } - _ => self.lower_variant_or_leaf(def, ty, vec![]), + _ => self.lower_variant_or_leaf(def, span, ty, vec![]), }; Pattern { @@ -680,138 +742,167 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } } + /// Converts literals, paths and negation of literals to patterns. + /// The special case for negation exists to allow things like -128i8 + /// which would overflow if we tried to evaluate 128i8 and then negate + /// afterwards. fn lower_lit(&mut self, expr: &'tcx hir::Expr) -> PatternKind<'tcx> { - let const_cx = eval::ConstContext::new(self.tcx, - self.param_env.and(self.substs), - self.tables); - match const_cx.eval(expr) { - Ok(value) => { - if let ConstVal::Variant(def_id) = value.val { - let ty = self.tables.expr_ty(expr); - self.lower_variant_or_leaf(Def::Variant(def_id), ty, vec![]) - } else { - PatternKind::Constant { value } + match expr.node { + hir::ExprLit(ref lit) => { + let ty = self.tables.expr_ty(expr); + match lit_to_const(&lit.node, self.tcx, ty, false) { + Ok(val) => { + let instance = ty::Instance::new( + self.tables.local_id_root.expect("literal outside any scope"), + self.substs, + ); + let cv = self.tcx.mk_const(ty::Const { val, ty }); + *self.const_to_pat(instance, cv, expr.hir_id, lit.span).kind + }, + Err(()) => { + self.errors.push(PatternError::FloatBug); + PatternKind::Wild + }, + } + }, + hir::ExprPath(ref qpath) => *self.lower_path(qpath, expr.hir_id, expr.span).kind, + hir::ExprUnary(hir::UnNeg, ref expr) => { + let ty = self.tables.expr_ty(expr); + let lit = match expr.node { + hir::ExprLit(ref lit) => lit, + _ => span_bug!(expr.span, "not a literal: {:?}", expr), + }; + match lit_to_const(&lit.node, self.tcx, ty, true) { + Ok(val) => { + let instance = ty::Instance::new( + self.tables.local_id_root.expect("literal outside any scope"), + self.substs, + ); + let cv = self.tcx.mk_const(ty::Const { val, ty }); + *self.const_to_pat(instance, cv, expr.hir_id, lit.span).kind + }, + Err(()) => { + self.errors.push(PatternError::FloatBug); + PatternKind::Wild + }, } } - Err(e) => { - self.errors.push(PatternError::ConstEval(e)); - PatternKind::Wild - } + _ => span_bug!(expr.span, "not a literal: {:?}", expr), } } - fn lower_const_expr(&mut self, - expr: &'tcx hir::Expr, - pat_id: ast::NodeId, - span: Span) - -> Pattern<'tcx> { - let pat_ty = self.tables.expr_ty(expr); - debug!("expr={:?} pat_ty={:?} pat_id={}", expr, pat_ty, pat_id); - match pat_ty.sty { + /// Converts an evaluated constant to a pattern (if possible). + /// This means aggregate values (like structs and enums) are converted + /// to a pattern that matches the value (as if you'd compare via eq). + fn const_to_pat( + &self, + instance: ty::Instance<'tcx>, + cv: &'tcx ty::Const<'tcx>, + id: hir::HirId, + span: Span, + ) -> Pattern<'tcx> { + debug!("const_to_pat: cv={:#?}", cv); + let adt_subpattern = |i, variant_opt| { + let field = Field::new(i); + let val = match cv.val { + ConstVal::Value(miri) => const_val_field( + self.tcx, self.param_env, instance, + variant_opt, field, miri, cv.ty, + ).unwrap(), + _ => bug!("{:#?} is not a valid adt", cv), + }; + self.const_to_pat(instance, val, id, span) + }; + let adt_subpatterns = |n, variant_opt| { + (0..n).map(|i| { + let field = Field::new(i); + FieldPattern { + field, + pattern: adt_subpattern(i, variant_opt), + } + }).collect::>() + }; + let kind = match cv.ty.sty { ty::TyFloat(_) => { - self.tcx.sess.span_err(span, "floating point constants cannot be used in patterns"); - } + let id = self.tcx.hir.hir_to_node_id(id); + self.tcx.lint_node( + ::rustc::lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, + id, + span, + "floating-point types cannot be used in patterns", + ); + PatternKind::Constant { + value: cv, + } + }, ty::TyAdt(adt_def, _) if adt_def.is_union() => { // Matching on union fields is unsafe, we can't hide it in constants self.tcx.sess.span_err(span, "cannot use unions in constant patterns"); + PatternKind::Wild } - ty::TyAdt(adt_def, _) => { - if !self.tcx.has_attr(adt_def.did, "structural_match") { - let msg = format!("to use a constant of type `{}` in a pattern, \ - `{}` must be annotated with `#[derive(PartialEq, Eq)]`", - self.tcx.item_path_str(adt_def.did), - self.tcx.item_path_str(adt_def.did)); - self.tcx.sess.span_err(span, &msg); - } - } - _ => { } - } - let kind = match expr.node { - hir::ExprTup(ref exprs) => { - PatternKind::Leaf { - subpatterns: exprs.iter().enumerate().map(|(i, expr)| { - FieldPattern { - field: Field::new(i), - pattern: self.lower_const_expr(expr, pat_id, span) + ty::TyAdt(adt_def, _) if !self.tcx.has_attr(adt_def.did, "structural_match") => { + let msg = format!("to use a constant of type `{}` in a pattern, \ + `{}` must be annotated with `#[derive(PartialEq, Eq)]`", + self.tcx.item_path_str(adt_def.did), + self.tcx.item_path_str(adt_def.did)); + self.tcx.sess.span_err(span, &msg); + PatternKind::Wild + }, + ty::TyAdt(adt_def, substs) if adt_def.is_enum() => { + match cv.val { + ConstVal::Value(val) => { + let discr = const_discr( + self.tcx, self.param_env, instance, val, cv.ty + ).unwrap(); + let variant_index = adt_def + .discriminants(self.tcx) + .position(|var| var.val == discr) + .unwrap(); + let subpatterns = adt_subpatterns( + adt_def.variants[variant_index].fields.len(), + Some(variant_index), + ); + PatternKind::Variant { + adt_def, + substs, + variant_index, + subpatterns, } - }).collect() + }, + ConstVal::Unevaluated(..) => + span_bug!(span, "{:#?} is not a valid enum constant", cv), + } + }, + ty::TyAdt(adt_def, _) => { + let struct_var = adt_def.non_enum_variant(); + PatternKind::Leaf { + subpatterns: adt_subpatterns(struct_var.fields.len(), None), } } - - hir::ExprCall(ref callee, ref args) => { - let qpath = match callee.node { - hir::ExprPath(ref qpath) => qpath, - _ => bug!() - }; - let ty = self.tables.node_id_to_type(callee.hir_id); - let def = self.tables.qpath_def(qpath, callee.hir_id); - match def { - Def::Fn(..) | Def::Method(..) => self.lower_lit(expr), - _ => { - let subpatterns = args.iter().enumerate().map(|(i, expr)| { - FieldPattern { - field: Field::new(i), - pattern: self.lower_const_expr(expr, pat_id, span) - } - }).collect(); - self.lower_variant_or_leaf(def, ty, subpatterns) - } + ty::TyTuple(fields, _) => { + PatternKind::Leaf { + subpatterns: adt_subpatterns(fields.len(), None), } } - - hir::ExprStruct(ref qpath, ref fields, None) => { - let def = self.tables.qpath_def(qpath, expr.hir_id); - let adt_def = match pat_ty.sty { - ty::TyAdt(adt_def, _) => adt_def, - _ => { - span_bug!( - expr.span, - "struct expr without ADT type"); - } - }; - let variant_def = adt_def.variant_of_def(def); - - let subpatterns = - fields.iter() - .map(|field| { - let index = variant_def.index_of_field_named(field.name.node); - let index = index.unwrap_or_else(|| { - span_bug!( - expr.span, - "no field with name {:?}", - field.name); - }); - FieldPattern { - field: Field::new(index), - pattern: self.lower_const_expr(&field.expr, pat_id, span), - } - }) - .collect(); - - self.lower_variant_or_leaf(def, pat_ty, subpatterns) - } - - hir::ExprArray(ref exprs) => { - let pats = exprs.iter() - .map(|expr| self.lower_const_expr(expr, pat_id, span)) - .collect(); + ty::TyArray(_, n) => { PatternKind::Array { - prefix: pats, + prefix: (0..n.val.unwrap_u64()) + .map(|i| adt_subpattern(i as usize, None)) + .collect(), slice: None, - suffix: vec![] + suffix: Vec::new(), } } - - hir::ExprPath(ref qpath) => { - return self.lower_path(qpath, expr.hir_id, pat_id, span); - } - - _ => self.lower_lit(expr) + _ => { + PatternKind::Constant { + value: cv, + } + }, }; Pattern { span, - ty: pat_ty, + ty: cv.ty, kind: Box::new(kind), } } @@ -975,3 +1066,127 @@ impl<'tcx> PatternFoldable<'tcx> for PatternKind<'tcx> { } } } + +pub fn compare_const_vals(a: &ConstVal, b: &ConstVal, ty: Ty) -> Option { + use rustc_const_math::ConstFloat; + trace!("compare_const_vals: {:?}, {:?}", a, b); + use rustc::mir::interpret::{Value, PrimVal}; + match (a, b) { + (&ConstVal::Value(Value::ByVal(PrimVal::Bytes(a))), + &ConstVal::Value(Value::ByVal(PrimVal::Bytes(b)))) => { + match ty.sty { + ty::TyFloat(ty) => { + let l = ConstFloat { + bits: a, + ty, + }; + let r = ConstFloat { + bits: b, + ty, + }; + // FIXME(oli-obk): report cmp errors? + l.try_cmp(r).ok() + }, + ty::TyInt(_) => Some((a as i128).cmp(&(b as i128))), + _ => Some(a.cmp(&b)), + } + }, + _ if a == b => Some(Ordering::Equal), + _ => None, + } +} + +fn lit_to_const<'a, 'tcx>(lit: &'tcx ast::LitKind, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + ty: Ty<'tcx>, + neg: bool) + -> Result, ()> { + use syntax::ast::*; + + use rustc::mir::interpret::*; + let lit = match *lit { + LitKind::Str(ref s, _) => { + let s = s.as_str(); + let id = tcx.allocate_cached(s.as_bytes()); + let ptr = MemoryPointer::new(id, 0); + Value::ByValPair( + PrimVal::Ptr(ptr), + PrimVal::from_u128(s.len() as u128), + ) + }, + LitKind::ByteStr(ref data) => { + let id = tcx.allocate_cached(data); + let ptr = MemoryPointer::new(id, 0); + Value::ByVal(PrimVal::Ptr(ptr)) + }, + LitKind::Byte(n) => Value::ByVal(PrimVal::Bytes(n as u128)), + LitKind::Int(n, _) => { + enum Int { + Signed(IntTy), + Unsigned(UintTy), + } + let ty = match ty.sty { + ty::TyInt(IntTy::Isize) => Int::Signed(tcx.sess.target.isize_ty), + ty::TyInt(other) => Int::Signed(other), + ty::TyUint(UintTy::Usize) => Int::Unsigned(tcx.sess.target.usize_ty), + ty::TyUint(other) => Int::Unsigned(other), + _ => bug!(), + }; + let n = match ty { + // FIXME(oli-obk): are these casts correct? + Int::Signed(IntTy::I8) if neg => + (n as i128 as i8).overflowing_neg().0 as i128 as u128, + Int::Signed(IntTy::I16) if neg => + (n as i128 as i16).overflowing_neg().0 as i128 as u128, + Int::Signed(IntTy::I32) if neg => + (n as i128 as i32).overflowing_neg().0 as i128 as u128, + Int::Signed(IntTy::I64) if neg => + (n as i128 as i64).overflowing_neg().0 as i128 as u128, + Int::Signed(IntTy::I128) if neg => + (n as i128).overflowing_neg().0 as u128, + Int::Signed(IntTy::I8) => n as i128 as i8 as i128 as u128, + Int::Signed(IntTy::I16) => n as i128 as i16 as i128 as u128, + Int::Signed(IntTy::I32) => n as i128 as i32 as i128 as u128, + Int::Signed(IntTy::I64) => n as i128 as i64 as i128 as u128, + Int::Signed(IntTy::I128) => n, + Int::Unsigned(UintTy::U8) => n as u8 as u128, + Int::Unsigned(UintTy::U16) => n as u16 as u128, + Int::Unsigned(UintTy::U32) => n as u32 as u128, + Int::Unsigned(UintTy::U64) => n as u64 as u128, + Int::Unsigned(UintTy::U128) => n, + _ => bug!(), + }; + Value::ByVal(PrimVal::Bytes(n)) + }, + LitKind::Float(n, fty) => { + let n = n.as_str(); + let mut f = parse_float(&n, fty)?; + if neg { + f = -f; + } + let bits = f.bits; + Value::ByVal(PrimVal::Bytes(bits)) + } + LitKind::FloatUnsuffixed(n) => { + let fty = match ty.sty { + ty::TyFloat(fty) => fty, + _ => bug!() + }; + let n = n.as_str(); + let mut f = parse_float(&n, fty)?; + if neg { + f = -f; + } + let bits = f.bits; + Value::ByVal(PrimVal::Bytes(bits)) + } + LitKind::Bool(b) => Value::ByVal(PrimVal::Bytes(b as u128)), + LitKind::Char(c) => Value::ByVal(PrimVal::Bytes(c as u128)), + }; + Ok(ConstVal::Value(lit)) +} + +fn parse_float<'tcx>(num: &str, fty: ast::FloatTy) + -> Result { + ConstFloat::from_str(num, fty).map_err(|_| ()) +} diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index b476ea56852..e654142d216 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -1,4 +1,5 @@ use rustc::ty::Ty; +use rustc::ty::layout::LayoutOf; use syntax::ast::{FloatTy, IntTy, UintTy}; use rustc_const_math::ConstFloat; @@ -7,115 +8,95 @@ 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, src_ty: Ty<'tcx>, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx, PrimVal> { + use rustc::ty::TypeVariants::*; trace!("Casting {:?}: {:?} to {:?}", val, src_ty, dest_ty); - let src_kind = self.ty_to_primval_kind(src_ty)?; match val { PrimVal::Undef => Ok(PrimVal::Undef), PrimVal::Ptr(ptr) => self.cast_from_ptr(ptr, dest_ty), - val @ PrimVal::Bytes(_) => { - use rustc::mir::interpret::PrimValKind::*; - match src_kind { - F32 => self.cast_from_float(val.to_f32()?, dest_ty), - F64 => self.cast_from_float(val.to_f64()?, dest_ty), - - I8 | I16 | I32 | I64 | I128 => { - self.cast_from_signed_int(val.to_i128()?, dest_ty) - } - - Bool | Char | U8 | U16 | U32 | U64 | U128 | FnPtr | Ptr => { - self.cast_from_int(val.to_u128()?, dest_ty, false) - } + PrimVal::Bytes(b) => { + match src_ty.sty { + TyFloat(fty) => self.cast_from_float(b, fty, dest_ty), + _ => self.cast_from_int(b, src_ty, dest_ty), } } } } - fn cast_from_signed_int(&self, val: i128, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { - self.cast_from_int(val as u128, ty, val < 0) - } - - fn int_to_int(&self, v: i128, ty: IntTy) -> u128 { - match ty { - IntTy::I8 => v as i8 as u128, - IntTy::I16 => v as i16 as u128, - IntTy::I32 => v as i32 as u128, - IntTy::I64 => v as i64 as u128, - IntTy::I128 => v as u128, - IntTy::Isize => { - let ty = self.tcx.sess.target.isize_ty; - self.int_to_int(v, ty) - } - } - } - fn int_to_uint(&self, v: u128, ty: UintTy) -> u128 { - match ty { - UintTy::U8 => v as u8 as u128, - UintTy::U16 => v as u16 as u128, - UintTy::U32 => v as u32 as u128, - UintTy::U64 => v as u64 as u128, - UintTy::U128 => v, - UintTy::Usize => { - let ty = self.tcx.sess.target.usize_ty; - self.int_to_uint(v, ty) - } - } - } - fn cast_from_int( &self, v: u128, - ty: Ty<'tcx>, - negative: bool, + src_ty: Ty<'tcx>, + dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx, PrimVal> { - trace!("cast_from_int: {}, {}, {}", v, ty, negative); + let signed = self.layout_of(src_ty)?.abi.is_signed(); + let v = if signed { + self.sign_extend(v, src_ty)? + } else { + v + }; + trace!("cast_from_int: {}, {}, {}", v, src_ty, dest_ty); use rustc::ty::TypeVariants::*; - match ty.sty { - // Casts to bool are not permitted by rustc, no need to handle them here. - TyInt(ty) => Ok(PrimVal::Bytes(self.int_to_int(v as i128, ty))), - TyUint(ty) => Ok(PrimVal::Bytes(self.int_to_uint(v, ty))), + match dest_ty.sty { + TyInt(_) | TyUint(_) => { + let v = self.truncate(v, dest_ty)?; + Ok(PrimVal::Bytes(v)) + } - TyFloat(fty) if negative => Ok(PrimVal::Bytes(ConstFloat::from_i128(v as i128, fty).bits)), + TyFloat(fty) if signed => Ok(PrimVal::Bytes(ConstFloat::from_i128(v as i128, fty).bits)), TyFloat(fty) => Ok(PrimVal::Bytes(ConstFloat::from_u128(v, fty).bits)), TyChar if v as u8 as u128 == v => Ok(PrimVal::Bytes(v)), TyChar => err!(InvalidChar(v)), // No alignment check needed for raw pointers. But we have to truncate to target ptr size. - TyRawPtr(_) => Ok(PrimVal::Bytes(self.memory.truncate_to_ptr(v).0 as u128)), + TyRawPtr(_) => { + Ok(PrimVal::Bytes(self.memory.truncate_to_ptr(v).0 as u128)) + }, - _ => err!(Unimplemented(format!("int to {:?} cast", ty))), + // Casts to bool are not permitted by rustc, no need to handle them here. + _ => err!(Unimplemented(format!("int to {:?} cast", dest_ty))), } } - fn cast_from_float(&self, val: ConstFloat, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + fn cast_from_float(&self, bits: u128, fty: FloatTy, dest_ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { use rustc::ty::TypeVariants::*; - match ty.sty { + use rustc_apfloat::FloatConvert; + match dest_ty.sty { + // float -> uint TyUint(t) => { let width = t.bit_width().unwrap_or(self.memory.pointer_size() as usize * 8); - match val.ty { - FloatTy::F32 => Ok(PrimVal::Bytes(Single::from_bits(val.bits).to_u128(width).value)), - FloatTy::F64 => Ok(PrimVal::Bytes(Double::from_bits(val.bits).to_u128(width).value)), + match fty { + FloatTy::F32 => Ok(PrimVal::Bytes(Single::from_bits(bits).to_u128(width).value)), + FloatTy::F64 => Ok(PrimVal::Bytes(Double::from_bits(bits).to_u128(width).value)), } }, - + // float -> int TyInt(t) => { let width = t.bit_width().unwrap_or(self.memory.pointer_size() as usize * 8); - match val.ty { - FloatTy::F32 => Ok(PrimVal::from_i128(Single::from_bits(val.bits).to_i128(width).value)), - FloatTy::F64 => Ok(PrimVal::from_i128(Double::from_bits(val.bits).to_i128(width).value)), + match fty { + FloatTy::F32 => Ok(PrimVal::from_i128(Single::from_bits(bits).to_i128(width).value)), + FloatTy::F64 => Ok(PrimVal::from_i128(Double::from_bits(bits).to_i128(width).value)), } }, - - TyFloat(fty) => Ok(PrimVal::from_float(val.convert(fty))), - _ => err!(Unimplemented(format!("float to {:?} cast", ty))), + // f64 -> f32 + TyFloat(FloatTy::F32) if fty == FloatTy::F64 => { + Ok(PrimVal::Bytes(Single::to_bits(Double::from_bits(bits).convert(&mut false).value))) + }, + // f32 -> f64 + TyFloat(FloatTy::F64) if fty == FloatTy::F32 => { + Ok(PrimVal::Bytes(Double::to_bits(Single::from_bits(bits).convert(&mut false).value))) + }, + // identity cast + TyFloat(_) => Ok(PrimVal::Bytes(bits)), + _ => err!(Unimplemented(format!("float to {:?} cast", dest_ty))), } } diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index bc555368f0f..ee5874be9d7 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -1,34 +1,49 @@ +use rustc::hir; +use rustc::middle::const_val::{ConstEvalErr, ConstVal, ErrKind}; +use rustc::middle::const_val::ErrKind::{TypeckError, CheckMatchError}; +use rustc::mir; use rustc::ty::{self, TyCtxt, Ty, Instance}; use rustc::ty::layout::{self, LayoutOf}; -use rustc::ty::subst::Substs; -use rustc::hir::def_id::DefId; -use rustc::mir; -use rustc::middle::const_val::ErrKind::{CheckMatchError, TypeckError}; -use rustc::middle::const_val::{ConstEvalErr, ConstVal}; -use rustc_const_eval::{lookup_const_by_id, ConstContext}; -use rustc::mir::Field; -use rustc_data_structures::indexed_vec::Idx; +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}; - -use rustc_const_math::ConstInt; +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; +use std::rc::Rc; +pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + instance: Instance<'tcx>, + mir: &'mir mir::Mir<'tcx>, + span: Span, +) -> EvalResult<'tcx, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>> { + debug!("mk_borrowck_eval_cx: {:?}", instance); + let param_env = tcx.param_env(instance.def_id()); + let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ()); + // insert a stack frame so any queries have the correct substs + ecx.push_stack_frame( + instance, + 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, ()); + let span = tcx.def_span(instance.def_id()); + let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ()); let mir = ecx.load_mir(instance.def)?; // insert a stack frame so any queries have the correct substs ecx.push_stack_frame( @@ -41,99 +56,115 @@ pub fn mk_eval_cx<'a, 'tcx>( Ok(ecx) } -pub fn eval_body<'a, 'tcx>( +pub fn eval_body_with_mir<'a, 'mir, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, - instance: Instance<'tcx>, + cid: GlobalId<'tcx>, + mir: &'mir mir::Mir<'tcx>, param_env: ty::ParamEnv<'tcx>, -) -> EvalResult<'tcx, (Pointer, Ty<'tcx>)> { - debug!("eval_body: {:?}, {:?}", instance, param_env); - let limits = super::ResourceLimits::default(); - let mut ecx = EvalContext::new(tcx, param_env, limits, CompileTimeEvaluator, ()); - let cid = GlobalId { - instance, - promoted: None, - }; - - if ecx.tcx.has_attr(instance.def_id(), "linkage") { - return Err(ConstEvalError::NotConst("extern global".to_string()).into()); +) -> 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); + None + } } - let instance_ty = instance.ty(tcx); - if tcx.interpret_interner.borrow().get_cached(cid).is_none() { - let mir = ecx.load_mir(instance.def)?; - let layout = ecx.layout_of(instance_ty)?; - assert!(!layout.is_unsized()); - let ptr = ecx.memory.allocate( - layout.size.bytes(), - layout.align, - None, - )?; - tcx.interpret_interner.borrow_mut().cache(cid, ptr.alloc_id); - let cleanup = StackPopCleanup::MarkStatic(Mutability::Immutable); - let name = ty::tls::with(|tcx| tcx.item_path_str(instance.def_id())); - trace!("const_eval: pushing stack frame for global: {}", name); - ecx.push_stack_frame( - instance, - mir.span, - mir, - Place::from_ptr(ptr, layout.align), - cleanup.clone(), - )?; - - while ecx.step()? {} - } - let alloc = tcx.interpret_interner.borrow().get_cached(cid).expect("global not cached"); - Ok((MemoryPointer::new(alloc, 0).into(), instance_ty)) } -pub fn eval_body_as_integer<'a, 'tcx>( +pub fn eval_body<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, + cid: GlobalId<'tcx>, param_env: ty::ParamEnv<'tcx>, - instance: Instance<'tcx>, -) -> EvalResult<'tcx, ConstInt> { - let ptr_ty = eval_body(tcx, instance, param_env); - let (ptr, ty) = ptr_ty?; - let ecx = mk_eval_cx(tcx, instance, param_env)?; - let prim = match ecx.try_read_value(ptr, ecx.layout_of(ty)?.align, ty)? { - Some(Value::ByVal(prim)) => prim.to_bytes()?, - _ => return err!(TypeNotPrimitive(ty)), - }; - use syntax::ast::{IntTy, UintTy}; - use rustc::ty::TypeVariants::*; - use rustc_const_math::{ConstIsize, ConstUsize}; - Ok(match ty.sty { - TyInt(IntTy::I8) => ConstInt::I8(prim as i128 as i8), - TyInt(IntTy::I16) => ConstInt::I16(prim as i128 as i16), - TyInt(IntTy::I32) => ConstInt::I32(prim as i128 as i32), - TyInt(IntTy::I64) => ConstInt::I64(prim as i128 as i64), - TyInt(IntTy::I128) => ConstInt::I128(prim as i128), - TyInt(IntTy::Isize) => ConstInt::Isize( - ConstIsize::new(prim as i128 as i64, tcx.sess.target.isize_ty) - .expect("miri should already have errored"), - ), - TyUint(UintTy::U8) => ConstInt::U8(prim as u8), - TyUint(UintTy::U16) => ConstInt::U16(prim as u16), - TyUint(UintTy::U32) => ConstInt::U32(prim as u32), - TyUint(UintTy::U64) => ConstInt::U64(prim as u64), - TyUint(UintTy::U128) => ConstInt::U128(prim), - TyUint(UintTy::Usize) => ConstInt::Usize( - ConstUsize::new(prim as u64, tcx.sess.target.usize_ty) - .expect("miri should already have errored"), - ), - _ => { - return Err( - ConstEvalError::NeedsRfc( - "evaluating anything other than isize/usize during typeck".to_string(), - ).into(), - ) +) -> 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); + None } - }) + } +} + +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, 'mir, 'tcx, CompileTimeEvaluator>) { + debug!("eval_body: {:?}, {:?}", cid, param_env); + // we start out with the best span we have + // and try improving it down the road when more information is available + let span = tcx.def_span(cid.instance.def_id()); + let mut span = mir.map(|mir| mir.span).unwrap_or(span); + let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ()); + let res = (|| { + let mut mir = match mir { + Some(mir) => mir, + None => ecx.load_mir(cid.instance.def)?, + }; + if let Some(index) = cid.promoted { + mir = &mir.promoted[index]; + } + span = mir.span; + let layout = ecx.layout_of(mir.return_ty().subst(tcx, cid.instance.substs))?; + let alloc = tcx.interpret_interner.get_cached(cid.instance.def_id()); + let alloc = match alloc { + 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.cache(cid.instance.def_id(), ptr.alloc_id); + } + let internally_mutable = !layout.ty.is_freeze(tcx, param_env, mir.span); + let mutability = tcx.is_static(cid.instance.def_id()); + let mutability = if mutability == Some(hir::Mutability::MutMutable) || internally_mutable { + Mutability::Mutable + } else { + Mutability::Immutable + }; + let cleanup = StackPopCleanup::MarkStatic(mutability); + let name = ty::tls::with(|tcx| tcx.item_path_str(cid.instance.def_id())); + let prom = cid.promoted.map_or(String::new(), |p| format!("::promoted[{:?}]", p)); + trace!("const_eval: pushing stack frame for global: {}{}", name, prom); + assert!(mir.arg_count == 0); + ecx.push_stack_frame( + cid.instance, + mir.span, + mir, + Place::from_ptr(ptr, layout.align), + cleanup, + )?; + + while ecx.step()? {} + ptr.alloc_id + } + }; + let ptr = MemoryPointer::new(alloc, 0).into(); + let value = match ecx.try_read_value(ptr, layout.align, layout.ty)? { + Some(val) => val, + _ => Value::ByRef(ptr, layout.align), + }; + Ok((value, ptr, layout.ty)) + })(); + (res, ecx) } pub struct CompileTimeEvaluator; impl<'tcx> Into> for ConstEvalError { fn into(self) -> EvalError<'tcx> { - EvalErrorKind::MachineError(Box::new(self)).into() + EvalErrorKind::MachineError(self.to_string()).into() } } @@ -154,7 +185,7 @@ impl fmt::Display for ConstEvalError { msg ) } - NotConst(ref msg) => write!(f, "Cannot evaluate within constants: \"{}\"", msg), + NotConst(ref msg) => write!(f, "{}", msg), } } } @@ -173,33 +204,48 @@ 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>], + args: &[ValTy<'tcx>], span: Span, - _sig: ty::FnSig<'tcx>, + sig: ty::FnSig<'tcx>, ) -> EvalResult<'tcx, bool> { debug!("eval_fn_call: {:?}", instance); if !ecx.tcx.is_const_fn(instance.def_id()) { - return Err( - ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into(), - ); + let def_id = instance.def_id(); + let (op, oflo) = if let Some(op) = ecx.tcx.is_binop_lang_item(def_id) { + op + } else { + return Err( + ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into(), + ); + }; + let (dest, bb) = destination.expect("128 lowerings can't diverge"); + let dest_ty = sig.output(); + if oflo { + ecx.intrinsic_with_overflow(op, args[0], args[1], dest, dest_ty)?; + } else { + ecx.intrinsic_overflowing(op, args[0], args[1], dest, dest_ty)?; + } + ecx.goto_block(bb); + return Ok(true); } let mir = match ecx.load_mir(instance.def) { Ok(mir) => mir, - Err(EvalError { kind: EvalErrorKind::NoMirFor(path), .. }) => { - // some simple things like `malloc` might get accepted in the future - return Err( - ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path)) - .into(), - ); + Err(err) => { + if let EvalErrorKind::NoMirFor(ref path) = err.kind { + return Err( + ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path)) + .into(), + ); + } + return Err(err); } - Err(other) => return Err(other), }; let (return_place, return_to_block) = match destination { Some((place, block)) => (place, StackPopCleanup::Goto(block)), @@ -219,7 +265,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, @@ -261,7 +307,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>, @@ -277,12 +323,29 @@ 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 init_static<'a>( + ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, + cid: GlobalId<'tcx>, + ) -> EvalResult<'tcx, AllocId> { + // ensure the static is computed + ecx.const_eval(cid)?; + Ok(ecx + .tcx + .interpret_interner + .get_cached(cid.instance.def_id()) + .expect("uncached static")) } fn box_alloc<'a>( - _ecx: &mut EvalContext<'a, 'tcx, Self>, + _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, _ty: Ty<'tcx>, _dest: Place, ) -> EvalResult<'tcx> { @@ -292,7 +355,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> { @@ -302,275 +365,147 @@ impl<'tcx> super::Machine<'tcx> for CompileTimeEvaluator { } } +pub fn const_val_field<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + instance: ty::Instance<'tcx>, + variant: Option, + field: mir::Field, + value: Value, + ty: Ty<'tcx>, +) -> ::rustc::middle::const_val::EvalResult<'tcx> { + trace!("const_val_field: {:?}, {:?}, {:?}, {:?}", instance, field, value, ty); + let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap(); + let result = (|| { + let (mut field, ty) = match value { + Value::ByValPair(..) | Value::ByVal(_) => ecx.read_field(value, variant, field, ty)?.expect("const_val_field on non-field"), + Value::ByRef(ptr, align) => { + let place = Place::Ptr { + ptr, + align, + extra: variant.map_or(PlaceExtra::None, PlaceExtra::DowncastVariant), + }; + let layout = ecx.layout_of(ty)?; + let (place, layout) = ecx.place_field(place, field, layout)?; + let (ptr, align) = place.to_ptr_align(); + (Value::ByRef(ptr, align), layout.ty) + } + }; + if let Value::ByRef(ptr, align) = field { + if let Some(val) = ecx.try_read_value(ptr, align, ty)? { + field = val; + } + } + Ok((field, ty)) + })(); + match result { + Ok((field, ty)) => Ok(tcx.mk_const(ty::Const { + val: ConstVal::Value(field), + ty, + })), + Err(err) => { + let (trace, span) = ecx.generate_stacktrace(None); + let err = ErrKind::Miri(err, trace); + Err(ConstEvalErr { + kind: err.into(), + span, + }) + }, + } +} + +pub fn const_discr<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + instance: ty::Instance<'tcx>, + value: Value, + ty: Ty<'tcx>, +) -> EvalResult<'tcx, u128> { + trace!("const_discr: {:?}, {:?}, {:?}", instance, value, ty); + let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap(); + let (ptr, align) = match value { + Value::ByValPair(..) | Value::ByVal(_) => { + let layout = ecx.layout_of(ty)?; + use super::MemoryKind; + let ptr = ecx.memory.allocate(layout.size.bytes(), layout.align, Some(MemoryKind::Stack))?; + let ptr: Pointer = ptr.into(); + ecx.write_value_to_ptr(value, ptr, layout.align, ty)?; + (ptr, layout.align) + }, + Value::ByRef(ptr, align) => (ptr, align), + }; + let place = Place::from_primval_ptr(ptr, align); + ecx.read_discriminant_value(place, ty) +} + pub fn const_eval_provider<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, - key: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>, + key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, ) -> ::rustc::middle::const_val::EvalResult<'tcx> { trace!("const eval: {:?}", key); - let (def_id, substs) = if let Some(resolved) = lookup_const_by_id(tcx, key) { - resolved - } else { - return Err(ConstEvalErr { - span: tcx.def_span(key.value.0), - kind: TypeckError - }); - }; + let cid = key.value; + let def_id = cid.instance.def.def_id(); - let tables = tcx.typeck_tables_of(def_id); - let body = if let Some(id) = tcx.hir.as_local_node_id(def_id) { - let body_id = tcx.hir.body_owned_by(id); + if tcx.is_foreign_item(def_id) { + let id = tcx.interpret_interner.get_cached(def_id); + let id = match id { + // FIXME: due to caches this shouldn't happen, add some assertions + Some(id) => id, + None => { + let id = tcx.interpret_interner.reserve(); + tcx.interpret_interner.cache(def_id, id); + id + }, + }; + let ty = tcx.type_of(def_id); + let layout = tcx.layout_of(key.param_env.and(ty)).unwrap(); + let ptr = MemoryPointer::new(id, 0); + return Ok(tcx.mk_const(ty::Const { + val: ConstVal::Value(Value::ByRef(ptr.into(), layout.align)), + ty, + })) + } + + if let Some(id) = tcx.hir.as_local_node_id(def_id) { + let tables = tcx.typeck_tables_of(def_id); + let span = tcx.def_span(def_id); // Do match-check before building MIR if tcx.check_match(def_id).is_err() { return Err(ConstEvalErr { - span: tcx.def_span(key.value.0), - kind: CheckMatchError, + kind: Rc::new(CheckMatchError), + span, }); } - tcx.mir_const_qualif(def_id); - tcx.hir.body(body_id) - } else { - tcx.extern_const_body(def_id).body + if let hir::BodyOwnerKind::Const = tcx.hir.body_owner_kind(id) { + tcx.mir_const_qualif(def_id); + } + + // Do not continue into miri if typeck errors occurred; it will fail horribly + if tables.tainted_by_errors { + return Err(ConstEvalErr { + kind: Rc::new(TypeckError), + span, + }); + } }; - // do not continue into miri if typeck errors occurred - // it will fail horribly - if tables.tainted_by_errors { - return Err(ConstEvalErr { span: body.value.span, kind: TypeckError }) - } - - trace!("running old const eval"); - let old_result = ConstContext::new(tcx, key.param_env.and(substs), tables).eval(&body.value); - trace!("old const eval produced {:?}", old_result); - if tcx.sess.opts.debugging_opts.miri { - let instance = ty::Instance::new(def_id, substs); - trace!("const eval instance: {:?}, {:?}", instance, key.param_env); - let miri_result = ::interpret::eval_body(tcx, instance, key.param_env); - match (miri_result, old_result) { - (Err(err), Ok(ok)) => { - trace!("miri failed, ctfe returned {:?}", ok); - tcx.sess.span_warn( - tcx.def_span(key.value.0), - "miri failed to eval, while ctfe succeeded", - ); - let ecx = mk_eval_cx(tcx, instance, key.param_env).unwrap(); - let () = unwrap_miri(&ecx, Err(err)); - Ok(ok) - }, - (_, Err(err)) => Err(err), - (Ok((miri_val, miri_ty)), Ok(ctfe)) => { - let mut ecx = mk_eval_cx(tcx, instance, key.param_env).unwrap(); - let layout = ecx.layout_of(miri_ty).unwrap(); - let miri_place = Place::from_primval_ptr(miri_val, layout.align); - check_ctfe_against_miri(&mut ecx, miri_place, miri_ty, ctfe.val); - Ok(ctfe) - } + 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, + }) + }).map_err(|mut err| { + if tcx.is_static(def_id).is_some() { + ecx.report(&mut err, true, None); } - } else { - old_result - } -} - -fn check_ctfe_against_miri<'a, 'tcx>( - ecx: &mut EvalContext<'a, 'tcx, CompileTimeEvaluator>, - miri_place: Place, - miri_ty: Ty<'tcx>, - ctfe: ConstVal<'tcx>, -) { - use rustc::middle::const_val::ConstAggregate::*; - use rustc_const_math::ConstFloat; - use rustc::ty::TypeVariants::*; - let miri_val = ValTy { - value: ecx.read_place(miri_place).unwrap(), - ty: miri_ty - }; - match miri_ty.sty { - TyInt(int_ty) => { - let prim = get_prim(ecx, miri_val); - let c = ConstInt::new_signed_truncating(prim as i128, - int_ty, - ecx.tcx.sess.target.isize_ty); - let c = ConstVal::Integral(c); - assert_eq!(c, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", c, ctfe); - }, - TyUint(uint_ty) => { - let prim = get_prim(ecx, miri_val); - let c = ConstInt::new_unsigned_truncating(prim, - uint_ty, - ecx.tcx.sess.target.usize_ty); - let c = ConstVal::Integral(c); - assert_eq!(c, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", c, ctfe); - }, - TyFloat(ty) => { - let prim = get_prim(ecx, miri_val); - let f = ConstVal::Float(ConstFloat { bits: prim, ty }); - assert_eq!(f, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", f, ctfe); - }, - TyBool => { - let bits = get_prim(ecx, miri_val); - if bits > 1 { - bug!("miri evaluated to {}, but expected a bool {:?}", bits, ctfe); - } - let b = ConstVal::Bool(bits == 1); - assert_eq!(b, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", b, ctfe); - }, - TyChar => { - let bits = get_prim(ecx, miri_val); - if let Some(cm) = ::std::char::from_u32(bits as u32) { - assert_eq!( - ConstVal::Char(cm), ctfe, - "miri evaluated to {:?}, but expected {:?}", cm, ctfe, - ); - } else { - bug!("miri evaluated to {}, but expected a char {:?}", bits, ctfe); - } - }, - TyStr => { - let value = ecx.follow_by_ref_value(miri_val.value, miri_val.ty); - if let Ok(Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(len))) = value { - let bytes = ecx - .memory - .read_bytes(ptr.into(), len as u64) - .expect("bad miri memory for str"); - if let Ok(s) = ::std::str::from_utf8(bytes) { - if let ConstVal::Str(s2) = ctfe { - assert_eq!(s, s2, "miri produced {:?}, but expected {:?}", s, s2); - } else { - bug!("miri produced {:?}, but expected {:?}", s, ctfe); - } - } else { - bug!( - "miri failed to produce valid utf8 {:?}, while ctfe produced {:?}", - bytes, - ctfe, - ); - } - } else { - bug!("miri evaluated to {:?}, but expected a str {:?}", value, ctfe); - } - }, - TyArray(elem_ty, n) => { - let n = n.val.to_const_int().unwrap().to_u64().unwrap(); - let vec: Vec<(ConstVal, Ty<'tcx>)> = match ctfe { - ConstVal::ByteStr(arr) => arr.data.iter().map(|&b| { - (ConstVal::Integral(ConstInt::U8(b)), ecx.tcx.types.u8) - }).collect(), - ConstVal::Aggregate(Array(v)) => { - v.iter().map(|c| (c.val, c.ty)).collect() - }, - ConstVal::Aggregate(Repeat(v, n)) => { - vec![(v.val, v.ty); n as usize] - }, - _ => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe), - }; - let layout = ecx.layout_of(miri_ty).unwrap(); - for (i, elem) in vec.into_iter().enumerate() { - assert!((i as u64) < n); - let (field_place, _) = - ecx.place_field(miri_place, Field::new(i), layout).unwrap(); - check_ctfe_against_miri(ecx, field_place, elem_ty, elem.0); - } - }, - TyTuple(..) => { - let vec = match ctfe { - ConstVal::Aggregate(Tuple(v)) => v, - _ => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe), - }; - let layout = ecx.layout_of(miri_ty).unwrap(); - for (i, elem) in vec.into_iter().enumerate() { - let (field_place, _) = - ecx.place_field(miri_place, Field::new(i), layout).unwrap(); - check_ctfe_against_miri(ecx, field_place, elem.ty, elem.val); - } - }, - TyAdt(def, _) => { - let mut miri_place = miri_place; - let struct_variant = if def.is_enum() { - let discr = ecx.read_discriminant_value(miri_place, miri_ty).unwrap(); - let variant = def.discriminants(ecx.tcx).position(|variant_discr| { - variant_discr.to_u128_unchecked() == discr - }).expect("miri produced invalid enum discriminant"); - miri_place = ecx.place_downcast(miri_place, variant).unwrap(); - &def.variants[variant] - } else { - def.non_enum_variant() - }; - let vec = match ctfe { - ConstVal::Aggregate(Struct(v)) => v, - ConstVal::Variant(did) => { - assert_eq!(struct_variant.fields.len(), 0); - assert_eq!(did, struct_variant.did); - return; - }, - ctfe => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe), - }; - let layout = ecx.layout_of(miri_ty).unwrap(); - for &(name, elem) in vec.into_iter() { - let field = struct_variant.fields.iter().position(|f| f.name == name).unwrap(); - let (field_place, _) = - ecx.place_field(miri_place, Field::new(field), layout).unwrap(); - check_ctfe_against_miri(ecx, field_place, elem.ty, elem.val); - } - }, - TySlice(_) => bug!("miri produced a slice?"), - // not supported by ctfe - TyRawPtr(_) | - TyRef(..) => {} - TyDynamic(..) => bug!("miri produced a trait object"), - TyClosure(..) => bug!("miri produced a closure"), - TyGenerator(..) => bug!("miri produced a generator"), - TyGeneratorWitness(..) => bug!("miri produced a generator witness"), - TyNever => bug!("miri produced a value of the never type"), - TyProjection(_) => bug!("miri produced a projection"), - TyAnon(..) => bug!("miri produced an impl Trait type"), - TyParam(_) => bug!("miri produced an unmonomorphized type"), - TyInfer(_) => bug!("miri produced an uninferred type"), - TyError => bug!("miri produced a type error"), - TyForeign(_) => bug!("miri produced an extern type"), - // should be fine - TyFnDef(..) => {} - TyFnPtr(_) => { - let value = ecx.value_to_primval(miri_val); - let ptr = match value { - Ok(PrimVal::Ptr(ptr)) => ptr, - value => bug!("expected fn ptr, got {:?}", value), - }; - let inst = ecx.memory.get_fn(ptr).unwrap(); - match ctfe { - ConstVal::Function(did, substs) => { - let ctfe = ty::Instance::resolve( - ecx.tcx, - ecx.param_env, - did, - substs, - ).unwrap(); - assert_eq!(inst, ctfe, "expected fn ptr {:?}, but got {:?}", ctfe, inst); - }, - _ => bug!("ctfe produced {:?}, but miri produced function {:?}", ctfe, inst), - } - }, - } -} - -fn get_prim<'a, 'tcx>( - ecx: &mut EvalContext<'a, 'tcx, CompileTimeEvaluator>, - val: ValTy<'tcx>, -) -> u128 { - let res = ecx.value_to_primval(val).and_then(|prim| prim.to_bytes()); - unwrap_miri(ecx, res) -} - -fn unwrap_miri<'a, 'tcx, T>( - ecx: &EvalContext<'a, 'tcx, CompileTimeEvaluator>, - res: Result>, -) -> T { - match res { - Ok(val) => val, - Err(mut err) => { - ecx.report(&mut err); - ecx.tcx.sess.abort_if_errors(); - bug!("{:#?}", err); + let (trace, span) = ecx.generate_stacktrace(None); + let err = ErrKind::Miri(err, trace); + ConstEvalErr { + kind: err.into(), + span, } - } + }) } diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 25f933c5da6..13090ca5330 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -3,14 +3,15 @@ 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}; use rustc::ty::subst::{Subst, Substs}; use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::maps::TyCtxtAt; use rustc_data_structures::indexed_vec::Idx; -use syntax::codemap::{self, DUMMY_SP}; +use rustc::middle::const_val::FrameInfo; +use syntax::codemap::{self, Span}; use syntax::ast::Mutability; use rustc::mir::interpret::{ GlobalId, Value, Pointer, PrimVal, PrimValKind, @@ -18,41 +19,41 @@ use rustc::mir::interpret::{ }; use super::{Place, PlaceExtra, Memory, - HasMemory, MemoryKind, operator, + HasMemory, MemoryKind, 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, /// The results of the type checker, from rustc. - pub tcx: TyCtxt<'a, 'tcx, 'tcx>, + pub tcx: TyCtxtAt<'a, 'tcx, 'tcx>, /// Bounds in scope for polymorphic evaluations. pub param_env: ty::ParamEnv<'tcx>, /// 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, - /// The maximum number of operations that may be executed. + /// The maximum number of terminators that may be evaluated. /// This prevents infinite loops and huge computations from freezing up const eval. /// Remove once halting problem is solved. - pub(crate) steps_remaining: u64, + pub(crate) steps_remaining: usize, } /// 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>, @@ -102,23 +103,6 @@ pub enum StackPopCleanup { None, } -#[derive(Copy, Clone, Debug)] -pub struct ResourceLimits { - pub memory_size: u64, - pub step_limit: u64, - pub stack_limit: usize, -} - -impl Default for ResourceLimits { - fn default() -> Self { - ResourceLimits { - memory_size: 100 * 1024 * 1024, // 100 MB - step_limit: 1_000_000, - stack_limit: 100, - } - } -} - #[derive(Copy, Clone, Debug)] pub struct TyAndPacked<'tcx> { pub ty: Ty<'tcx>, @@ -131,6 +115,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 +131,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 + *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 + *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 +170,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,11 +180,10 @@ 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>, + tcx: TyCtxtAt<'a, 'tcx, 'tcx>, param_env: ty::ParamEnv<'tcx>, - limits: ResourceLimits, machine: M, memory_data: M::MemoryData, ) -> Self { @@ -199,10 +191,10 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { machine, tcx, param_env, - memory: Memory::new(tcx, limits.memory_size, memory_data), + memory: Memory::new(tcx, memory_data), stack: Vec::new(), - stack_limit: limits.stack_limit, - steps_remaining: limits.step_limit, + stack_limit: tcx.sess.const_eval_stack_frame_limit.get(), + steps_remaining: tcx.sess.const_eval_step_limit.get(), } } @@ -214,15 +206,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,45 +232,26 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { )) } - pub(super) fn const_to_value(&mut self, const_val: &ConstVal<'tcx>, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { - use rustc::middle::const_val::ConstVal::*; - - let primval = match *const_val { - Integral(const_int) => PrimVal::Bytes(const_int.to_u128_unchecked()), - - Float(val) => PrimVal::Bytes(val.bits), - - Bool(b) => PrimVal::from_bool(b), - Char(c) => PrimVal::from_char(c), - - Str(ref s) => return self.str_to_value(s), - - ByteStr(ref bs) => { - let ptr = self.memory.allocate_cached(bs.data); - PrimVal::Ptr(ptr) - } - - Unevaluated(def_id, substs) => { + 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)?; - return Ok(self.read_global_as_value(GlobalId { + self.read_global_as_value(GlobalId { instance, promoted: None, - }, self.layout_of(ty)?)); + }, ty) } - - Aggregate(..) | - Variant(_) => bug!("should not have aggregate or variant constants in MIR"), - // function items are zero sized and thus have no readable value - Function(..) => PrimVal::Undef, - }; - - Ok(Value::ByVal(primval)) + ConstVal::Value(val) => Ok(val), + } } pub(super) fn resolve(&self, def_id: DefId, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, ty::Instance<'tcx>> { - let substs = self.tcx.trans_apply_param_substs(self.substs(), &substs); + trace!("resolve: {:?}, {:#?}", def_id, substs); + trace!("substs: {:#?}", self.substs()); + trace!("param_env: {:#?}", self.param_env); + let substs = self.tcx.trans_apply_param_substs_env(self.substs(), self.param_env, &substs); ty::Instance::resolve( - self.tcx, + *self.tcx, self.param_env, def_id, substs, @@ -286,7 +259,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } pub(super) fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { - ty.is_sized(self.tcx.at(DUMMY_SP), self.param_env) + ty.is_sized(self.tcx, self.param_env) } pub fn load_mir( @@ -313,7 +286,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { // miri doesn't care about lifetimes, and will choke on some crazy ones // let's simply get rid of them let without_lifetimes = self.tcx.erase_regions(&ty); - let substituted = without_lifetimes.subst(self.tcx, substs); + let substituted = without_lifetimes.subst(*self.tcx, substs); let substituted = self.tcx.fully_normalize_monormophic_ty(&substituted); substituted } @@ -402,14 +375,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(); @@ -477,7 +450,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { StackPopCleanup::MarkStatic(mutable) => { if let Place::Ptr { ptr, .. } = frame.return_place { // FIXME: to_ptr()? might be too extreme here, static zsts might reach this under certain conditions - self.memory.mark_static_initalized( + self.memory.mark_static_initialized( ptr.to_ptr()?.alloc_id, mutable, )? @@ -563,16 +536,16 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { UnaryOp(un_op, ref operand) => { let val = self.eval_operand_to_primval(operand)?; - let kind = self.ty_to_primval_kind(dest_ty)?; + let val = self.unary_op(un_op, val, dest_ty)?; self.write_primval( dest, - operator::unary_op(un_op, val, kind)?, + val, dest_ty, )?; } Aggregate(ref kind, ref operands) => { - self.inc_step_counter_and_check_limit(operands.len() as u64)?; + self.inc_step_counter_and_check_limit(operands.len())?; let (dest, active_field_index) = match **kind { mir::AggregateKind::Adt(adt_def, variant_index, _, active_field_index) => { @@ -600,7 +573,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { Repeat(ref operand, _) => { let (elem_ty, length) = match dest_ty.sty { - ty::TyArray(elem_ty, n) => (elem_ty, n.val.to_const_int().unwrap().to_u64().unwrap()), + ty::TyArray(elem_ty, n) => (elem_ty, n.val.unwrap_u64()), _ => { bug!( "tried to assign array-repeat to non-array type {:?}", @@ -720,8 +693,13 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { bug!("reifying a fn ptr that requires \ const arguments"); } - let instance = self.resolve(def_id, substs)?; - let fn_ptr = self.memory.create_fn_alloc(instance); + let instance: EvalResult<'tcx, _> = ty::Instance::resolve( + *self.tcx, + self.param_env, + def_id, + substs, + ).ok_or(EvalErrorKind::TypeckError.into()); + let fn_ptr = self.memory.create_fn_alloc(instance?); let valty = ValTy { value: Value::ByVal(PrimVal::Ptr(fn_ptr)), ty: dest_ty, @@ -748,7 +726,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { ty::TyClosure(def_id, substs) => { let substs = self.tcx.trans_apply_param_substs(self.substs(), &substs); let instance = ty::Instance::resolve_closure( - self.tcx, + *self.tcx, def_id, substs, ty::ClosureKind::FnOnce, @@ -771,9 +749,9 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { let place = self.eval_place(place)?; let discr_val = self.read_discriminant_value(place, ty)?; if let ty::TyAdt(adt_def, _) = ty.sty { - trace!("Read discriminant {}, valid discriminants {:?}", discr_val, adt_def.discriminants(self.tcx).collect::>()); - if adt_def.discriminants(self.tcx).all(|v| { - discr_val != v.to_u128_unchecked() + trace!("Read discriminant {}, valid discriminants {:?}", discr_val, adt_def.discriminants(*self.tcx).collect::>()); + if adt_def.discriminants(*self.tcx).all(|v| { + discr_val != v.val }) { return err!(InvalidDiscriminant); @@ -820,7 +798,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { pub fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, ValTy<'tcx>> { use rustc::mir::Operand::*; - let ty = self.monomorphize(op.ty(self.mir(), self.tcx), self.substs()); + let ty = self.monomorphize(op.ty(self.mir(), *self.tcx), self.substs()); match *op { // FIXME: do some more logic on `move` to invalidate the old location Copy(ref place) | @@ -841,7 +819,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)? } }; @@ -928,8 +906,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } layout::Variants::Tagged { .. } => { let discr_val = dest_ty.ty_adt_def().unwrap() - .discriminant_for_variant(self.tcx, variant_index) - .to_u128_unchecked(); + .discriminant_for_variant(*self.tcx, variant_index) + .val; let (discr_dest, discr) = self.place_field(dest, mir::Field::new(0), layout)?; self.write_primval(discr_dest, PrimVal::Bytes(discr_val), discr.ty)?; @@ -953,9 +931,38 @@ 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 + .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 = self.const_eval(gid)?; + self.const_to_value(&cv.val, ty) + } + + pub fn const_eval(&self, gid: GlobalId<'tcx>) -> EvalResult<'tcx, &'tcx ty::Const<'tcx>> { + let param_env = if self.tcx.is_static(gid.instance.def_id()).is_some() { + use rustc::traits; + ty::ParamEnv::empty(traits::Reveal::All) + } else { + self.param_env + }; + self.tcx.const_eval(param_env.and(gid)).map_err(|err| match *err.kind { + ErrKind::Miri(ref err, _) => match err.kind { + EvalErrorKind::TypeckError | + EvalErrorKind::Layout(_) => EvalErrorKind::TypeckError.into(), + _ => EvalErrorKind::ReferencedConstant.into(), + }, + ErrKind::TypeckError => EvalErrorKind::TypeckError.into(), + ref other => bug!("const eval returned {:?}", other), + }) } pub fn force_allocation(&mut self, place: Place) -> EvalResult<'tcx, Place> { @@ -1121,20 +1128,22 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { dest_align: Align, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx> { - trace!("write_value_to_ptr: {:#?}", value); let layout = self.layout_of(dest_ty)?; + trace!("write_value_to_ptr: {:#?}, {}, {:#?}", value, dest_ty, layout); match value { Value::ByRef(ptr, align) => { self.memory.copy(ptr, align.min(layout.align), dest, dest_align.min(layout.align), layout.size.bytes(), false) } Value::ByVal(primval) => { - match layout.abi { - layout::Abi::Scalar(_) => {} - _ if primval.is_undef() => {} + let signed = match layout.abi { + layout::Abi::Scalar(ref scal) => match scal.value { + layout::Primitive::Int(_, signed) => signed, + _ => false, + }, + _ if primval.is_undef() => false, _ => bug!("write_value_to_ptr: invalid ByVal layout: {:#?}", layout) - } - // TODO: Do we need signedness? - self.memory.write_primval(dest.to_ptr()?, dest_align, primval, layout.size.bytes(), false) + }; + self.memory.write_primval(dest.to_ptr()?, dest_align, primval, layout.size.bytes(), signed) } Value::ByValPair(a_val, b_val) => { let ptr = dest.to_ptr()?; @@ -1247,7 +1256,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { pointee_ty: Ty<'tcx>, ) -> EvalResult<'tcx, Value> { let ptr_size = self.memory.pointer_size(); - let p: Pointer = self.memory.read_ptr_sized_unsigned(ptr, ptr_align)?.into(); + let p: Pointer = self.memory.read_ptr_sized(ptr, ptr_align)?.into(); if self.type_is_sized(pointee_ty) { Ok(p.to_value()) } else { @@ -1255,11 +1264,15 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { let extra = ptr.offset(ptr_size, self)?; match self.tcx.struct_tail(pointee_ty).sty { ty::TyDynamic(..) => Ok(p.to_value_with_vtable( - self.memory.read_ptr_sized_unsigned(extra, ptr_align)?.to_ptr()?, + self.memory.read_ptr_sized(extra, ptr_align)?.to_ptr()?, )), - ty::TySlice(..) | ty::TyStr => Ok( - p.to_value_with_len(self.memory.read_ptr_sized_unsigned(extra, ptr_align)?.to_bytes()? as u64), - ), + ty::TySlice(..) | ty::TyStr => { + let len = self + .memory + .read_ptr_sized(extra, ptr_align)? + .to_bytes()?; + Ok(p.to_value_with_len(len as u64)) + }, _ => bug!("unsized primval ptr read from {:?}", pointee_ty), } } @@ -1271,7 +1284,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { let ptr = ptr.to_ptr()?; let val = match ty.sty { ty::TyBool => { - let val = self.memory.read_primval(ptr, ptr_align, 1, false)?; + let val = self.memory.read_primval(ptr, ptr_align, 1)?; let val = match val { PrimVal::Bytes(0) => false, PrimVal::Bytes(1) => true, @@ -1281,7 +1294,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { PrimVal::from_bool(val) } ty::TyChar => { - let c = self.memory.read_primval(ptr, ptr_align, 4, false)?.to_bytes()? as u32; + let c = self.memory.read_primval(ptr, ptr_align, 4)?.to_bytes()? as u32; match ::std::char::from_u32(c) { Some(ch) => PrimVal::from_char(ch), None => return err!(InvalidChar(c as u128)), @@ -1298,7 +1311,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { I128 => 16, Isize => self.memory.pointer_size(), }; - self.memory.read_primval(ptr, ptr_align, size, true)? + self.memory.read_primval(ptr, ptr_align, size)? } ty::TyUint(uint_ty) => { @@ -1311,17 +1324,17 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { U128 => 16, Usize => self.memory.pointer_size(), }; - self.memory.read_primval(ptr, ptr_align, size, false)? + self.memory.read_primval(ptr, ptr_align, size)? } ty::TyFloat(FloatTy::F32) => { - PrimVal::Bytes(self.memory.read_primval(ptr, ptr_align, 4, false)?.to_bytes()?) + PrimVal::Bytes(self.memory.read_primval(ptr, ptr_align, 4)?.to_bytes()?) } ty::TyFloat(FloatTy::F64) => { - PrimVal::Bytes(self.memory.read_primval(ptr, ptr_align, 8, false)?.to_bytes()?) + PrimVal::Bytes(self.memory.read_primval(ptr, ptr_align, 8)?.to_bytes()?) } - ty::TyFnPtr(_) => self.memory.read_ptr_sized_unsigned(ptr, ptr_align)?, + ty::TyFnPtr(_) => self.memory.read_ptr_sized(ptr, ptr_align)?, ty::TyRef(_, ref tam) | ty::TyRawPtr(ref tam) => return self.read_ptr(ptr, ptr_align, tam.ty).map(Some), @@ -1331,12 +1344,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } if let layout::Abi::Scalar(ref scalar) = self.layout_of(ty)?.abi { - let mut signed = false; - if let layout::Int(_, s) = scalar.value { - signed = s; - } let size = scalar.value.size(self).bytes(); - self.memory.read_primval(ptr, ptr_align, size, signed)? + self.memory.read_primval(ptr, ptr_align, size)? } else { return Ok(None); } @@ -1348,15 +1357,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 } @@ -1385,7 +1394,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { let ptr = self.into_ptr(src)?; // u64 cast is from usize to u64, which is always good let valty = ValTy { - value: ptr.to_value_with_len(length.val.to_const_int().unwrap().to_u64().unwrap() ), + value: ptr.to_value_with_len(length.val.unwrap_u64() ), ty: dest_ty, }; self.write_value(valty, dest) @@ -1402,7 +1411,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } (_, &ty::TyDynamic(ref data, _)) => { let trait_ref = data.principal().unwrap().with_self_ty( - self.tcx, + *self.tcx, src_pointee_ty, ); let trait_ref = self.tcx.erase_regions(&trait_ref); @@ -1504,11 +1513,12 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { write!(msg, ":").unwrap(); match self.stack[frame].get_local(local) { - Err(EvalError { kind: EvalErrorKind::DeadLocal, .. }) => { - write!(msg, " is dead").unwrap(); - } Err(err) => { - panic!("Failed to access local: {:?}", err); + if let EvalErrorKind::DeadLocal = err.kind { + write!(msg, " is dead").unwrap(); + } else { + panic!("Failed to access local: {:?}", err); + } } Ok(Value::ByRef(ptr, align)) => { match ptr.into_inner_primval() { @@ -1566,7 +1576,40 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { Ok(()) } - pub fn report(&self, e: &mut EvalError) { + pub fn generate_stacktrace(&self, explicit_span: Option) -> (Vec, Span) { + let mut last_span = None; + let mut frames = Vec::new(); + // skip 1 because the last frame is just the environment of the constant + for &Frame { instance, span, .. } in self.stack().iter().skip(1).rev() { + // make sure we don't emit frames that are duplicates of the previous + if explicit_span == Some(span) { + last_span = Some(span); + continue; + } + if let Some(last) = last_span { + if last == span { + continue; + } + } else { + last_span = Some(span); + } + let location = if self.tcx.def_key(instance.def_id()).disambiguated_data.data == DefPathData::ClosureExpr { + "closure".to_owned() + } else { + instance.to_string() + }; + frames.push(FrameInfo { span, location }); + } + trace!("generate stacktrace: {:#?}, {:?}", frames, explicit_span); + (frames, self.tcx.span) + } + + pub fn report(&self, e: &mut EvalError, as_err: bool, explicit_span: Option) { + match e.kind { + EvalErrorKind::Layout(_) | + EvalErrorKind::TypeckError => return, + _ => {}, + } if let Some(ref mut backtrace) = e.backtrace { let mut trace_text = "\n\nAn error occurred in miri:\n".to_string(); backtrace.resolve(); @@ -1599,29 +1642,60 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } if let Some(frame) = self.stack().last() { let block = &frame.mir.basic_blocks()[frame.block]; - let span = if frame.stmt < block.statements.len() { + let span = explicit_span.unwrap_or_else(|| if frame.stmt < block.statements.len() { block.statements[frame.stmt].source_info.span } else { block.terminator().source_info.span + }); + trace!("reporting const eval failure at {:?}", span); + let mut err = if as_err { + ::rustc::middle::const_val::struct_error(*self.tcx, span, "constant evaluation error") + } else { + let node_id = self + .stack() + .iter() + .rev() + .filter_map(|frame| self.tcx.hir.as_local_node_id(frame.instance.def_id())) + .next() + .expect("some part of a failing const eval must be local"); + self.tcx.struct_span_lint_node( + ::rustc::lint::builtin::CONST_ERR, + node_id, + span, + "constant evaluation error", + ) }; - let mut err = self.tcx.sess.struct_span_err(span, &e.to_string()); - for &Frame { instance, span, .. } in self.stack().iter().rev() { - if self.tcx.def_key(instance.def_id()).disambiguated_data.data == - DefPathData::ClosureExpr - { - err.span_note(span, "inside call to closure"); - continue; - } - err.span_note(span, &format!("inside call to {}", instance)); + let (frames, span) = self.generate_stacktrace(explicit_span); + err.span_label(span, e.to_string()); + for FrameInfo { span, location } in frames { + err.span_note(span, &format!("inside call to `{}`", location)); } err.emit(); } else { self.tcx.sess.err(&e.to_string()); } } + + pub fn sign_extend(&self, value: u128, ty: Ty<'tcx>) -> EvalResult<'tcx, u128> { + let layout = self.layout_of(ty)?; + let size = layout.size.bits(); + assert!(layout.abi.is_signed()); + // sign extend + let amt = 128 - size; + // shift the unsigned value to the left + // and back to the right as signed (essentially fills with FF on the left) + Ok((((value << amt) as i128) >> amt) as u128) + } + + pub fn truncate(&self, value: u128, ty: Ty<'tcx>) -> EvalResult<'tcx, u128> { + let size = self.layout_of(ty)?.size.bits(); + let amt = 128 - size; + // truncate (shift left to drop out leftover values, shift right to fill with zeroes) + Ok((value << amt) >> amt) + } } -impl<'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()) @@ -1655,14 +1729,3 @@ impl<'tcx> Frame<'tcx> { return Ok(old); } } - -// TODO(solson): Upstream these methods into rustc::ty::layout. - -pub fn resolve_drop_in_place<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - ty: Ty<'tcx>, -) -> ty::Instance<'tcx> { - let def_id = tcx.require_lang_item(::rustc::middle::lang_items::DropInPlaceFnLangItem); - let substs = tcx.intern_substs(&[ty.into()]); - ty::Instance::resolve(tcx, ty::ParamEnv::empty(Reveal::All), def_id, substs).unwrap() -} diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs index c2989dbaaf1..5af0a053e92 100644 --- a/src/librustc_mir/interpret/machine.rs +++ b/src/librustc_mir/interpret/machine.rs @@ -2,7 +2,7 @@ //! This separation exists to ensure that no fancy miri features like //! interpreting common C functions leak into CTFE. -use rustc::mir::interpret::{AllocId, EvalResult, PrimVal, MemoryPointer, AccessKind}; +use rustc::mir::interpret::{AllocId, EvalResult, PrimVal, MemoryPointer, AccessKind, GlobalId}; use super::{EvalContext, Place, ValTy, Memory}; use rustc::mir; @@ -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,37 @@ 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>; + + /// Called when requiring a pointer to a static. Non const eval can + /// create a mutable memory location for `static mut` + fn init_static<'a>( + ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, + cid: GlobalId<'tcx>, + ) -> EvalResult<'tcx, AllocId>; /// Heap allocations via the `box` keyword /// /// Returns a pointer to the allocated memory 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 +99,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 +112,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..b369f80e849 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -1,8 +1,9 @@ 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::Instance; +use rustc::ty::maps::TyCtxtAt; use rustc::ty::layout::{self, Align, TargetDataLayout}; use syntax::ast::Mutability; @@ -19,8 +20,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 +28,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, @@ -44,28 +43,20 @@ pub struct Memory<'a, 'tcx: 'a, M: Machine<'tcx>> { /// Stores statics while they are being processed, before they are interned and thus frozen uninitialized_statics: HashMap, - /// Number of virtual bytes allocated. - memory_usage: u64, - - /// Maximum number of virtual bytes that may be allocated. - memory_size: u64, - /// The current stack frame. Used to check accesses against locks. pub cur_frame: usize, - pub tcx: TyCtxt<'a, 'tcx, 'tcx>, + pub tcx: TyCtxtAt<'a, 'tcx, 'tcx>, } -impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, max_memory: u64, data: M::MemoryData) -> Self { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { + pub fn new(tcx: TyCtxtAt<'a, 'tcx, 'tcx>, data: M::MemoryData) -> Self { Memory { data, alloc_kind: HashMap::new(), alloc_map: HashMap::new(), uninitialized_statics: HashMap::new(), tcx, - memory_size: max_memory, - memory_usage: 0, cur_frame: usize::max_value(), } } @@ -77,7 +68,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { } pub fn create_fn_alloc(&mut self, instance: Instance<'tcx>) -> MemoryPointer { - let id = self.tcx.interpret_interner.borrow_mut().create_fn_alloc(instance); + let id = self.tcx.interpret_interner.create_fn_alloc(instance); MemoryPointer::new(id, 0) } @@ -93,22 +84,15 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { align: Align, kind: Option>, ) -> EvalResult<'tcx, MemoryPointer> { - if self.memory_size - self.memory_usage < size { - return err!(OutOfMemory { - allocation_size: size, - memory_size: self.memory_size, - memory_usage: self.memory_usage, - }); - } - self.memory_usage += size; assert_eq!(size as usize as u64, size); let alloc = Allocation { bytes: vec![0; size as usize], relocations: BTreeMap::new(), undef_mask: UndefMask::new(size), align, + runtime_mutability: Mutability::Immutable, }; - let id = self.tcx.interpret_interner.borrow_mut().reserve(); + let id = self.tcx.interpret_interner.reserve(); M::add_lock(self, id); match kind { Some(kind @ MemoryKind::Stack) | @@ -119,7 +103,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 +147,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(()), @@ -192,12 +171,12 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { "uninitializedstatic".to_string(), format!("{:?}", kind), )) - } else if self.tcx.interpret_interner.borrow().get_fn(ptr.alloc_id).is_some() { + } else if self.tcx.interpret_interner.get_fn(ptr.alloc_id).is_some() { return err!(DeallocatedWrongMemoryKind( "function".to_string(), format!("{:?}", kind), )) - } else if self.tcx.interpret_interner.borrow().get_alloc(ptr.alloc_id).is_some() { + } else if self.tcx.interpret_interner.get_alloc(ptr.alloc_id).is_some() { return err!(DeallocatedWrongMemoryKind( "static".to_string(), format!("{:?}", kind), @@ -228,7 +207,6 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { } } - self.memory_usage -= alloc.bytes.len() as u64; debug!("deallocated : {}", ptr.alloc_id); Ok(()) @@ -292,7 +270,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) { @@ -301,11 +279,10 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { None => match self.uninitialized_statics.get(&id) { Some(alloc) => Ok(alloc), None => { - let int = self.tcx.interpret_interner.borrow(); // static alloc? - int.get_alloc(id) + self.tcx.interpret_interner.get_alloc(id) // no alloc? produce an error - .ok_or_else(|| if int.get_fn(id).is_some() { + .ok_or_else(|| if self.tcx.interpret_interner.get_fn(id).is_some() { EvalErrorKind::DerefFunctionPointer.into() } else { EvalErrorKind::DanglingPointerDeref.into() @@ -326,11 +303,10 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { None => match self.uninitialized_statics.get_mut(&id) { Some(alloc) => Ok(alloc), None => { - let int = self.tcx.interpret_interner.borrow(); // no alloc or immutable alloc? produce an error - if int.get_alloc(id).is_some() { + if self.tcx.interpret_interner.get_alloc(id).is_some() { err!(ModifiedConstantMemory) - } else if int.get_fn(id).is_some() { + } else if self.tcx.interpret_interner.get_fn(id).is_some() { err!(DerefFunctionPointer) } else { err!(DanglingPointerDeref) @@ -347,7 +323,6 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { debug!("reading fn ptr: {}", ptr.alloc_id); self.tcx .interpret_interner - .borrow() .get_fn(ptr.alloc_id) .ok_or(EvalErrorKind::ExecuteMemory.into()) } @@ -376,27 +351,25 @@ 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) { Some(a) => (a, " (static in the process of initialization)".to_owned()), None => { - let int = self.tcx.interpret_interner.borrow(); // static alloc? - match int.get_alloc(id) { + match self.tcx.interpret_interner.get_alloc(id) { Some(a) => (a, "(immutable)".to_owned()), - None => if let Some(func) = int.get_fn(id) { + None => if let Some(func) = self.tcx.interpret_interner.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 +414,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 +425,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 +489,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,80 +497,47 @@ 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), + Some(_) => self.mark_static_initialized(alloc, mutability), } } /// mark an allocation as static and initialized, either mutable or not - pub fn mark_static_initalized( + pub fn mark_static_initialized( &mut self, alloc_id: AllocId, mutability: Mutability, ) -> EvalResult<'tcx> { trace!( - "mark_static_initalized {:?}, mutability: {:?}", + "mark_static_initialized {:?}, mutability: {:?}", alloc_id, mutability ); - 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.runtime_mutability = mutability; + let alloc = self.tcx.intern_const_alloc(alloc); + self.tcx.interpret_interner.intern_at_reserved(alloc_id, alloc); + // recurse into inner allocations + for &alloc in alloc.relocations.values() { + self.mark_inner_allocation_initialized(alloc, mutability)?; } - return Ok(()); + } else { + bug!("no allocation found for {:?}", alloc_id); } - // 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(()) } @@ -720,7 +655,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { Ok(()) } - pub fn read_primval(&self, ptr: MemoryPointer, ptr_align: Align, size: u64, signed: bool) -> EvalResult<'tcx, PrimVal> { + pub fn read_primval(&self, ptr: MemoryPointer, ptr_align: Align, size: u64) -> EvalResult<'tcx, PrimVal> { self.check_relocation_edges(ptr, size)?; // Make sure we don't read part of a pointer as a pointer let endianness = self.endianness(); let bytes = self.get_bytes_unchecked(ptr, size, ptr_align.min(self.int_align(size)))?; @@ -730,11 +665,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { return Ok(PrimVal::Undef.into()); } // Now we do the actual reading - let bytes = if signed { - read_target_int(endianness, bytes).unwrap() as u128 - } else { - read_target_uint(endianness, bytes).unwrap() - }; + let bytes = read_target_uint(endianness, bytes).unwrap(); // See if we got a pointer if size != self.pointer_size() { if self.relocations(ptr, size)?.count() != 0 { @@ -751,8 +682,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { Ok(PrimVal::Bytes(bytes)) } - pub fn read_ptr_sized_unsigned(&self, ptr: MemoryPointer, ptr_align: Align) -> EvalResult<'tcx, PrimVal> { - self.read_primval(ptr, ptr_align, self.pointer_size(), false) + pub fn read_ptr_sized(&self, ptr: MemoryPointer, ptr_align: Align) -> EvalResult<'tcx, PrimVal> { + self.read_primval(ptr, ptr_align, self.pointer_size()) } pub fn write_primval(&mut self, ptr: MemoryPointer, ptr_align: Align, val: PrimVal, size: u64, signed: bool) -> EvalResult<'tcx> { @@ -764,19 +695,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { val.offset as u128 } - PrimVal::Bytes(bytes) => { - // We need to mask here, or the byteorder crate can die when given a u64 larger - // than fits in an integer of the requested size. - let mask = match size { - 1 => !0u8 as u128, - 2 => !0u16 as u128, - 4 => !0u32 as u128, - 8 => !0u64 as u128, - 16 => !0, - n => bug!("unexpected PrimVal::Bytes size: {}", n), - }; - bytes & mask - } + PrimVal::Bytes(bytes) => bytes, PrimVal::Undef => { self.mark_definedness(PrimVal::Ptr(ptr).into(), size, false)?; @@ -829,7 +748,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 +802,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 +863,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 +874,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,27 +887,20 @@ 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 { - match endianness { - layout::Endian::Little => source.read_int128::(source.len()), - layout::Endian::Big => source.read_int128::(source.len()), - } -} - //////////////////////////////////////////////////////////////////////////////// // Unaligned accesses //////////////////////////////////////////////////////////////////////////////// -pub trait HasMemory<'a, 'tcx: 'a, M: Machine<'tcx>> { - 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. @@ -997,7 +910,7 @@ pub trait HasMemory<'a, 'tcx: 'a, M: Machine<'tcx>> { ) -> EvalResult<'tcx, Pointer> { Ok(match value { Value::ByRef(ptr, align) => { - self.memory().read_ptr_sized_unsigned(ptr.to_ptr()?, align)? + self.memory().read_ptr_sized(ptr.to_ptr()?, align)? } Value::ByVal(ptr) | Value::ByValPair(ptr, _) => ptr, @@ -1011,8 +924,8 @@ pub trait HasMemory<'a, 'tcx: 'a, M: Machine<'tcx>> { match value { Value::ByRef(ref_ptr, align) => { let mem = self.memory(); - let ptr = mem.read_ptr_sized_unsigned(ref_ptr.to_ptr()?, align)?.into(); - let vtable = mem.read_ptr_sized_unsigned( + let ptr = mem.read_ptr_sized(ref_ptr.to_ptr()?, align)?.into(); + let vtable = mem.read_ptr_sized( ref_ptr.offset(mem.pointer_size(), &mem.tcx.data_layout)?.to_ptr()?, align )?.to_ptr()?; @@ -1033,8 +946,8 @@ pub trait HasMemory<'a, 'tcx: 'a, M: Machine<'tcx>> { match value { Value::ByRef(ref_ptr, align) => { let mem = self.memory(); - let ptr = mem.read_ptr_sized_unsigned(ref_ptr.to_ptr()?, align)?.into(); - let len = mem.read_ptr_sized_unsigned( + let ptr = mem.read_ptr_sized(ref_ptr.to_ptr()?, align)?.into(); + let len = mem.read_ptr_sized( ref_ptr.offset(mem.pointer_size(), &mem.tcx.data_layout)?.to_ptr()?, align )?.to_bytes()? as u64; @@ -1051,31 +964,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 fee62c8a82e..ae6337d82c3 100644 --- a/src/librustc_mir/interpret/mod.rs +++ b/src/librustc_mir/interpret/mod.rs @@ -11,13 +11,23 @@ mod step; mod terminator; mod traits; -pub use self::eval_context::{EvalContext, Frame, ResourceLimits, StackPopCleanup, +pub use self::eval_context::{EvalContext, Frame, StackPopCleanup, TyAndPacked, ValTy}; pub use self::place::{Place, PlaceExtra}; pub use self::memory::{Memory, MemoryKind, HasMemory}; -pub use self::const_eval::{eval_body_as_integer, eval_body, CompileTimeEvaluator, const_eval_provider}; +pub use self::const_eval::{ + eval_body_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::memory::{write_target_uint, write_target_int, read_target_uint}; diff --git a/src/librustc_mir/interpret/operator.rs b/src/librustc_mir/interpret/operator.rs index 6ab1aec38b8..dfc0c4a824a 100644 --- a/src/librustc_mir/interpret/operator.rs +++ b/src/librustc_mir/interpret/operator.rs @@ -1,14 +1,15 @@ use rustc::mir; -use rustc::ty::Ty; +use rustc::ty::{self, Ty}; use rustc_const_math::ConstFloat; use syntax::ast::FloatTy; use std::cmp::Ordering; +use rustc::ty::layout::LayoutOf; use super::{EvalContext, Place, Machine, ValTy}; -use rustc::mir::interpret::{EvalResult, PrimVal, PrimValKind, Value, bytes_to_f32, bytes_to_f64}; +use rustc::mir::interpret::{EvalResult, PrimVal, Value}; -impl<'a, '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, @@ -55,57 +56,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } } -macro_rules! overflow { - ($op:ident, $l:expr, $r:expr) => ({ - let (val, overflowed) = $l.$op($r); - let primval = PrimVal::Bytes(val as u128); - Ok((primval, overflowed)) - }) -} - -macro_rules! int_arithmetic { - ($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({ - let l = $l; - let r = $r; - use rustc::mir::interpret::PrimValKind::*; - match $kind { - I8 => overflow!($int_op, l as i8, r as i8), - I16 => overflow!($int_op, l as i16, r as i16), - I32 => overflow!($int_op, l as i32, r as i32), - I64 => overflow!($int_op, l as i64, r as i64), - I128 => overflow!($int_op, l as i128, r as i128), - U8 => overflow!($int_op, l as u8, r as u8), - U16 => overflow!($int_op, l as u16, r as u16), - U32 => overflow!($int_op, l as u32, r as u32), - U64 => overflow!($int_op, l as u64, r as u64), - U128 => overflow!($int_op, l as u128, r as u128), - _ => bug!("int_arithmetic should only be called on int primvals"), - } - }) -} - -macro_rules! int_shift { - ($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({ - let l = $l; - let r = $r; - let r_wrapped = r as u32; - match $kind { - I8 => overflow!($int_op, l as i8, r_wrapped), - I16 => overflow!($int_op, l as i16, r_wrapped), - I32 => overflow!($int_op, l as i32, r_wrapped), - I64 => overflow!($int_op, l as i64, r_wrapped), - I128 => overflow!($int_op, l as i128, r_wrapped), - U8 => overflow!($int_op, l as u8, r_wrapped), - U16 => overflow!($int_op, l as u16, r_wrapped), - U32 => overflow!($int_op, l as u32, r_wrapped), - U64 => overflow!($int_op, l as u64, r_wrapped), - U128 => overflow!($int_op, l as u128, r_wrapped), - _ => bug!("int_shift should only be called on int primvals"), - }.map(|(val, over)| (val, over || r != r_wrapped as u128)) - }) -} - -impl<'a, '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, @@ -116,11 +67,10 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { right_ty: Ty<'tcx>, ) -> EvalResult<'tcx, (PrimVal, bool)> { use rustc::mir::BinOp::*; - use rustc::mir::interpret::PrimValKind::*; let left_kind = self.ty_to_primval_kind(left_ty)?; let right_kind = self.ty_to_primval_kind(right_ty)?; - //trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind); + trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind); // I: Handle operations that support pointers if !left_kind.is_float() && !right_kind.is_float() { @@ -133,13 +83,34 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { let l = left.to_bytes()?; let r = right.to_bytes()?; + let left_layout = self.layout_of(left_ty)?; + // These ops can have an RHS with a different numeric type. if right_kind.is_int() && (bin_op == Shl || bin_op == Shr) { - return match bin_op { - Shl => int_shift!(left_kind, overflowing_shl, l, r), - Shr => int_shift!(left_kind, overflowing_shr, l, r), - _ => bug!("it has already been checked that this is a shift op"), + let signed = left_layout.abi.is_signed(); + let mut r = r as u32; + let size = left_layout.size.bits() as u32; + let oflo = r >= size; + if oflo { + r %= size; + } + let result = if signed { + let l = self.sign_extend(l, left_ty)? as i128; + let result = match bin_op { + Shl => l << r, + Shr => l >> r, + _ => bug!("it has already been checked that this is a shift op"), + }; + result as u128 + } else { + match bin_op { + Shl => l << r, + Shr => l >> r, + _ => bug!("it has already been checked that this is a shift op"), + } }; + let truncated = self.truncate(result, left_ty)?; + return Ok((PrimVal::Bytes(truncated), oflo)); } if left_kind != right_kind { @@ -179,41 +150,95 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } }; - let val = match (bin_op, left_kind) { - (_, F32) => float_op(bin_op, l, r, FloatTy::F32), - (_, F64) => float_op(bin_op, l, r, FloatTy::F64), + if left_layout.abi.is_signed() { + let op: Option bool> = match bin_op { + Lt => Some(i128::lt), + Le => Some(i128::le), + Gt => Some(i128::gt), + Ge => Some(i128::ge), + _ => None, + }; + if let Some(op) = op { + let l = self.sign_extend(l, left_ty)? as i128; + let r = self.sign_extend(r, right_ty)? as i128; + return Ok((PrimVal::from_bool(op(&l, &r)), false)); + } + let op: Option (i128, bool)> = match bin_op { + Rem | Div if r == 0 => return Ok((PrimVal::Bytes(l), true)), + Div => Some(i128::overflowing_div), + Rem => Some(i128::overflowing_rem), + Add => Some(i128::overflowing_add), + Sub => Some(i128::overflowing_sub), + Mul => Some(i128::overflowing_mul), + _ => None, + }; + if let Some(op) = op { + let l128 = self.sign_extend(l, left_ty)? as i128; + let r = self.sign_extend(r, right_ty)? as i128; + let size = left_layout.size.bits(); + match bin_op { + Rem | Div => { + // int_min / -1 + if r == -1 && l == (1 << (size - 1)) { + return Ok((PrimVal::Bytes(l), true)); + } + }, + _ => {}, + } + trace!("{}, {}, {}", l, l128, r); + let (result, mut oflo) = op(l128, r); + trace!("{}, {}", result, oflo); + if !oflo && size != 128 { + let max = 1 << (size - 1); + oflo = result >= max || result < -max; + } + let result = result as u128; + let truncated = self.truncate(result, left_ty)?; + return Ok((PrimVal::Bytes(truncated), oflo)); + } + } + if let ty::TyFloat(fty) = left_ty.sty { + return Ok((float_op(bin_op, l, r, fty), false)); + } - (Eq, _) => PrimVal::from_bool(l == r), - (Ne, _) => PrimVal::from_bool(l != r), + // only ints left + let val = match bin_op { + Eq => PrimVal::from_bool(l == r), + Ne => PrimVal::from_bool(l != r), - (Lt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) < (r as i128)), - (Lt, _) => PrimVal::from_bool(l < r), - (Le, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) <= (r as i128)), - (Le, _) => PrimVal::from_bool(l <= r), - (Gt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) > (r as i128)), - (Gt, _) => PrimVal::from_bool(l > r), - (Ge, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) >= (r as i128)), - (Ge, _) => PrimVal::from_bool(l >= r), + Lt => PrimVal::from_bool(l < r), + Le => PrimVal::from_bool(l <= r), + Gt => PrimVal::from_bool(l > r), + Ge => PrimVal::from_bool(l >= r), - (BitOr, _) => PrimVal::Bytes(l | r), - (BitAnd, _) => PrimVal::Bytes(l & r), - (BitXor, _) => PrimVal::Bytes(l ^ r), + BitOr => PrimVal::Bytes(l | r), + BitAnd => PrimVal::Bytes(l & r), + BitXor => PrimVal::Bytes(l ^ r), - (Add, k) if k.is_int() => return int_arithmetic!(k, overflowing_add, l, r), - (Sub, k) if k.is_int() => return int_arithmetic!(k, overflowing_sub, l, r), - (Mul, k) if k.is_int() => return int_arithmetic!(k, overflowing_mul, l, r), - (Div, k) if k.is_int() => return int_arithmetic!(k, overflowing_div, l, r), - (Rem, k) if k.is_int() => return int_arithmetic!(k, overflowing_rem, l, r), + Add | Sub | Mul | Rem | Div => { + let op: fn(u128, u128) -> (u128, bool) = match bin_op { + Add => u128::overflowing_add, + Sub => u128::overflowing_sub, + Mul => u128::overflowing_mul, + Rem | Div if r == 0 => return Ok((PrimVal::Bytes(l), true)), + Div => u128::overflowing_div, + Rem => u128::overflowing_rem, + _ => bug!(), + }; + let (result, oflo) = op(l, r); + let truncated = self.truncate(result, left_ty)?; + return Ok((PrimVal::Bytes(truncated), oflo || truncated != result)); + } _ => { let msg = format!( "unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, - left_kind, + left_ty, right, - right_kind + right_ty, ); return err!(Unimplemented(msg)); } @@ -221,47 +246,33 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { Ok((val, false)) } -} - -pub fn unary_op<'tcx>( - un_op: mir::UnOp, - val: PrimVal, - val_kind: PrimValKind, -) -> EvalResult<'tcx, PrimVal> { - use rustc::mir::UnOp::*; - use rustc::mir::interpret::PrimValKind::*; - - let bytes = val.to_bytes()?; - - let result_bytes = match (un_op, val_kind) { - (Not, Bool) => !val.to_bool()? as u128, - - (Not, U8) => !(bytes as u8) as u128, - (Not, U16) => !(bytes as u16) as u128, - (Not, U32) => !(bytes as u32) as u128, - (Not, U64) => !(bytes as u64) as u128, - (Not, U128) => !bytes, - - (Not, I8) => !(bytes as i8) as u128, - (Not, I16) => !(bytes as i16) as u128, - (Not, I32) => !(bytes as i32) as u128, - (Not, I64) => !(bytes as i64) as u128, - (Not, I128) => !(bytes as i128) as u128, - - (Neg, I8) => -(bytes as i8) as u128, - (Neg, I16) => -(bytes as i16) as u128, - (Neg, I32) => -(bytes as i32) as u128, - (Neg, I64) => -(bytes as i64) as u128, - (Neg, I128) => -(bytes as i128) as u128, - - (Neg, F32) => (-bytes_to_f32(bytes)).bits, - (Neg, F64) => (-bytes_to_f64(bytes)).bits, - - _ => { - let msg = format!("unimplemented unary op: {:?}, {:?}", un_op, val); - return err!(Unimplemented(msg)); - } - }; - - Ok(PrimVal::Bytes(result_bytes)) + + pub fn unary_op( + &self, + un_op: mir::UnOp, + val: PrimVal, + ty: Ty<'tcx>, + ) -> EvalResult<'tcx, PrimVal> { + use rustc::mir::UnOp::*; + use rustc_apfloat::ieee::{Single, Double}; + use rustc_apfloat::Float; + + let bytes = val.to_bytes()?; + let size = self.layout_of(ty)?.size.bits(); + + let result_bytes = match (un_op, &ty.sty) { + + (Not, ty::TyBool) => !val.to_bool()? as u128, + + (Not, _) => !bytes, + + (Neg, ty::TyFloat(FloatTy::F32)) => Single::to_bits(-Single::from_bits(bytes)), + (Neg, ty::TyFloat(FloatTy::F64)) => Double::to_bits(-Double::from_bits(bytes)), + + (Neg, _) if bytes == (1 << (size - 1)) => return err!(OverflowingMath), + (Neg, _) => (-(bytes as i128)) as u128, + }; + + Ok(PrimVal::Bytes(self.truncate(result_bytes, ty)?)) + } } diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 701b7a07ac9..d27de3ef6bf 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -71,7 +71,7 @@ impl<'tcx> Place { pub(super) fn elem_ty_and_len(self, ty: Ty<'tcx>) -> (Ty<'tcx>, u64) { match ty.sty { - ty::TyArray(elem, n) => (elem, n.val.to_const_int().unwrap().to_u64().unwrap() as u64), + ty::TyArray(elem, n) => (elem, n.val.unwrap_u64() as u64), ty::TySlice(elem) => { match self { @@ -90,7 +90,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( @@ -103,18 +103,43 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { Local(mir::RETURN_PLACE) => err!(ReadFromReturnPointer), // Directly reading a local will always succeed Local(local) => self.frame().get_local(local).map(Some), - // Directly reading a static will always succeed - Static(ref static_) => { - let instance = ty::Instance::mono(self.tcx, static_.def_id); - Ok(Some(self.read_global_as_value(GlobalId { - instance, - promoted: None, - }, self.layout_of(self.place_ty(place))?))) - } + // No fast path for statics. Reading from statics is rare and would require another + // Machine function to handle differently in miri. + Static(_) => Ok(None), Projection(ref proj) => self.try_read_place_projection(proj), } } + pub fn read_field( + &self, + base: Value, + variant: Option, + field: mir::Field, + base_ty: Ty<'tcx>, + ) -> EvalResult<'tcx, Option<(Value, Ty<'tcx>)>> { + let mut base_layout = self.layout_of(base_ty)?; + if let Some(variant_index) = variant { + base_layout = base_layout.for_variant(self, variant_index); + } + let field_index = field.index(); + let field = base_layout.field(self, field_index)?; + if field.size.bytes() == 0 { + return Ok(Some((Value::ByVal(PrimVal::Undef), field.ty))) + } + let offset = base_layout.fields.offset(field_index); + match base { + // the field covers the entire type + Value::ByValPair(..) | + Value::ByVal(_) if offset.bytes() == 0 && field.size == base_layout.size => Ok(Some((base, field.ty))), + // split fat pointers, 2 element tuples, ... + Value::ByValPair(a, b) if base_layout.fields.count() == 2 => { + let val = [a, b][field_index]; + Ok(Some((Value::ByVal(val), field.ty))) + }, + _ => Ok(None), + } + } + fn try_read_place_projection( &mut self, proj: &mir::PlaceProjection<'tcx>, @@ -126,23 +151,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { }; let base_ty = self.place_ty(&proj.base); match proj.elem { - Field(field, _) => { - let base_layout = self.layout_of(base_ty)?; - let field_index = field.index(); - let field = base_layout.field(&self, field_index)?; - let offset = base_layout.fields.offset(field_index); - match base { - // the field covers the entire type - Value::ByValPair(..) | - Value::ByVal(_) if offset.bytes() == 0 && field.size == base_layout.size => Ok(Some(base)), - // split fat pointers, 2 element tuples, ... - Value::ByValPair(a, b) if base_layout.fields.count() == 2 => { - let val = [a, b][field_index]; - Ok(Some(Value::ByVal(val))) - }, - _ => Ok(None), - } - }, + Field(field, _) => Ok(self.read_field(base, None, field, base_ty)?.map(|(f, _)| f)), // The NullablePointer cases should work fine, need to take care for normal enums Downcast(..) | Subslice { .. } | @@ -188,17 +197,29 @@ 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 + .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 alloc = Machine::init_static(self, cid)?; + Place::Ptr { + ptr: MemoryPointer::new(alloc, 0).into(), + align: layout.align, + extra: PlaceExtra::None, + } } } @@ -424,7 +445,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { pub fn place_ty(&self, place: &mir::Place<'tcx>) -> Ty<'tcx> { self.monomorphize( - place.ty(self.mir(), self.tcx).to_ty(self.tcx), + place.ty(self.mir(), *self.tcx).to_ty(*self.tcx), self.substs(), ) } diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs index 2b0f9041d51..4e1750caf26 100644 --- a/src/librustc_mir/interpret/step.rs +++ b/src/librustc_mir/interpret/step.rs @@ -2,22 +2,13 @@ //! //! 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> { - pub fn inc_step_counter_and_check_limit(&mut self, n: u64) -> EvalResult<'tcx> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { + pub fn inc_step_counter_and_check_limit(&mut self, n: usize) -> EvalResult<'tcx> { self.steps_remaining = self.steps_remaining.saturating_sub(n); if self.steps_remaining > 0 { Ok(()) @@ -28,7 +19,6 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { /// Returns true as long as there are more things to do. pub fn step(&mut self) -> EvalResult<'tcx, bool> { - self.inc_step_counter_and_check_limit(1)?; if self.stack.is_empty() { return Ok(false); } @@ -41,52 +31,16 @@ 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); } + self.inc_step_counter_and_check_limit(1)?; + 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) } @@ -98,6 +52,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { // Some statements (e.g. box) push new stack frames. We have to record the stack frame number // *before* executing the statement. let frame_idx = self.cur_frame(); + self.tcx.span = stmt.source_info.span; + self.memory.tcx.span = stmt.source_info.span; match stmt.kind { Assign(ref place, ref rvalue) => self.eval_rvalue_into_place(rvalue, place)?, @@ -146,190 +102,12 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<'tcx> { trace!("{:?}", terminator.kind); + self.tcx.span = terminator.source_info.span; + self.memory.tcx.span = terminator.source_info.span; self.eval_terminator(terminator)?; if !self.stack.is_empty() { trace!("// {:?}", self.frame().block); } 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 606bda51edb..babc7847014 100644 --- a/src/librustc_mir/interpret/terminator/mod.rs +++ b/src/librustc_mir/interpret/terminator/mod.rs @@ -5,15 +5,14 @@ use syntax::codemap::Span; use syntax::abi::Abi; use rustc::mir::interpret::{EvalResult, PrimVal, Value}; -use super::{EvalContext, eval_context, - Place, Machine, ValTy}; +use super::{EvalContext, Place, Machine, ValTy}; use rustc_data_structures::indexed_vec::Idx; use interpret::memory::HasMemory; 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; @@ -45,8 +44,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { // Branch to the `otherwise` case by default, if no match is found. let mut target_block = targets[targets.len() - 1]; - for (index, const_int) in values.iter().enumerate() { - let prim = PrimVal::Bytes(const_int.to_u128_unchecked()); + for (index, &const_int) in values.iter().enumerate() { + let prim = PrimVal::Bytes(const_int); if discr_prim.to_bytes()? == prim.to_bytes()? { target_block = targets[index]; break; @@ -72,10 +71,10 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { ty::TyFnPtr(sig) => { let fn_ptr = self.value_to_primval(func)?.to_ptr()?; let instance = self.memory.get_fn(fn_ptr)?; - let instance_ty = instance.ty(self.tcx); + let instance_ty = instance.ty(*self.tcx); match instance_ty.sty { ty::TyFnDef(..) => { - let real_sig = instance_ty.fn_sig(self.tcx); + let real_sig = instance_ty.fn_sig(*self.tcx); let sig = self.tcx.erase_late_bound_regions_and_normalize(&sig); let real_sig = self.tcx.erase_late_bound_regions_and_normalize(&real_sig); if !self.check_sig_compat(sig, real_sig)? { @@ -88,7 +87,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } ty::TyFnDef(def_id, substs) => ( self.resolve(def_id, substs)?, - func.ty.fn_sig(self.tcx), + func.ty.fn_sig(*self.tcx), ), _ => { let msg = format!("can't handle callee of type {:?}", func.ty); @@ -117,7 +116,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { let ty = self.tcx.trans_apply_param_substs(self.substs(), &ty); trace!("TerminatorKind::drop: {:?}, type {}", location, ty); - let instance = eval_context::resolve_drop_in_place(self.tcx, ty); + let instance = ::monomorphize::resolve_drop_in_place(*self.tcx, ty); self.drop_place( place, instance, @@ -402,7 +401,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { let ptr_size = self.memory.pointer_size(); let ptr_align = self.tcx.data_layout.pointer_align; let (ptr, vtable) = self.into_ptr_vtable_pair(args[0].value)?; - let fn_ptr = self.memory.read_ptr_sized_unsigned( + let fn_ptr = self.memory.read_ptr_sized( vtable.offset(ptr_size * (idx as u64 + 3), &self)?, ptr_align )?.to_ptr()?; diff --git a/src/librustc_mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs index 22417201f0d..ded27108e71 100644 --- a/src/librustc_mir/interpret/traits.rs +++ b/src/librustc_mir/interpret/traits.rs @@ -3,10 +3,9 @@ use rustc::ty::layout::{Size, Align, LayoutOf}; use syntax::ast::Mutability; use rustc::mir::interpret::{PrimVal, Value, MemoryPointer, EvalResult}; -use super::{EvalContext, eval_context, - Machine}; +use super::{EvalContext, Machine}; -impl<'a, '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. /// @@ -34,7 +33,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { None, )?; - let drop = eval_context::resolve_drop_in_place(self.tcx, ty); + let drop = ::monomorphize::resolve_drop_in_place(*self.tcx, ty); let drop = self.memory.create_fn_alloc(drop); self.memory.write_ptr_sized_unsigned(vtable, ptr_align, PrimVal::Ptr(drop))?; @@ -52,9 +51,9 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } } - self.memory.mark_static_initalized( + self.memory.mark_static_initialized( vtable.alloc_id, - Mutability::Mutable, + Mutability::Immutable, )?; Ok(vtable) @@ -80,8 +79,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { ) -> EvalResult<'tcx, (Size, Align)> { let pointer_size = self.memory.pointer_size(); let pointer_align = self.tcx.data_layout.pointer_align; - let size = self.memory.read_ptr_sized_unsigned(vtable.offset(pointer_size, self)?, pointer_align)?.to_bytes()? as u64; - let align = self.memory.read_ptr_sized_unsigned( + let size = self.memory.read_ptr_sized(vtable.offset(pointer_size, self)?, pointer_align)?.to_bytes()? as u64; + let align = self.memory.read_ptr_sized( vtable.offset(pointer_size * 2, self)?, pointer_align )?.to_bytes()? as u64; diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 8c15d1cf8b0..c31e95fd826 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -16,6 +16,8 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![deny(warnings)] +#![feature(slice_patterns)] +#![feature(from_ref)] #![feature(box_patterns)] #![feature(box_syntax)] #![feature(catch_expr)] @@ -38,6 +40,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(nonzero)] #![feature(underscore_lifetimes)] +extern crate arena; #[macro_use] extern crate bitflags; #[macro_use] extern crate log; @@ -52,7 +55,6 @@ extern crate syntax; extern crate syntax_pos; extern crate rustc_back; extern crate rustc_const_math; -extern crate rustc_const_eval; extern crate core; // for NonZero extern crate log_settings; extern crate rustc_apfloat; @@ -70,6 +72,7 @@ pub mod util; pub mod interpret; pub mod monomorphize; +pub use hair::pattern::check_crate as matchck_crate; use rustc::ty::maps::Providers; pub fn provide(providers: &mut Providers) { @@ -77,6 +80,7 @@ pub fn provide(providers: &mut Providers) { shim::provide(providers); transform::provide(providers); providers.const_eval = interpret::const_eval_provider; + providers.check_match = hair::pattern::check_match; } __build_diagnostic_array! { librustc_mir, DIAGNOSTICS } diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index 0f512569adf..f6b47efca31 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -194,15 +194,17 @@ 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}; use rustc::ty::{self, TypeFoldable, Ty, TyCtxt}; use rustc::ty::adjustment::CustomCoerceUnsized; use rustc::session::config; -use rustc::mir::{self, Location}; +use rustc::mir::{self, Location, Promoted}; use rustc::mir::visit::Visitor as MirVisitor; use rustc::mir::mono::MonoItem; +use rustc::mir::interpret::GlobalId; use monomorphize::{self, Instance}; use rustc::util::nodemap::{FxHashSet, FxHashMap, DefIdMap}; @@ -377,7 +379,19 @@ fn collect_items_rec<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, recursion_depth_reset = None; - collect_neighbours(tcx, instance, true, &mut neighbors); + let cid = GlobalId { + instance, + promoted: None, + }; + let param_env = ty::ParamEnv::empty(traits::Reveal::All); + + match tcx.const_eval(param_env.and(cid)) { + Ok(val) => collect_const(tcx, val, instance.substs, &mut neighbors), + Err(err) => { + let span = tcx.def_span(def_id); + err.report(tcx, span, "static"); + } + } } MonoItem::Fn(instance) => { // Sanity check whether this ended up being collected accidentally @@ -389,7 +403,7 @@ fn collect_items_rec<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, recursion_depths)); check_type_length_limit(tcx, instance); - collect_neighbours(tcx, instance, false, &mut neighbors); + collect_neighbours(tcx, instance, &mut neighbors); } MonoItem::GlobalAsm(..) => { recursion_depth_reset = None; @@ -498,7 +512,6 @@ struct MirNeighborCollector<'a, 'tcx: 'a> { mir: &'a mir::Mir<'tcx>, output: &'a mut Vec>, param_substs: &'tcx Substs<'tcx>, - const_context: bool, } impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { @@ -568,15 +581,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, location: Location) { debug!("visiting const {:?} @ {:?}", *constant, location); - 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); - } + collect_const(self.tcx, constant, self.param_substs, self.output); self.super_const(constant); } @@ -592,30 +597,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { mir::TerminatorKind::Call { ref func, .. } => { let callee_ty = func.ty(self.mir, tcx); let callee_ty = tcx.trans_apply_param_substs(self.param_substs, &callee_ty); - - let constness = match (self.const_context, &callee_ty.sty) { - (true, &ty::TyFnDef(def_id, substs)) if self.tcx.is_const_fn(def_id) => { - let instance = - ty::Instance::resolve(self.tcx, - ty::ParamEnv::empty(traits::Reveal::All), - def_id, - substs).unwrap(); - Some(instance) - } - _ => None - }; - - if let Some(const_fn_instance) = constness { - // If this is a const fn, called from a const context, we - // have to visit its body in order to find any fn reifications - // it might contain. - collect_neighbours(self.tcx, - const_fn_instance, - true, - self.output); - } else { - visit_fn_use(self.tcx, callee_ty, true, &mut self.output); - } + visit_fn_use(self.tcx, callee_ty, true, &mut self.output); } mir::TerminatorKind::Drop { ref location, .. } | mir::TerminatorKind::DropAndReplace { ref location, .. } => { @@ -1098,26 +1080,59 @@ 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>, +) { + if let Some(did) = tcx.interpret_interner.get_corresponding_static_def_id(alloc_id) { + let instance = Instance::mono(tcx, did); + if should_monomorphize_locally(tcx, &instance) { + trace!("collecting static {:?}", did); + output.push(MonoItem::Static(did)); + } + } else if let Some(alloc) = tcx.interpret_interner.get_alloc(alloc_id) { + trace!("collecting {:?} with {:#?}", alloc_id, alloc); + for &inner in alloc.relocations.values() { + collect_miri(tcx, inner, output); + } + } else if let Some(fn_instance) = tcx.interpret_interner.get_fn(alloc_id) { + if should_monomorphize_locally(tcx, &fn_instance) { + trace!("collecting {:?} with {:#?}", alloc_id, fn_instance); + output.push(create_fn_mono_item(fn_instance)); + } + } 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>, - const_context: bool, output: &mut Vec>) { let mir = tcx.instance_mir(instance.def); - let mut visitor = MirNeighborCollector { + MirNeighborCollector { tcx, mir: &mir, output, param_substs: instance.substs, - const_context, - }; - - visitor.visit_mir(&mir); - for promoted in &mir.promoted { - visitor.mir = promoted; - visitor.visit_mir(promoted); + }.visit_mir(&mir); + let param_env = ty::ParamEnv::empty(traits::Reveal::All); + for (i, promoted) in mir.promoted.iter().enumerate() { + use rustc_data_structures::indexed_vec::Idx; + let cid = GlobalId { + instance, + promoted: Some(Promoted::new(i)), + }; + match tcx.const_eval(param_env.and(cid)) { + Ok(val) => collect_const(tcx, val, instance.substs, output), + Err(err) => { + err.report(tcx, promoted.span, "promoted"); + } + } } } @@ -1129,3 +1144,60 @@ fn def_id_to_string<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, printer.push_def_path(def_id, &mut output); output } + +fn collect_const<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + constant: &ty::Const<'tcx>, + param_substs: &'tcx Substs<'tcx>, + output: &mut Vec>, +) { + debug!("visiting const {:?}", *constant); + + let val = match constant.val { + ConstVal::Unevaluated(def_id, substs) => { + let param_env = ty::ParamEnv::empty(traits::Reveal::All); + let substs = tcx.trans_apply_param_substs(param_substs, + &substs); + let instance = ty::Instance::resolve(tcx, + param_env, + def_id, + substs).unwrap(); + + let cid = GlobalId { + instance, + promoted: None, + }; + match tcx.const_eval(param_env.and(cid)) { + Ok(val) => val.val, + Err(err) => { + let span = tcx.def_span(def_id); + err.report(tcx, span, "constant"); + return; + } + } + }, + _ => constant.val, + }; + match val { + ConstVal::Unevaluated(..) => bug!("const eval yielded unevaluated const"), + ConstVal::Value(Value::ByValPair(PrimVal::Ptr(a), PrimVal::Ptr(b))) => { + collect_miri(tcx, a.alloc_id, output); + collect_miri(tcx, b.alloc_id, output); + } + ConstVal::Value(Value::ByValPair(_, PrimVal::Ptr(ptr))) | + ConstVal::Value(Value::ByValPair(PrimVal::Ptr(ptr), _)) | + ConstVal::Value(Value::ByVal(PrimVal::Ptr(ptr))) => + collect_miri(tcx, ptr.alloc_id, output), + ConstVal::Value(Value::ByRef(Pointer { primval: PrimVal::Ptr(ptr) }, _)) => { + // by ref should only collect the inner allocation, not the value itself + let alloc = tcx + .interpret_interner + .get_alloc(ptr.alloc_id) + .expect("ByRef to extern static is not allowed"); + for &inner in alloc.relocations.values() { + collect_miri(tcx, inner, output); + } + } + _ => {}, + } +} diff --git a/src/librustc_mir/monomorphize/item.rs b/src/librustc_mir/monomorphize/item.rs index 38b8ffc6b9c..1f7f1237ba7 100644 --- a/src/librustc_mir/monomorphize/item.rs +++ b/src/librustc_mir/monomorphize/item.rs @@ -314,7 +314,7 @@ impl<'a, 'tcx> DefPathBasedNames<'a, 'tcx> { output.push('['); self.push_type_name(inner_type, output); write!(output, "; {}", - len.val.to_const_int().unwrap().to_u64().unwrap()).unwrap(); + len.val.unwrap_u64()).unwrap(); output.push(']'); }, ty::TySlice(inner_type) => { diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index e6ebdd3d6c1..9aff7fa2a2c 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -16,7 +16,7 @@ use rustc::mir::*; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::subst::{Kind, Subst, Substs}; use rustc::ty::maps::Providers; -use rustc_const_math::{ConstInt, ConstUsize}; +use rustc::mir::interpret::{Value, PrimVal}; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; @@ -303,7 +303,7 @@ fn build_clone_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, match self_ty.sty { _ if is_copy => builder.copy_shim(), ty::TyArray(ty, len) => { - let len = len.val.to_const_int().unwrap().to_u64().unwrap(); + let len = len.val.unwrap_u64(); builder.array_shim(dest, src, ty, len) } ty::TyClosure(def_id, substs) => { @@ -443,7 +443,8 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { ty: func_ty, literal: Literal::Value { value: tcx.mk_const(ty::Const { - val: ConstVal::Function(self.def_id, substs), + // ZST function type + val: ConstVal::Value(Value::ByVal(PrimVal::Undef)), ty: func_ty }), }, @@ -501,13 +502,12 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { } fn make_usize(&self, value: u64) -> Box> { - let value = ConstUsize::new(value, self.tcx.sess.target.usize_ty).unwrap(); box Constant { span: self.span, ty: self.tcx.types.usize, literal: Literal::Value { value: self.tcx.mk_const(ty::Const { - val: ConstVal::Integral(ConstInt::Usize(value)), + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(value.into()))), ty: self.tcx.types.usize, }) } @@ -739,8 +739,8 @@ fn build_call_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty, literal: Literal::Value { value: tcx.mk_const(ty::Const { - val: ConstVal::Function(def_id, - Substs::identity_for_item(tcx, def_id)), + // ZST function type + val: ConstVal::Value(Value::ByVal(PrimVal::Undef)), ty }), }, 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/const_prop.rs b/src/librustc_mir/transform/const_prop.rs new file mode 100644 index 00000000000..f133d6e9c6d --- /dev/null +++ b/src/librustc_mir/transform/const_prop.rs @@ -0,0 +1,517 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Propagates constants for early reporting of statically known +//! assertion failures + + +use rustc::hir::def::Def; +use rustc::mir::{Constant, Literal, Location, Place, Mir, Operand, Rvalue, Local}; +use rustc::mir::{NullOp, StatementKind, Statement, BasicBlock, LocalKind}; +use rustc::mir::{TerminatorKind, ClearCrossCrate, SourceInfo, BinOp, ProjectionElem}; +use rustc::mir::visit::{Visitor, PlaceContext}; +use rustc::middle::const_val::ConstVal; +use rustc::ty::{TyCtxt, self, Instance}; +use rustc::mir::interpret::{Value, PrimVal, GlobalId}; +use interpret::{eval_body_with_mir, mk_borrowck_eval_cx, ValTy}; +use transform::{MirPass, MirSource}; +use syntax::codemap::Span; +use rustc::ty::subst::Substs; +use rustc_data_structures::indexed_vec::IndexVec; +use rustc::ty::ParamEnv; +use rustc::ty::layout::{ + LayoutOf, TyLayout, LayoutError, + HasTyCtxt, TargetDataLayout, HasDataLayout, +}; + +pub struct ConstProp; + +impl MirPass for ConstProp { + fn run_pass<'a, 'tcx>(&self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + source: MirSource, + mir: &mut Mir<'tcx>) { + // will be evaluated by miri and produce its errors there + if source.promoted.is_some() { + return; + } + match tcx.describe_def(source.def_id) { + // skip statics because they'll be evaluated by miri anyway + Some(Def::Static(..)) => return, + _ => {}, + } + trace!("ConstProp starting for {:?}", source.def_id); + + // FIXME(oli-obk, eddyb) Optimize locals (or even local paths) to hold + // constants, instead of just checking for const-folding succeeding. + // That would require an uniform one-def no-mutation analysis + // and RPO (or recursing when needing the value of a local). + let mut optimization_finder = ConstPropagator::new(mir, tcx, source); + optimization_finder.visit_mir(mir); + + trace!("ConstProp done for {:?}", source.def_id); + } +} + +type Const<'tcx> = (Value, ty::Ty<'tcx>, Span); + +/// Finds optimization opportunities on the MIR. +struct ConstPropagator<'b, 'a, 'tcx:'a+'b> { + mir: &'b Mir<'tcx>, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + source: MirSource, + places: IndexVec>>, + can_const_prop: IndexVec, + param_env: ParamEnv<'tcx>, +} + +impl<'a, 'b, 'tcx> LayoutOf> for &'a ConstPropagator<'a, 'b, 'tcx> { + type TyLayout = Result, LayoutError<'tcx>>; + + fn layout_of(self, ty: ty::Ty<'tcx>) -> Self::TyLayout { + self.tcx.layout_of(self.param_env.and(ty)) + } +} + +impl<'a, 'b, 'tcx> HasDataLayout for &'a ConstPropagator<'a, 'b, 'tcx> { + #[inline] + fn data_layout(&self) -> &TargetDataLayout { + &self.tcx.data_layout + } +} + +impl<'a, 'b, 'tcx> HasTyCtxt<'tcx> for &'a ConstPropagator<'a, 'b, 'tcx> { + #[inline] + fn tcx<'c>(&'c self) -> TyCtxt<'c, 'tcx, 'tcx> { + self.tcx + } +} + +impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { + fn new( + mir: &'b Mir<'tcx>, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + source: MirSource, + ) -> ConstPropagator<'b, 'a, 'tcx> { + let param_env = tcx.param_env(source.def_id); + ConstPropagator { + mir, + tcx, + source, + param_env, + can_const_prop: CanConstProp::check(mir), + places: IndexVec::from_elem(None, &mir.local_decls), + } + } + + fn const_eval(&self, cid: GlobalId<'tcx>, span: Span) -> Option> { + let value = match self.tcx.const_eval(self.param_env.and(cid)) { + Ok(val) => val, + Err(err) => { + err.report(self.tcx, err.span, "constant propagated"); + return None; + }, + }; + let val = match value.val { + ConstVal::Value(v) => v, + _ => bug!("eval produced: {:?}", value), + }; + let val = (val, value.ty, span); + trace!("evaluated {:?} to {:?}", cid, val); + Some(val) + } + + fn eval_constant(&mut self, c: &Constant<'tcx>) -> Option> { + match c.literal { + Literal::Value { value } => match value.val { + ConstVal::Value(v) => Some((v, value.ty, c.span)), + ConstVal::Unevaluated(did, substs) => { + let instance = Instance::resolve( + self.tcx, + self.param_env, + did, + substs, + )?; + let cid = GlobalId { + instance, + promoted: None, + }; + self.const_eval(cid, c.span) + }, + }, + // 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), + }; + // cannot use `const_eval` here, because that would require having the MIR + // for the current function available, but we're producing said MIR right now + let (value, _, ty) = eval_body_with_mir(self.tcx, cid, self.mir, self.param_env)?; + let val = (value, ty, c.span); + trace!("evaluated {:?} to {:?}", c, val); + Some(val) + } + } + } + + fn eval_place(&mut self, place: &Place<'tcx>) -> Option> { + match *place { + Place::Local(loc) => self.places[loc].clone(), + Place::Projection(ref proj) => match proj.elem { + ProjectionElem::Field(field, _) => { + trace!("field proj on {:?}", proj.base); + let (base, ty, span) = self.eval_place(&proj.base)?; + match base { + Value::ByValPair(a, b) => { + trace!("by val pair: {:?}, {:?}", a, b); + let base_layout = self.tcx.layout_of(self.param_env.and(ty)).ok()?; + trace!("layout computed"); + use rustc_data_structures::indexed_vec::Idx; + let field_index = field.index(); + let val = [a, b][field_index]; + let field = base_layout.field(&*self, field_index).ok()?; + trace!("projection resulted in: {:?}", val); + Some((Value::ByVal(val), field.ty, span)) + }, + _ => None, + } + }, + _ => None, + }, + _ => None, + } + } + + fn eval_operand(&mut self, op: &Operand<'tcx>) -> Option> { + match *op { + Operand::Constant(ref c) => self.eval_constant(c), + Operand::Move(ref place) | Operand::Copy(ref place) => self.eval_place(place), + } + } + + fn const_prop( + &mut self, + rvalue: &Rvalue<'tcx>, + place_ty: ty::Ty<'tcx>, + source_info: SourceInfo, + ) -> Option> { + let span = source_info.span; + match *rvalue { + // No need to overwrite an already evaluated constant + Rvalue::Use(Operand::Constant(box Constant { + literal: Literal::Value { + value: &ty::Const { + val: ConstVal::Value(_), + .. + }, + }, + .. + })) => None, + // This branch exists for the sanity type check + Rvalue::Use(Operand::Constant(ref c)) => { + assert_eq!(c.ty, place_ty); + self.eval_constant(c) + }, + Rvalue::Use(ref op) => { + self.eval_operand(op) + }, + Rvalue::Repeat(..) | + Rvalue::Ref(..) | + Rvalue::Cast(..) | + Rvalue::Aggregate(..) | + Rvalue::NullaryOp(NullOp::Box, _) | + Rvalue::Discriminant(..) => None, + // FIXME(oli-obk): evaluate static/constant slice lengths + Rvalue::Len(_) => None, + Rvalue::NullaryOp(NullOp::SizeOf, ty) => { + let param_env = self.tcx.param_env(self.source.def_id); + type_size_of(self.tcx, param_env, ty).map(|n| ( + Value::ByVal(PrimVal::Bytes(n as u128)), + self.tcx.types.usize, + span, + )) + } + Rvalue::UnaryOp(op, ref arg) => { + let def_id = if self.tcx.is_closure(self.source.def_id) { + self.tcx.closure_base_def_id(self.source.def_id) + } else { + self.source.def_id + }; + let generics = self.tcx.generics_of(def_id); + if generics.parent_types as usize + generics.types.len() > 0 { + // FIXME: can't handle code with generics + return None; + } + let substs = Substs::identity_for_item(self.tcx, self.source.def_id); + let instance = Instance::new(self.source.def_id, substs); + let ecx = mk_borrowck_eval_cx(self.tcx, instance, self.mir, span).unwrap(); + + let val = self.eval_operand(arg)?; + let prim = ecx.value_to_primval(ValTy { value: val.0, ty: val.1 }).ok()?; + match ecx.unary_op(op, prim, val.1) { + Ok(val) => Some((Value::ByVal(val), place_ty, span)), + Err(mut err) => { + ecx.report(&mut err, false, Some(span)); + None + }, + } + } + Rvalue::CheckedBinaryOp(op, ref left, ref right) | + Rvalue::BinaryOp(op, ref left, ref right) => { + trace!("rvalue binop {:?} for {:?} and {:?}", op, left, right); + let right = self.eval_operand(right)?; + let def_id = if self.tcx.is_closure(self.source.def_id) { + self.tcx.closure_base_def_id(self.source.def_id) + } else { + self.source.def_id + }; + let generics = self.tcx.generics_of(def_id); + let has_generics = generics.parent_types as usize + generics.types.len() > 0; + if has_generics { + // FIXME: can't handle code with generics + return None; + } + let substs = Substs::identity_for_item(self.tcx, self.source.def_id); + let instance = Instance::new(self.source.def_id, substs); + let ecx = mk_borrowck_eval_cx(self.tcx, instance, self.mir, span).unwrap(); + + let r = ecx.value_to_primval(ValTy { value: right.0, ty: right.1 }).ok()?; + if op == BinOp::Shr || op == BinOp::Shl { + let param_env = self.tcx.param_env(self.source.def_id); + let left_ty = left.ty(self.mir, self.tcx); + let bits = self.tcx.layout_of(param_env.and(left_ty)).unwrap().size.bits(); + if r.to_bytes().ok().map_or(false, |b| b >= bits as u128) { + let scope_info = match self.mir.visibility_scope_info { + ClearCrossCrate::Set(ref data) => data, + ClearCrossCrate::Clear => return None, + }; + let dir = if op == BinOp::Shr { + "right" + } else { + "left" + }; + let node_id = scope_info[source_info.scope].lint_root; + self.tcx.lint_node( + ::rustc::lint::builtin::EXCEEDING_BITSHIFTS, + node_id, + span, + &format!("attempt to shift {} with overflow", dir)); + return None; + } + } + let left = self.eval_operand(left)?; + let l = ecx.value_to_primval(ValTy { value: left.0, ty: left.1 }).ok()?; + trace!("const evaluating {:?} for {:?} and {:?}", op, left, right); + match ecx.binary_op(op, l, left.1, r, right.1) { + Ok((val, overflow)) => { + let val = if let Rvalue::CheckedBinaryOp(..) = *rvalue { + Value::ByValPair( + val, + PrimVal::from_bool(overflow), + ) + } else { + if overflow { + use rustc::mir::interpret::EvalErrorKind; + let mut err = EvalErrorKind::OverflowingMath.into(); + ecx.report(&mut err, false, Some(span)); + return None; + } + Value::ByVal(val) + }; + Some((val, place_ty, span)) + }, + Err(mut err) => { + ecx.report(&mut err, false, Some(span)); + None + }, + } + }, + } + } +} + +fn type_size_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty: ty::Ty<'tcx>) -> Option { + tcx.layout_of(param_env.and(ty)).ok().map(|layout| layout.size.bytes()) +} + +struct CanConstProp { + can_const_prop: IndexVec, + // false at the beginning, once set, there are not allowed to be any more assignments + found_assignment: IndexVec, +} + +impl CanConstProp { + /// returns true if `local` can be propagated + fn check(mir: &Mir) -> IndexVec { + let mut cpv = CanConstProp { + can_const_prop: IndexVec::from_elem(true, &mir.local_decls), + found_assignment: IndexVec::from_elem(false, &mir.local_decls), + }; + for (local, val) in cpv.can_const_prop.iter_enumerated_mut() { + // cannot use args at all + // cannot use locals because if x < y { y - x } else { x - y } would + // lint for x != y + // FIXME(oli-obk): lint variables until they are used in a condition + // FIXME(oli-obk): lint if return value is constant + *val = mir.local_kind(local) == LocalKind::Temp; + } + cpv.visit_mir(mir); + cpv.can_const_prop + } +} + +impl<'tcx> Visitor<'tcx> for CanConstProp { + fn visit_local( + &mut self, + &local: &Local, + context: PlaceContext<'tcx>, + _: Location, + ) { + use rustc::mir::visit::PlaceContext::*; + match context { + // Constants must have at most one write + // FIXME(oli-obk): we could be more powerful here, if the multiple writes + // only occur in independent execution paths + Store => if self.found_assignment[local] { + self.can_const_prop[local] = false; + } else { + self.found_assignment[local] = true + }, + // Reading constants is allowed an arbitrary number of times + Copy | Move | + StorageDead | StorageLive | + Validate | + Projection(_) | + Inspect => {}, + _ => self.can_const_prop[local] = false, + } + } +} + +impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> { + fn visit_constant( + &mut self, + constant: &Constant<'tcx>, + location: Location, + ) { + trace!("visit_constant: {:?}", constant); + self.super_constant(constant, location); + self.eval_constant(constant); + } + + fn visit_statement( + &mut self, + block: BasicBlock, + statement: &Statement<'tcx>, + location: Location, + ) { + trace!("visit_statement: {:?}", statement); + if let StatementKind::Assign(ref place, ref rval) = statement.kind { + let place_ty = place + .ty(&self.mir.local_decls, self.tcx) + .to_ty(self.tcx); + if let Some(value) = self.const_prop(rval, place_ty, statement.source_info) { + if let Place::Local(local) = *place { + trace!("checking whether {:?} can be stored to {:?}", value, local); + if self.can_const_prop[local] { + trace!("storing {:?} to {:?}", value, local); + assert!(self.places[local].is_none()); + self.places[local] = Some(value); + } + } + } + } + self.super_statement(block, statement, location); + } + + fn visit_terminator_kind( + &mut self, + block: BasicBlock, + kind: &TerminatorKind<'tcx>, + location: Location, + ) { + self.super_terminator_kind(block, kind, location); + if let TerminatorKind::Assert { expected, msg, cond, .. } = kind { + if let Some(value) = self.eval_operand(cond) { + trace!("assertion on {:?} should be {:?}", value, expected); + if Value::ByVal(PrimVal::from_bool(*expected)) != value.0 { + // poison all places this operand references so that further code + // doesn't use the invalid value + match cond { + Operand::Move(ref place) | Operand::Copy(ref place) => { + let mut place = place; + while let Place::Projection(ref proj) = *place { + place = &proj.base; + } + if let Place::Local(local) = *place { + self.places[local] = None; + } + }, + Operand::Constant(_) => {} + } + let span = self.mir[block] + .terminator + .as_ref() + .unwrap() + .source_info + .span; + let node_id = self + .tcx + .hir + .as_local_node_id(self.source.def_id) + .expect("some part of a failing const eval must be local"); + use rustc::mir::AssertMessage::*; + let msg = match msg { + // Need proper const propagator for these + GeneratorResumedAfterReturn | + GeneratorResumedAfterPanic => return, + Math(ref err) => err.description().to_owned(), + BoundsCheck { ref len, ref index } => { + let len = self.eval_operand(len).expect("len must be const"); + let len = match len.0 { + Value::ByVal(PrimVal::Bytes(n)) => n, + _ => bug!("const len not primitive: {:?}", len), + }; + let index = self + .eval_operand(index) + .expect("index must be const"); + let index = match index.0 { + Value::ByVal(PrimVal::Bytes(n)) => n, + _ => bug!("const index not primitive: {:?}", index), + }; + format!( + "index out of bounds: \ + the len is {} but the index is {}", + len, + index, + ) + }, + }; + self.tcx.lint_node( + ::rustc::lint::builtin::CONST_ERR, + node_id, + span, + &msg, + ); + } + } + } + } +} diff --git a/src/librustc_mir/transform/elaborate_drops.rs b/src/librustc_mir/transform/elaborate_drops.rs index 9eca343cb5e..2e8dd623d74 100644 --- a/src/librustc_mir/transform/elaborate_drops.rs +++ b/src/librustc_mir/transform/elaborate_drops.rs @@ -19,6 +19,7 @@ use rustc::hir; use rustc::ty::{self, TyCtxt}; use rustc::mir::*; use rustc::middle::const_val::ConstVal; +use rustc::mir::interpret::{Value, PrimVal}; use rustc::util::nodemap::FxHashMap; use rustc_data_structures::indexed_set::IdxSetBuf; use rustc_data_structures::indexed_vec::Idx; @@ -541,7 +542,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { ty: self.tcx.types.bool, literal: Literal::Value { value: self.tcx.mk_const(ty::Const { - val: ConstVal::Bool(val), + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(val as u128))), ty: self.tcx.types.bool }) } diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index 04ebaa031fe..0ff73569433 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -68,7 +68,6 @@ use rustc::ty::{self, TyCtxt, AdtDef, Ty, GeneratorInterior}; use rustc::ty::subst::Substs; use util::dump_mir; use util::liveness::{self, LivenessMode}; -use rustc_const_math::ConstInt; use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::indexed_set::IdxSetBuf; use std::collections::HashMap; @@ -80,6 +79,7 @@ use transform::simplify; use transform::no_landing_pads::no_landing_pads; use dataflow::{do_dataflow, DebugFormatted, state_for_location}; use dataflow::{MaybeStorageLive, HaveBeenBorrowedLocals}; +use rustc::mir::interpret::{Value, PrimVal}; pub struct StateTransform; @@ -181,7 +181,7 @@ impl<'a, 'tcx> TransformVisitor<'a, 'tcx> { ty: self.tcx.types.u32, literal: Literal::Value { value: self.tcx.mk_const(ty::Const { - val: ConstVal::Integral(ConstInt::U32(state_disc)), + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(state_disc.into()))), ty: self.tcx.types.u32 }), }, @@ -534,7 +534,7 @@ fn insert_switch<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let switch = TerminatorKind::SwitchInt { discr: Operand::Copy(transform.make_field(transform.state_field, tcx.types.u32)), switch_ty: tcx.types.u32, - values: Cow::from(cases.iter().map(|&(i, _)| ConstInt::U32(i)).collect::>()), + values: Cow::from(cases.iter().map(|&(i, _)| i.into()).collect::>()), targets: cases.iter().map(|&(_, d)| d).chain(once(default_block)).collect(), }; @@ -698,7 +698,7 @@ fn insert_panic_block<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: tcx.types.bool, literal: Literal::Value { value: tcx.mk_const(ty::Const { - val: ConstVal::Bool(false), + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(0))), ty: tcx.types.bool }), }, diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs index 64c702b99cd..b8a0e0f8907 100644 --- a/src/librustc_mir/transform/inline.rs +++ b/src/librustc_mir/transform/inline.rs @@ -207,6 +207,13 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { return false; } + // Do not inline {u,i}128 lang items, trans const eval depends + // on detecting calls to these lang items and intercepting them + if tcx.is_binop_lang_item(callsite.callee).is_some() { + debug!(" not inlining 128bit integer lang item"); + return false; + } + let trans_fn_attrs = tcx.trans_fn_attrs(callsite.callee); let hinted = match trans_fn_attrs.inline { diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 9d1f7631375..81b740c917b 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -41,6 +41,7 @@ pub mod dump_mir; pub mod deaggregator; pub mod instcombine; pub mod copy_prop; +pub mod const_prop; pub mod generator; pub mod inline; pub mod lower_128bit; @@ -265,6 +266,8 @@ fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx generator::StateTransform, instcombine::InstCombine, + const_prop::ConstProp, + simplify_branches::SimplifyBranches::new("after-const-prop"), deaggregator::Deaggregator, copy_prop::CopyPropagation, remove_noop_landing_pads::RemoveNoopLandingPads, diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 8f5831270d6..88618122e4f 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -690,7 +690,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { _ => false } } else if let ty::TyArray(_, len) = ty.sty { - len.val.to_const_int().unwrap().to_u64().unwrap() == 0 && + len.val.unwrap_u64() == 0 && self.mode == Mode::Fn } else { false diff --git a/src/librustc_mir/transform/simplify_branches.rs b/src/librustc_mir/transform/simplify_branches.rs index 41089f567bd..9dd48952208 100644 --- a/src/librustc_mir/transform/simplify_branches.rs +++ b/src/librustc_mir/transform/simplify_branches.rs @@ -13,6 +13,7 @@ use rustc::ty::{self, TyCtxt}; use rustc::middle::const_val::ConstVal; use rustc::mir::*; +use rustc::mir::interpret::{Value, PrimVal}; use transform::{MirPass, MirSource}; use std::borrow::Cow; @@ -40,10 +41,10 @@ impl MirPass for SimplifyBranches { TerminatorKind::SwitchInt { discr: Operand::Constant(box Constant { literal: Literal::Value { ref value }, .. }), ref values, ref targets, .. } => { - if let Some(ref constint) = value.val.to_const_int() { + if let Some(constint) = value.val.to_raw_bits() { let (otherwise, targets) = targets.split_last().unwrap(); let mut ret = TerminatorKind::Goto { target: *otherwise }; - for (v, t) in values.iter().zip(targets.iter()) { + for (&v, t) in values.iter().zip(targets.iter()) { if v == constint { ret = TerminatorKind::Goto { target: *t }; break; @@ -56,9 +57,12 @@ impl MirPass for SimplifyBranches { }, TerminatorKind::Assert { target, cond: Operand::Constant(box Constant { literal: Literal::Value { - value: &ty::Const { val: ConstVal::Bool(cond), .. } + value: &ty::Const { + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(cond))), + .. } }, .. - }), expected, .. } if cond == expected => { + }), expected, .. } if (cond == 1) == expected => { + assert!(cond <= 1); TerminatorKind::Goto { target: target } }, TerminatorKind::FalseEdges { real_target, .. } => { diff --git a/src/librustc_mir/transform/uniform_array_move_out.rs b/src/librustc_mir/transform/uniform_array_move_out.rs index e46de170479..9cc3ffb3063 100644 --- a/src/librustc_mir/transform/uniform_array_move_out.rs +++ b/src/librustc_mir/transform/uniform_array_move_out.rs @@ -81,8 +81,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UniformArrayMoveOutVisitor<'a, 'tcx> { } else { let place_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx); if let ty::TyArray(item_ty, const_size) = place_ty.sty { - if let Some(size) = const_size.val.to_const_int().and_then(|v| v.to_u64()) { - assert!(size <= (u32::max_value() as u64), + if let Some(size) = const_size.val.to_raw_bits() { + assert!(size <= (u32::max_value() as u128), "unform array move out doesn't supported for array bigger then u32"); self.uniform(location, dst_place, proj, item_ty, size as u32); @@ -203,7 +203,7 @@ impl MirPass for RestoreSubsliceArrayMoveOut { let opt_size = opt_src_place.and_then(|src_place| { let src_ty = src_place.ty(mir, tcx).to_ty(tcx); if let ty::TyArray(_, ref size_o) = src_ty.sty { - size_o.val.to_const_int().and_then(|v| v.to_u64()) + size_o.val.to_raw_bits().map(|n| n as u64) } else { None } diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs index e2feb0ed390..458dd488409 100644 --- a/src/librustc_mir/util/elaborate_drops.rs +++ b/src/librustc_mir/util/elaborate_drops.rs @@ -11,13 +11,14 @@ use std::fmt; use rustc::hir; use rustc::mir::*; -use rustc::middle::const_val::{ConstInt, ConstVal}; +use rustc::middle::const_val::ConstVal; use rustc::middle::lang_items; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::subst::{Kind, Substs}; use rustc::ty::util::IntTypeExt; use rustc_data_structures::indexed_vec::Idx; use util::patch::MirPatch; +use rustc::mir::interpret::{Value, PrimVal}; use std::{iter, u32}; @@ -425,7 +426,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> variant_path, &adt.variants[variant_index], substs); - values.push(discr); + values.push(discr.val); if let Unwind::To(unwind) = unwind { // We can't use the half-ladder from the original // drop ladder, because this breaks the @@ -480,7 +481,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> fn adt_switch_block(&mut self, adt: &'tcx ty::AdtDef, blocks: Vec, - values: &[ConstInt], + values: &[u128], succ: BasicBlock, unwind: Unwind) -> BasicBlock { @@ -803,7 +804,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> self.complete_drop(Some(DropFlagMode::Deep), succ, unwind) } ty::TyArray(ety, size) => self.open_drop_for_array( - ety, size.val.to_const_int().and_then(|v| v.to_u64())), + ety, size.val.to_raw_bits().map(|i| i as u64)), ty::TySlice(ety) => self.open_drop_for_array(ety, None), _ => bug!("open drop from non-ADT `{:?}`", ty) @@ -949,7 +950,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> ty: self.tcx().types.usize, literal: Literal::Value { value: self.tcx().mk_const(ty::Const { - val: ConstVal::Integral(self.tcx().const_usize(val)), + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(val.into()))), ty: self.tcx().types.usize }) } diff --git a/src/librustc_passes/Cargo.toml b/src/librustc_passes/Cargo.toml index e87e976eac3..4bab24ae139 100644 --- a/src/librustc_passes/Cargo.toml +++ b/src/librustc_passes/Cargo.toml @@ -11,7 +11,7 @@ crate-type = ["dylib"] [dependencies] log = "0.4" rustc = { path = "../librustc" } -rustc_const_eval = { path = "../librustc_const_eval" } +rustc_mir = { path = "../librustc_mir"} rustc_const_math = { path = "../librustc_const_math" } rustc_data_structures = { path = "../librustc_data_structures" } syntax = { path = "../libsyntax" } diff --git a/src/librustc_passes/diagnostics.rs b/src/librustc_passes/diagnostics.rs index 184fab778c6..7a54fc72d53 100644 --- a/src/librustc_passes/diagnostics.rs +++ b/src/librustc_passes/diagnostics.rs @@ -31,23 +31,6 @@ const FOO2: i32 = { 0 }; // but brackets are useless here ``` "##, */ -E0030: r##" -When matching against a range, the compiler verifies that the range is -non-empty. Range patterns include both end-points, so this is equivalent to -requiring the start of the range to be less than or equal to the end of the -range. - -For example: - -```compile_fail -match 5u32 { - // This range is ok, albeit pointless. - 1 ... 1 => {} - // This range is empty, and the compiler can tell. - 1000 ... 5 => {} -} -``` -"##, E0130: r##" You declared a pattern as an argument in a foreign function declaration. @@ -228,24 +211,6 @@ impl Foo for Bar { "##, -E0579: r##" -When matching against an exclusive range, the compiler verifies that the range -is non-empty. Exclusive range patterns include the start point but not the end -point, so this is equivalent to requiring the start of the range to be less -than the end of the range. - -For example: - -```compile_fail -match 5u32 { - // This range is ok, albeit pointless. - 1 .. 2 => {} - // This range is empty, and the compiler can tell. - 5 .. 5 => {} -} -``` -"##, - E0590: r##" `break` or `continue` must include a label when used in the condition of a `while` loop. diff --git a/src/librustc_passes/lib.rs b/src/librustc_passes/lib.rs index 8bd95982887..1f6cc1f71fc 100644 --- a/src/librustc_passes/lib.rs +++ b/src/librustc_passes/lib.rs @@ -23,7 +23,7 @@ #[macro_use] extern crate rustc; -extern crate rustc_const_eval; +extern crate rustc_mir; extern crate rustc_const_math; extern crate rustc_data_structures; @@ -39,7 +39,7 @@ use rustc::ty::maps::Providers; mod diagnostics; pub mod ast_validation; -pub mod consts; +pub mod rvalue_promotion; pub mod hir_stats; pub mod loops; mod mir_stats; @@ -47,5 +47,5 @@ mod mir_stats; __build_diagnostic_array! { librustc_passes, DIAGNOSTICS } pub fn provide(providers: &mut Providers) { - consts::provide(providers); + rvalue_promotion::provide(providers); } diff --git a/src/librustc_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs index e4705674e22..4a4ce63cc1d 100644 --- a/src/librustc_passes/mir_stats.rs +++ b/src/librustc_passes/mir_stats.rs @@ -12,7 +12,6 @@ // pieces of MIR. The resulting numbers are good approximations but not // completely accurate (some things might be counted twice, others missed). -use rustc_const_math::{ConstUsize}; use rustc::mir::{AggregateKind, AssertMessage, BasicBlock, BasicBlockData}; use rustc::mir::{Constant, Literal, Location, Local, LocalDecl}; use rustc::mir::{Place, PlaceElem, PlaceProjection}; @@ -265,13 +264,6 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> { self.super_const(constant); } - fn visit_const_usize(&mut self, - const_usize: &ConstUsize, - _: Location) { - self.record("ConstUsize", const_usize); - self.super_const_usize(const_usize); - } - fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) { diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/rvalue_promotion.rs similarity index 77% rename from src/librustc_passes/consts.rs rename to src/librustc_passes/rvalue_promotion.rs index b93b759fdf8..16278c37a0c 100644 --- a/src/librustc_passes/consts.rs +++ b/src/librustc_passes/rvalue_promotion.rs @@ -25,12 +25,6 @@ // by borrowck::gather_loans use rustc::ty::cast::CastKind; -use rustc_const_eval::ConstContext; -use rustc::middle::const_val::ConstEvalErr; -use rustc::middle::const_val::ErrKind::{IndexOpFeatureGated, UnimplementedConstVal, MiscCatchAll}; -use rustc::middle::const_val::ErrKind::{ErroneousReferencedConstant, MiscBinaryOp, NonConstPath}; -use rustc::middle::const_val::ErrKind::{TypeckError, Math, LayoutError}; -use rustc_const_math::{ConstMathErr, Op}; use rustc::hir::def::{Def, CtorKind}; use rustc::hir::def_id::DefId; use rustc::hir::map::blocks::FnLikeNode; @@ -38,20 +32,16 @@ use rustc::middle::expr_use_visitor as euv; use rustc::middle::mem_categorization as mc; use rustc::middle::mem_categorization::Categorization; use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::maps::{queries, Providers}; +use rustc::ty::maps::Providers; use rustc::ty::subst::Substs; use rustc::traits::Reveal; -use rustc::util::common::ErrorReported; use rustc::util::nodemap::{ItemLocalSet, NodeSet}; -use rustc::lint::builtin::CONST_ERR; -use rustc::hir::{self, PatKind, RangeEnd}; +use rustc::hir; use rustc_data_structures::sync::Lrc; use syntax::ast; use syntax_pos::{Span, DUMMY_SP}; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; -use std::cmp::Ordering; - pub fn provide(providers: &mut Providers) { *providers = Providers { rvalue_promotable_map, @@ -124,29 +114,6 @@ struct CheckCrateVisitor<'a, 'tcx: 'a> { } impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> { - fn const_cx(&self) -> ConstContext<'a, 'gcx> { - ConstContext::new(self.tcx, self.param_env.and(self.identity_substs), self.tables) - } - - fn check_const_eval(&self, expr: &'gcx hir::Expr) { - if let Err(err) = self.const_cx().eval(expr) { - match err.kind { - UnimplementedConstVal(_) => {} - IndexOpFeatureGated => {} - ErroneousReferencedConstant(_) => {} - TypeckError => {} - MiscCatchAll => {} - _ => { - self.tcx.lint_node(CONST_ERR, - expr.id, - expr.span, - &format!("constant evaluation error: {}", - err.description().into_oneline())); - } - } - } - } - // Returns true iff all the values of the type are promotable. fn type_has_only_promotable_values(&mut self, ty: Ty<'gcx>) -> bool { ty.is_freeze(self.tcx, self.param_env, DUMMY_SP) && @@ -196,9 +163,6 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> { self.identity_substs = Substs::identity_for_item(self.tcx, item_def_id); let body = self.tcx.hir.body(body_id); - if !self.in_fn { - self.check_const_eval(&body.value); - } let tcx = self.tcx; let param_env = self.param_env; @@ -214,52 +178,6 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> { self.identity_substs = outer_identity_substs; } - fn visit_pat(&mut self, p: &'tcx hir::Pat) { - match p.node { - PatKind::Lit(ref lit) => { - self.check_const_eval(lit); - } - PatKind::Range(ref start, ref end, RangeEnd::Excluded) => { - match self.const_cx().compare_lit_exprs(p.span, start, end) { - Ok(Ordering::Less) => {} - Ok(Ordering::Equal) | - Ok(Ordering::Greater) => { - span_err!(self.tcx.sess, - start.span, - E0579, - "lower range bound must be less than upper"); - } - Err(ErrorReported) => {} - } - } - PatKind::Range(ref start, ref end, RangeEnd::Included) => { - match self.const_cx().compare_lit_exprs(p.span, start, end) { - Ok(Ordering::Less) | - Ok(Ordering::Equal) => {} - Ok(Ordering::Greater) => { - let mut err = struct_span_err!( - self.tcx.sess, - start.span, - E0030, - "lower range bound must be less than or equal to upper" - ); - err.span_label(start.span, "lower bound larger than upper bound"); - if self.tcx.sess.teach(&err.get_code().unwrap()) { - err.note("When matching against a range, the compiler verifies that \ - the range is non-empty. Range patterns include both \ - end-points, so this is equivalent to requiring the start of \ - the range to be less than or equal to the end of the range."); - } - err.emit(); - } - Err(ErrorReported) => {} - } - } - _ => {} - } - intravisit::walk_pat(self, p); - } - fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt) { match stmt.node { hir::StmtDecl(ref decl, _) => { @@ -308,30 +226,6 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> { self.promotable = false; } - if self.in_fn && self.promotable { - match self.const_cx().eval(ex) { - Ok(_) => {} - Err(ConstEvalErr { kind: UnimplementedConstVal(_), .. }) | - Err(ConstEvalErr { kind: MiscCatchAll, .. }) | - Err(ConstEvalErr { kind: MiscBinaryOp, .. }) | - Err(ConstEvalErr { kind: NonConstPath, .. }) | - Err(ConstEvalErr { kind: ErroneousReferencedConstant(_), .. }) | - Err(ConstEvalErr { kind: Math(ConstMathErr::Overflow(Op::Shr)), .. }) | - Err(ConstEvalErr { kind: Math(ConstMathErr::Overflow(Op::Shl)), .. }) | - Err(ConstEvalErr { kind: IndexOpFeatureGated, .. }) => {} - Err(ConstEvalErr { kind: TypeckError, .. }) => {} - Err(ConstEvalErr { - kind: LayoutError(ty::layout::LayoutError::Unknown(_)), .. - }) => {} - Err(msg) => { - self.tcx.lint_node(CONST_ERR, - ex.id, - msg.span, - &msg.description().into_oneline().into_owned()); - } - } - } - if self.promotable { self.result.insert(ex.hir_id.local_id); } @@ -397,7 +291,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node // References to a static that are themselves within a static // are inherently promotable with the exception - // of "#[thread_loca]" statics, which may not + // of "#[thread_local]" statics, which may not // outlive the current function Def::Static(did, _) => { @@ -431,16 +325,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node // Don't peek inside trait associated constants. false } else { - queries::const_is_rvalue_promotable_to_static::try_get(v.tcx, e.span, did) - .unwrap_or_else(|mut err| { - // A cycle between constants ought to be reported elsewhere. - err.cancel(); - v.tcx.sess.delay_span_bug( - e.span, - &format!("cycle encountered during const qualification: {:?}", - did)); - false - }) + v.tcx.at(e.span).const_is_rvalue_promotable_to_static(did) }; // Just in case the type is more specific than the definition, diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 3708f6f6ec4..49a5b7ac8b9 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -195,7 +195,7 @@ pub fn unsized_info<'cx, 'tcx>(cx: &CodegenCx<'cx, 'tcx>, let (source, target) = cx.tcx.struct_lockstep_tails(source, target); match (&source.sty, &target.sty) { (&ty::TyArray(_, len), &ty::TySlice(_)) => { - C_usize(cx, len.val.to_const_int().unwrap().to_u64().unwrap()) + C_usize(cx, len.val.unwrap_u64()) } (&ty::TyDynamic(..), &ty::TyDynamic(..)) => { // For now, upcasts are limited to changes in marker @@ -334,14 +334,6 @@ pub fn cast_shift_expr_rhs( cast_shift_rhs(op, lhs, rhs, |a, b| cx.trunc(a, b), |a, b| cx.zext(a, b)) } -pub fn cast_shift_const_rhs(op: hir::BinOp_, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - cast_shift_rhs(op, - lhs, - rhs, - |a, b| unsafe { llvm::LLVMConstTrunc(a, b.to_ref()) }, - |a, b| unsafe { llvm::LLVMConstZExt(a, b.to_ref()) }) -} - fn cast_shift_rhs(op: hir::BinOp_, lhs: ValueRef, rhs: ValueRef, @@ -980,6 +972,8 @@ fn collect_and_partition_translation_items<'a, 'tcx>( collector::collect_crate_mono_items(tcx, collection_mode) }); + tcx.sess.abort_if_errors(); + ::rustc_mir::monomorphize::assert_symbols_are_distinct(tcx, items.iter()); let strategy = if tcx.sess.opts.incremental.is_some() { diff --git a/src/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/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs index 2c430d03c96..d20b51ca0fd 100644 --- a/src/librustc_trans/debuginfo/metadata.rs +++ b/src/librustc_trans/debuginfo/metadata.rs @@ -276,7 +276,7 @@ fn fixed_vec_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, let upper_bound = match array_or_slice_type.sty { ty::TyArray(_, len) => { - len.val.to_const_int().unwrap().to_u64().unwrap() as c_longlong + len.val.unwrap_u64() as c_longlong } _ => -1 }; @@ -1378,7 +1378,7 @@ fn prepare_enum_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, DIB(cx), name.as_ptr(), // FIXME: what if enumeration has i128 discriminant? - discr.to_u128_unchecked() as u64) + discr.val as u64) } }) .collect(); diff --git a/src/librustc_trans/debuginfo/type_names.rs b/src/librustc_trans/debuginfo/type_names.rs index 6490d109f29..a88eb9ae354 100644 --- a/src/librustc_trans/debuginfo/type_names.rs +++ b/src/librustc_trans/debuginfo/type_names.rs @@ -97,7 +97,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, ty::TyArray(inner_type, len) => { output.push('['); push_debuginfo_type_name(cx, inner_type, true, output); - output.push_str(&format!("; {}", len.val.to_const_int().unwrap().to_u64().unwrap())); + output.push_str(&format!("; {}", len.val.unwrap_u64())); output.push(']'); }, ty::TySlice(inner_type) => { diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs index f683703ce6d..dd34dc03458 100644 --- a/src/librustc_trans/mir/analyze.rs +++ b/src/librustc_trans/mir/analyze.rs @@ -13,8 +13,7 @@ use rustc_data_structures::bitvec::BitVector; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; -use rustc::middle::const_val::ConstVal; -use rustc::mir::{self, Location, TerminatorKind, Literal}; +use rustc::mir::{self, Location, TerminatorKind}; use rustc::mir::visit::{Visitor, PlaceContext}; use rustc::mir::traversal; use rustc::ty; @@ -109,15 +108,18 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> { block: mir::BasicBlock, kind: &mir::TerminatorKind<'tcx>, location: Location) { - match *kind { + let check = match *kind { mir::TerminatorKind::Call { - func: mir::Operand::Constant(box mir::Constant { - literal: Literal::Value { - value: &ty::Const { val: ConstVal::Function(def_id, _), .. }, .. - }, .. - }), + func: mir::Operand::Constant(ref c), ref args, .. - } if Some(def_id) == self.fx.cx.tcx.lang_items().box_free_fn() => { + } => match c.ty.sty { + ty::TyFnDef(did, _) => Some((did, args)), + _ => None, + }, + _ => None, + }; + if let Some((def_id, args)) = check { + if Some(def_id) == self.fx.cx.tcx.lang_items().box_free_fn() { // box_free(x) shares with `drop x` the property that it // is not guaranteed to be statically dominated by the // definition of x, so x must always be in an alloca. @@ -125,7 +127,6 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> { self.visit_place(place, PlaceContext::Drop, location); } } - _ => {} } self.super_terminator_kind(block, kind, location); diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index bb2a7840fae..efb5338f680 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, ConstInt, 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}; @@ -196,19 +194,21 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { if switch_ty == bx.tcx().types.bool { let lltrue = llblock(self, targets[0]); let llfalse = llblock(self, targets[1]); - if let [ConstInt::U8(0)] = values[..] { + if let [0] = values[..] { bx.cond_br(discr.immediate(), llfalse, lltrue); } else { + assert_eq!(&values[..], &[1]); bx.cond_br(discr.immediate(), lltrue, llfalse); } } else { let (otherwise, targets) = targets.split_last().unwrap(); let switch = bx.switch(discr.immediate(), llblock(self, *otherwise), values.len()); - for (value, target) in values.iter().zip(targets) { - let val = Const::from_constint(bx.cx, value); + let switch_llty = bx.cx.layout_of(switch_ty).immediate_llvm_type(bx.cx); + for (&value, target) in values.iter().zip(targets) { + let llval = C_uint_big(switch_llty, value); let llbb = llblock(self, *target); - bx.add_case(switch, val.llval, llbb) + bx.add_case(switch, llval, llbb) } } } @@ -351,26 +351,18 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { .max(tcx.data_layout.pointer_align); // Put together the arguments to the panic entry point. - let (lang_item, args, const_err) = match *msg { + let (lang_item, args) = match *msg { mir::AssertMessage::BoundsCheck { ref len, ref index } => { let len = self.trans_operand(&mut bx, len).immediate(); let index = self.trans_operand(&mut bx, index).immediate(); - let const_err = common::const_to_opt_u128(len, false) - .and_then(|len| common::const_to_opt_u128(index, false) - .map(|index| ErrKind::IndexOutOfBounds { - len: len as u64, - index: index as u64 - })); - let file_line_col = C_struct(bx.cx, &[filename, line, col], false); let file_line_col = consts::addr_of(bx.cx, file_line_col, align, "panic_bounds_check_loc"); (lang_items::PanicBoundsCheckFnLangItem, - vec![file_line_col, index, len], - const_err) + vec![file_line_col, index, len]) } mir::AssertMessage::Math(ref err) => { let msg_str = Symbol::intern(err.description()).as_str(); @@ -383,8 +375,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { align, "panic_loc"); (lang_items::PanicFnLangItem, - vec![msg_file_line_col], - Some(ErrKind::Math(err.clone()))) + vec![msg_file_line_col]) } mir::AssertMessage::GeneratorResumedAfterReturn | mir::AssertMessage::GeneratorResumedAfterPanic => { @@ -403,23 +394,10 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { align, "panic_loc"); (lang_items::PanicFnLangItem, - vec![msg_file_line_col], - None) + vec![msg_file_line_col]) } }; - // If we know we always panic, and the error message - // is also constant, then we can produce a warning. - if const_cond == Some(!expected) { - if let Some(err) = const_err { - let 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); - diag.emit(); - } - } - // Obtain the panic entry point. let def_id = common::langcall(bx.tcx(), Some(span), "", lang_item); let instance = ty::Instance::mono(bx.tcx(), def_id); @@ -529,10 +507,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.simd_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 d470f92b752..6aa8b7e5449 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -9,1341 +9,229 @@ // except according to those terms. use llvm::{self, ValueRef}; -use rustc::middle::const_val::{ConstEvalErr, ConstVal, ErrKind}; -use rustc_const_math::ConstInt::*; -use rustc_const_math::{ConstInt, ConstMathErr, MAX_F32_PLUS_HALF_ULP}; +use rustc::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::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 base; -use abi::{self, Abi}; -use callee; +use rustc_data_structures::indexed_vec::Idx; +use rustc::mir::interpret::{Allocation, GlobalId, MemoryPointer, PrimVal, Value as MiriValue}; +use rustc::ty::{self, Ty}; +use rustc::ty::layout::{self, HasDataLayout, LayoutOf, Scalar}; 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::const_to_opt_u128; +use common::{CodegenCx}; +use common::{C_bytes, C_struct, C_uint_big, C_undef, C_usize}; use consts; use type_of::LayoutLlvmExt; use type_::Type; -use value::Value; +use syntax::ast::Mutability; -use syntax_pos::Span; -use syntax::ast; - -use std::fmt; -use std::ptr; - -use super::operand::{OperandRef, OperandValue}; +use super::super::callee; 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_constint(cx: &CodegenCx<'a, 'tcx>, ci: &ConstInt) -> Const<'tcx> { - let tcx = cx.tcx; - let (llval, ty) = match *ci { - I8(v) => (C_int(Type::i8(cx), v as i64), tcx.types.i8), - I16(v) => (C_int(Type::i16(cx), v as i64), tcx.types.i16), - I32(v) => (C_int(Type::i32(cx), v as i64), tcx.types.i32), - I64(v) => (C_int(Type::i64(cx), v as i64), tcx.types.i64), - I128(v) => (C_uint_big(Type::i128(cx), v as u128), tcx.types.i128), - Isize(v) => (C_int(Type::isize(cx), v.as_i64()), tcx.types.isize), - U8(v) => (C_uint(Type::i8(cx), v as u64), tcx.types.u8), - U16(v) => (C_uint(Type::i16(cx), v as u64), tcx.types.u16), - U32(v) => (C_uint(Type::i32(cx), v as u64), tcx.types.u32), - U64(v) => (C_uint(Type::i64(cx), v), tcx.types.u64), - U128(v) => (C_uint_big(Type::i128(cx), v), tcx.types.u128), - Usize(v) => (C_uint(Type::isize(cx), v.as_u64()), tcx.types.usize), - }; - Const { llval: llval, ty: ty } - } - - /// 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); - let val = match *cv { - ConstVal::Float(v) => { - let bits = match v.ty { - ast::FloatTy::F32 => C_u32(cx, v.bits as u32), - ast::FloatTy::F64 => C_u64(cx, v.bits as u64) - }; - consts::bitcast(bits, llty) +pub fn primval_to_llvm(cx: &CodegenCx, + cv: PrimVal, + scalar: &Scalar, + llty: Type) -> ValueRef { + let bits = if scalar.is_bool() { 1 } else { scalar.value.size(cx).bits() }; + match cv { + PrimVal::Undef => C_undef(Type::ix(cx, bits)), + PrimVal::Bytes(b) => { + let llval = C_uint_big(Type::ix(cx, bits), b); + if scalar.value == layout::Pointer { + unsafe { llvm::LLVMConstIntToPtr(llval, llty.to_ref()) } + } else { + consts::bitcast(llval, llty) } - ConstVal::Bool(v) => C_bool(cx, v), - ConstVal::Integral(ref i) => return Const::from_constint(cx, i), - ConstVal::Str(ref v) => C_str_slice(cx, v.clone()), - ConstVal::ByteStr(v) => { - consts::addr_of(cx, C_bytes(cx, v.data), cx.align_of(ty), "byte_str") - } - ConstVal::Char(c) => C_uint(Type::char(cx), c as u64), - ConstVal::Function(..) => C_undef(llty), - ConstVal::Variant(_) | - ConstVal::Aggregate(..) | - ConstVal::Unevaluated(..) => { - bug!("MIR must not use `{:?}` (aggregates are expanded to MIR rvalues)", cv) - } - }; - - 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.to_const_int().unwrap().to_u64().unwrap()) - } - 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 statemenets 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) - }; - - 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)) = self.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 + }, + PrimVal::Ptr(ptr) => { + if let Some(fn_instance) = cx.tcx.interpret_interner.get_fn(ptr.alloc_id) { + callee::get_fn(cx, fn_instance) + } else { + let static_ = cx + .tcx + .interpret_interner + .get_corresponding_static_def_id(ptr.alloc_id); + let base_addr = if let Some(def_id) = static_ { + assert!(cx.tcx.is_static(def_id).is_some()); + consts::get_static(cx, def_id) + } else if let Some(alloc) = cx.tcx.interpret_interner + .get_alloc(ptr.alloc_id) { + let init = global_initializer(cx, alloc); + if alloc.runtime_mutability == Mutability::Mutable { + consts::addr_of_mut(cx, init, alloc.align, "byte_str") } else { - span_bug!(span, "diverging {:?} in constant", terminator.kind); + consts::addr_of(cx, init, alloc.align, "byte_str") } - } - _ => span_bug!(span, "{:?} in constant", terminator.kind) - }; - } - } - - fn is_binop_lang_item(&mut self, def_id: DefId) -> Option<(mir::BinOp, bool)> { - let tcx = self.cx.tcx; - let items = tcx.lang_items(); - let def_id = Some(def_id); - if items.i128_add_fn() == def_id { Some((mir::BinOp::Add, false)) } - else if items.u128_add_fn() == def_id { Some((mir::BinOp::Add, false)) } - else if items.i128_sub_fn() == def_id { Some((mir::BinOp::Sub, false)) } - else if items.u128_sub_fn() == def_id { Some((mir::BinOp::Sub, false)) } - else if items.i128_mul_fn() == def_id { Some((mir::BinOp::Mul, false)) } - else if items.u128_mul_fn() == def_id { Some((mir::BinOp::Mul, false)) } - else if items.i128_div_fn() == def_id { Some((mir::BinOp::Div, false)) } - else if items.u128_div_fn() == def_id { Some((mir::BinOp::Div, false)) } - else if items.i128_rem_fn() == def_id { Some((mir::BinOp::Rem, false)) } - else if items.u128_rem_fn() == def_id { Some((mir::BinOp::Rem, false)) } - else if items.i128_shl_fn() == def_id { Some((mir::BinOp::Shl, false)) } - else if items.u128_shl_fn() == def_id { Some((mir::BinOp::Shl, false)) } - else if items.i128_shr_fn() == def_id { Some((mir::BinOp::Shr, false)) } - else if items.u128_shr_fn() == def_id { Some((mir::BinOp::Shr, false)) } - else if items.i128_addo_fn() == def_id { Some((mir::BinOp::Add, true)) } - else if items.u128_addo_fn() == def_id { Some((mir::BinOp::Add, true)) } - else if items.i128_subo_fn() == def_id { Some((mir::BinOp::Sub, true)) } - else if items.u128_subo_fn() == def_id { Some((mir::BinOp::Sub, true)) } - else if items.i128_mulo_fn() == def_id { Some((mir::BinOp::Mul, true)) } - else if items.u128_mulo_fn() == def_id { Some((mir::BinOp::Mul, true)) } - else if items.i128_shlo_fn() == def_id { Some((mir::BinOp::Shl, true)) } - else if items.u128_shlo_fn() == def_id { Some((mir::BinOp::Shl, true)) } - else if items.i128_shro_fn() == def_id { Some((mir::BinOp::Shr, true)) } - else if items.u128_shro_fn() == def_id { Some((mir::BinOp::Shr, true)) } - else { None } - } - - fn store(&mut self, - dest: &mir::Place<'tcx>, - value: Result, ConstEvalErr<'tcx>>, - 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) + bug!("missing allocation {:?}", ptr.alloc_id); }; - 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) - } - -} - -fn to_const_int(value: ValueRef, t: Ty, tcx: TyCtxt) -> Option { - match t.sty { - ty::TyInt(int_type) => const_to_opt_u128(value, true) - .and_then(|input| ConstInt::new_signed(input as i128, int_type, - tcx.sess.target.isize_ty)), - ty::TyUint(uint_type) => const_to_opt_u128(value, false) - .and_then(|input| ConstInt::new_unsigned(input, uint_type, - tcx.sess.target.usize_ty)), - _ => None - - } -} - -pub fn const_scalar_binop(op: mir::BinOp, - lhs: ValueRef, - rhs: ValueRef, - input_ty: Ty) -> ValueRef { - assert!(!input_ty.is_simd()); - let is_float = input_ty.is_fp(); - let signed = input_ty.is_signed(); - - unsafe { - match op { - mir::BinOp::Add if is_float => llvm::LLVMConstFAdd(lhs, rhs), - mir::BinOp::Add => llvm::LLVMConstAdd(lhs, rhs), - - mir::BinOp::Sub if is_float => llvm::LLVMConstFSub(lhs, rhs), - mir::BinOp::Sub => llvm::LLVMConstSub(lhs, rhs), - - mir::BinOp::Mul if is_float => llvm::LLVMConstFMul(lhs, rhs), - mir::BinOp::Mul => llvm::LLVMConstMul(lhs, rhs), - - mir::BinOp::Div if is_float => llvm::LLVMConstFDiv(lhs, rhs), - mir::BinOp::Div if signed => llvm::LLVMConstSDiv(lhs, rhs), - mir::BinOp::Div => llvm::LLVMConstUDiv(lhs, rhs), - - mir::BinOp::Rem if is_float => llvm::LLVMConstFRem(lhs, rhs), - mir::BinOp::Rem if signed => llvm::LLVMConstSRem(lhs, rhs), - mir::BinOp::Rem => llvm::LLVMConstURem(lhs, rhs), - - mir::BinOp::BitXor => llvm::LLVMConstXor(lhs, rhs), - mir::BinOp::BitAnd => llvm::LLVMConstAnd(lhs, rhs), - mir::BinOp::BitOr => llvm::LLVMConstOr(lhs, rhs), - mir::BinOp::Shl => { - let rhs = base::cast_shift_const_rhs(op.to_hir_binop(), lhs, rhs); - llvm::LLVMConstShl(lhs, rhs) - } - mir::BinOp::Shr => { - let rhs = base::cast_shift_const_rhs(op.to_hir_binop(), lhs, rhs); - if signed { llvm::LLVMConstAShr(lhs, rhs) } - else { llvm::LLVMConstLShr(lhs, rhs) } - } - mir::BinOp::Eq | mir::BinOp::Ne | - mir::BinOp::Lt | mir::BinOp::Le | - mir::BinOp::Gt | mir::BinOp::Ge => { - if is_float { - let cmp = base::bin_op_to_fcmp_predicate(op.to_hir_binop()); - llvm::LLVMConstFCmp(cmp, lhs, rhs) + let llval = unsafe { llvm::LLVMConstInBoundsGEP( + consts::bitcast(base_addr, Type::i8p(cx)), + &C_usize(cx, ptr.offset), + 1, + ) }; + if scalar.value != layout::Pointer { + unsafe { llvm::LLVMConstPtrToInt(llval, llty.to_ref()) } } else { - let cmp = base::bin_op_to_icmp_predicate(op.to_hir_binop(), - signed); - llvm::LLVMConstICmp(cmp, lhs, rhs) + consts::bitcast(llval, llty) } } - mir::BinOp::Offset => unreachable!("BinOp::Offset in const-eval!") } } } -pub fn const_scalar_checked_binop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - op: mir::BinOp, - lllhs: ValueRef, - llrhs: ValueRef, - input_ty: Ty<'tcx>) - -> Option<(ValueRef, bool)> { - if let (Some(lhs), Some(rhs)) = (to_const_int(lllhs, input_ty, tcx), - to_const_int(llrhs, input_ty, tcx)) { - let result = match op { - mir::BinOp::Add => lhs + rhs, - mir::BinOp::Sub => lhs - rhs, - mir::BinOp::Mul => lhs * rhs, - mir::BinOp::Shl => lhs << rhs, - mir::BinOp::Shr => lhs >> rhs, - _ => { - bug!("Operator `{:?}` is not a checkable operator", op) - } - }; +pub fn global_initializer(cx: &CodegenCx, alloc: &Allocation) -> ValueRef { + let mut llvals = Vec::with_capacity(alloc.relocations.len() + 1); + let layout = cx.data_layout(); + let pointer_size = layout.pointer_size.bytes() as usize; - let of = match result { - Ok(_) => false, - Err(ConstMathErr::Overflow(_)) | - Err(ConstMathErr::ShiftNegative) => true, - Err(err) => { - bug!("Operator `{:?}` on `{:?}` and `{:?}` errored: {}", - op, lhs, rhs, err.description()); - } - }; - - Some((const_scalar_binop(op, lllhs, llrhs, input_ty), of)) - } else { - None + 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(cx, &alloc.bytes[next_offset..offset])); + } + let ptr_offset = read_target_uint( + layout.endian, + &alloc.bytes[offset..(offset + pointer_size)], + ).expect("global_initializer: could not read relocation pointer") as u64; + llvals.push(primval_to_llvm( + cx, + PrimVal::Ptr(MemoryPointer { alloc_id, offset: ptr_offset }), + &Scalar { + value: layout::Primitive::Pointer, + valid_range: 0..=!0 + }, + Type::i8p(cx) + )); + next_offset = offset + pointer_size; } -} - -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"); + if alloc.bytes.len() >= next_offset { + llvals.push(C_bytes(cx, &alloc.bytes[next_offset ..])); } - C_uint_big(int_ty, cast_result.value) + + C_struct(cx, &llvals, true) } -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() - } - 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()) - } else { - Ok(Const::from_constval(bx.cx, &value.val, ty)) - } - } - }; - - 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 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(cx.tcx, def_id); + let cid = GlobalId { + instance, + promoted: None }; + let param_env = ty::ParamEnv::empty(traits::Reveal::All); + cx.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 = cx + .tcx + .interpret_interner + .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 = cx + .tcx + .interpret_interner + .get_alloc(alloc_id) + .expect("miri allocation never successfully created"); + Ok(global_initializer(cx, 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, + bx: &Builder<'a, 'tcx>, + constant: &'tcx ty::Const<'tcx>, + ) -> Result> { + match constant.val { + ConstVal::Unevaluated(def_id, ref substs) => { + let tcx = bx.tcx(); + let param_env = ty::ParamEnv::empty(traits::Reveal::All); + let instance = ty::Instance::resolve(tcx, param_env, def_id, substs).unwrap(); + let cid = GlobalId { + 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(bx, 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, + bx: &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), + }; + bx.tcx().const_eval(param_env.and(cid)) } - } - _ => {} + mir::Literal::Value { value } => { + Ok(self.monomorphize(&value)) + } + }.and_then(|c| self.const_to_miri_value(bx, 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; + /// process constant containing SIMD shuffle indices + pub fn simd_shuffle_indices( + &mut self, + bx: &Builder<'a, 'tcx>, + constant: &mir::Constant<'tcx>, + ) -> (ValueRef, Ty<'tcx>) { + self.mir_constant_to_miri_value(bx, constant) + .and_then(|c| { + let field_ty = constant.ty.builtin_index().unwrap(); + let fields = match constant.ty.sty { + ty::TyArray(_, n) => n.val.unwrap_u64(), + ref other => bug!("invalid simd shuffle type: {}", other), + }; + let values: Result, _> = (0..fields).map(|field| { + let field = const_val_field( + bx.tcx(), + ty::ParamEnv::empty(traits::Reveal::All), + self.instance, + None, + mir::Field::new(field as usize), + c, + constant.ty, + )?; + match field.val { + ConstVal::Value(MiriValue::ByVal(prim)) => { + let layout = bx.cx.layout_of(field_ty); + let scalar = match layout.abi { + layout::Abi::Scalar(ref x) => x, + _ => bug!("from_const: invalid ByVal layout: {:#?}", layout) + }; + Ok(primval_to_llvm( + bx.cx, prim, scalar, + layout.immediate_llvm_type(bx.cx), + )) + }, + other => bug!("simd shuffle field {:?}, {}", other, constant.ty), + } + }).collect(); + let llval = C_struct(bx.cx, &values?, false); + Ok((llval, constant.ty)) + }) + .unwrap_or_else(|e| { + e.report(bx.tcx(), constant.span, "shuffle_indices"); + // We've errored, so we don't have to produce working code. + let ty = self.monomorphize(&constant.ty); + let llty = bx.cx.layout_of(ty).llvm_type(bx.cx); + (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..75df349de41 100644 --- a/src/librustc_trans/mir/operand.rs +++ b/src/librustc_trans/mir/operand.rs @@ -9,12 +9,15 @@ // 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; +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 +27,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 +93,70 @@ impl<'a, 'tcx> OperandRef<'tcx> { } } + pub fn from_const(bx: &Builder<'a, 'tcx>, + miri_val: MiriValue, + ty: ty::Ty<'tcx>) + -> Result, ConstEvalErr<'tcx>> { + let layout = bx.cx.layout_of(ty); + + if layout.is_zst() { + return Ok(OperandRef::new_zst(bx.cx, 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( + bx.cx, + x, + scalar, + layout.immediate_llvm_type(bx.cx), + ); + 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( + bx.cx, + a, + a_scalar, + layout.scalar_pair_element_llvm_type(bx.cx, 0), + ); + let b_llval = primval_to_llvm( + bx.cx, + b, + b_scalar, + layout.scalar_pair_element_llvm_type(bx.cx, 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( + bx.cx, + ptr.into_inner_primval(), + &scalar, + layout.llvm_type(bx.cx).ptr_to(), + ); + return Ok(PlaceRef::new_sized(ptr, layout, align).load(bx)); + }, + }; + + 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 +395,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/mir/place.rs b/src/librustc_trans/mir/place.rs index 99770476e12..b340d91b027 100644 --- a/src/librustc_trans/mir/place.rs +++ b/src/librustc_trans/mir/place.rs @@ -328,7 +328,7 @@ impl<'a, 'tcx> PlaceRef<'tcx> { let ptr = self.project_field(bx, 0); let to = self.layout.ty.ty_adt_def().unwrap() .discriminant_for_variant(bx.tcx(), variant_index) - .to_u128_unchecked() as u64; + .val as u64; bx.store(C_int(ptr.layout.llvm_type(bx.cx), to as i64), ptr.llval, ptr.align); } diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index 34ac44cec02..fa0514952d2 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -30,7 +30,6 @@ use type_of::LayoutLlvmExt; use value::Value; use super::{FunctionCx, LocalRef}; -use super::constant::const_scalar_checked_binop; use super::operand::{OperandRef, OperandValue}; use super::place::PlaceRef; @@ -122,7 +121,6 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { } } - let count = count.as_u64(); let count = C_usize(bx.cx, count); let end = dest.project_index(&bx, count).llval; @@ -497,7 +495,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { if let mir::Place::Local(index) = *place { if let LocalRef::Operand(Some(op)) = self.locals[index] { if let ty::TyArray(_, n) = op.layout.ty.sty { - let n = n.val.to_const_int().unwrap().to_u64().unwrap(); + let n = n.val.unwrap_u64(); return common::C_usize(bx.cx, n); } } @@ -645,14 +643,6 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { return OperandValue::Pair(val, C_bool(bx.cx, false)); } - // First try performing the operation on constants, which - // will only succeed if both operands are constant. - // This is necessary to determine when an overflow Assert - // will always panic at runtime, and produce a warning. - if let Some((val, of)) = const_scalar_checked_binop(bx.tcx(), op, lhs, rhs, input_ty) { - return OperandValue::Pair(val, C_bool(bx.cx, of)); - } - let (val, of) = match op { // These are checked using intrinsics mir::BinOp::Add | mir::BinOp::Sub | mir::BinOp::Mul => { diff --git a/src/librustc_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/_match.rs b/src/librustc_typeck/check/_match.rs index a261c12bcdd..eb02c05fd39 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -413,7 +413,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let expected_ty = self.structurally_resolved_type(pat.span, expected); let (inner_ty, slice_ty) = match expected_ty.sty { ty::TyArray(inner_ty, size) => { - let size = size.val.to_const_int().unwrap().to_u64().unwrap(); + let size = size.val.unwrap_u64(); let min_len = before.len() as u64 + after.len() as u64; if slice.is_none() { if min_len != size { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 0f59973eab2..19085ff039e 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -93,13 +93,14 @@ use rustc::infer::{self, InferCtxt, InferOk, RegionVariableOrigin}; use rustc::infer::anon_types::AnonTypeDecl; use rustc::infer::type_variable::{TypeVariableOrigin}; use rustc::middle::region; +use rustc::mir::interpret::{GlobalId}; use rustc::ty::subst::{Kind, Subst, Substs}; use rustc::traits::{self, FulfillmentContext, ObligationCause, ObligationCauseCode}; use rustc::ty::{self, Ty, TyCtxt, Visibility, ToPredicate}; use rustc::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc::ty::fold::TypeFoldable; use rustc::ty::maps::Providers; -use rustc::ty::util::{Representability, IntTypeExt}; +use rustc::ty::util::{Representability, IntTypeExt, Discr}; use errors::{DiagnosticBuilder, DiagnosticId}; use require_c_abi_if_variadic; @@ -132,7 +133,6 @@ use rustc::hir::itemlikevisit::ItemLikeVisitor; use rustc::hir::map::Node; use rustc::hir::{self, PatKind}; use rustc::middle::lang_items; -use rustc_const_math::ConstInt; mod autoderef; pub mod dropck; @@ -1631,10 +1631,10 @@ pub fn check_enum<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } - let mut disr_vals: Vec = Vec::new(); + let mut disr_vals: Vec> = Vec::new(); for (discr, v) in def.discriminants(tcx).zip(vs) { // Check for duplicate discriminant values - if let Some(i) = disr_vals.iter().position(|&x| x == discr) { + if let Some(i) = disr_vals.iter().position(|&x| x.val == discr.val) { let variant_i_node_id = tcx.hir.as_local_node_id(def.variants[i].did).unwrap(); let variant_i = tcx.hir.expect_variant(variant_i_node_id); let i_span = match variant_i.node.disr_expr { @@ -3999,10 +3999,20 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let count_def_id = tcx.hir.body_owner_def_id(count); let param_env = ty::ParamEnv::empty(traits::Reveal::UserFacing); let substs = Substs::identity_for_item(tcx.global_tcx(), count_def_id); - let count = tcx.const_eval(param_env.and((count_def_id, substs))); + let instance = ty::Instance::resolve( + tcx.global_tcx(), + param_env, + count_def_id, + substs, + ).unwrap(); + let global_id = GlobalId { + instance, + promoted: None + }; + let count = tcx.const_eval(param_env.and(global_id)); if let Err(ref err) = count { - err.report(tcx, tcx.def_span(count_def_id), "constant expression"); + err.report(tcx, tcx.def_span(count_def_id), "constant expression"); } let uty = match expected { @@ -4029,9 +4039,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }; if let Ok(count) = count { - let zero_or_one = count.val.to_const_int().and_then(|count| { - count.to_u64().map(|count| count <= 1) - }).unwrap_or(false); + let zero_or_one = count.val.to_raw_bits().map_or(false, |count| count <= 1); if !zero_or_one { // For [foo, ..n] where n > 1, `foo` must have // Copy type: diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index 12791107ebb..47a229cbd3b 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -447,10 +447,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { hir::BiBitOr => ("bitor", lang.bitor_trait()), hir::BiShl => ("shl", lang.shl_trait()), hir::BiShr => ("shr", lang.shr_trait()), - hir::BiLt => ("lt", lang.ord_trait()), - hir::BiLe => ("le", lang.ord_trait()), - hir::BiGe => ("ge", lang.ord_trait()), - hir::BiGt => ("gt", lang.ord_trait()), + hir::BiLt => ("lt", lang.partial_ord_trait()), + hir::BiLe => ("le", lang.partial_ord_trait()), + hir::BiGe => ("ge", lang.partial_ord_trait()), + hir::BiGt => ("gt", lang.partial_ord_trait()), hir::BiEq => ("eq", lang.eq_trait()), hir::BiNe => ("ne", lang.eq_trait()), hir::BiAnd | hir::BiOr => { diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 1a7d8bb5678..858d9fb5b74 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -28,19 +28,15 @@ use astconv::{AstConv, Bounds}; use lint; use constrained_type_params as ctp; use middle::lang_items::SizedTraitLangItem; -use middle::const_val::ConstVal; use middle::resolve_lifetime as rl; use rustc::mir::mono::Linkage; -use rustc::traits::Reveal; use rustc::ty::subst::Substs; use rustc::ty::{ToPredicate, ReprOptions}; use rustc::ty::{self, AdtKind, ToPolyTraitRef, Ty, TyCtxt}; use rustc::ty::maps::Providers; use rustc::ty::util::IntTypeExt; -use rustc::util::nodemap::FxHashSet; -use util::nodemap::FxHashMap; - -use rustc_const_math::ConstInt; +use rustc::util::nodemap::{FxHashSet, FxHashMap}; +use rustc::ty::util::Discr; use syntax::{abi, ast}; use syntax::ast::MetaItemKind; @@ -512,30 +508,17 @@ fn convert_variant_ctor<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, fn convert_enum_variant_types<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, variants: &[hir::Variant]) { - let param_env = ty::ParamEnv::empty(Reveal::UserFacing); let def = tcx.adt_def(def_id); let repr_type = def.repr.discr_type(); let initial = repr_type.initial_discriminant(tcx); - let mut prev_discr = None::; + let mut prev_discr = None::>; // fill the discriminant values and field types for variant in variants { - let wrapped_discr = prev_discr.map_or(initial, |d| d.wrap_incr()); + let wrapped_discr = prev_discr.map_or(initial, |d| d.wrap_incr(tcx)); prev_discr = Some(if let Some(e) = variant.node.disr_expr { let expr_did = tcx.hir.local_def_id(e.node_id); - let substs = Substs::identity_for_item(tcx, expr_did); - let result = tcx.at(variant.span).const_eval(param_env.and((expr_did, substs))); - - // enum variant evaluation happens before the global constant check - // so we need to report the real error - if let Err(ref err) = result { - err.report(tcx, variant.span, "enum discriminant"); - } - - match result { - Ok(&ty::Const { val: ConstVal::Integral(x), .. }) => Some(x), - _ => None - } + def.eval_explicit_discr(tcx, expr_did) } else if let Some(discr) = repr_type.disr_incr(tcx, prev_discr) { Some(discr) } else { diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 96b2ec745f1..bd2267a4601 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -1020,41 +1020,6 @@ Here `X` will have already been specified the discriminant 0 by the time `Y` is encountered, so a conflict occurs. "##, -E0082: r##" -#### Note: this error code is no longer emitted by the compiler. - -When you specify enum discriminants with `=`, the compiler expects `isize` -values by default. Or you can add the `repr` attibute to the enum declaration -for an explicit choice of the discriminant type. In either cases, the -discriminant values must fall within a valid range for the expected type; -otherwise this error is raised. For example: - -```compile_fail -# #![deny(overflowing_literals)] -#[repr(u8)] -enum Thing { - A = 1024, - B = 5, -} -``` - -Here, 1024 lies outside the valid range for `u8`, so the discriminant for `A` is -invalid. Here is another, more subtle example which depends on target word size: - -```compile_fail,E0080 -# #[repr(i32)] -enum DependsOnPointerSize { - A = 1 << 32, -} -``` - -Here, `1 << 32` is interpreted as an `isize` value. So it is invalid for 32 bit -target (`target_pointer_width = "32"`) but valid for 64 bit target. - -You may want to change representation types to fix this, or else change invalid -discriminant values so that they fit within the existing type. -"##, - E0084: r##" An unsupported representation was attempted on a zero-variant enum. diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index d9bd96b0d76..40385cabf56 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -86,6 +86,7 @@ This API is completely unstable and subject to change. #![feature(refcell_replace_swap)] #![feature(rustc_diagnostic_macros)] #![feature(slice_patterns)] +#![feature(i128_type)] #[macro_use] extern crate log; #[macro_use] extern crate syntax; diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index d0230a69374..5d4addce2c4 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -34,6 +34,7 @@ use rustc::middle::privacy::AccessLevels; use rustc::middle::resolve_lifetime as rl; use rustc::ty::fold::TypeFolder; use rustc::middle::lang_items; +use rustc::mir::interpret::GlobalId; use rustc::hir::{self, HirVec}; use rustc::hir::def::{self, Def, CtorKind}; use rustc::hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX, LOCAL_CRATE}; @@ -51,7 +52,6 @@ use std::collections::hash_map::Entry; use std::collections::VecDeque; use std::fmt; -use rustc_const_math::ConstInt; use std::default::Default; use std::{mem, slice, vec}; use std::iter::{FromIterator, once}; @@ -2501,23 +2501,17 @@ 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 }) }); - let n = if let ConstVal::Integral(ConstInt::Usize(n)) = n.val { - n.to_string() - } else if let ConstVal::Unevaluated(def_id, _) = n.val { - if let Some(node_id) = cx.tcx.hir.as_local_node_id(def_id) { - print_const_expr(cx, cx.tcx.hir.body_owned_by(node_id)) - } else { - inline::print_inlined_const(cx, def_id) - } - } else { - format!("{:?}", n) - }; + let n = print_const(cx, n); Array(box ty.clean(cx), n) }, TyTup(ref tys) => Tuple(tys.clean(cx)), @@ -2636,21 +2630,15 @@ 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; } }; - let n = if let ConstVal::Integral(ConstInt::Usize(n)) = n.val { - n.to_string() - } else if let ConstVal::Unevaluated(def_id, _) = n.val { - if let Some(node_id) = cx.tcx.hir.as_local_node_id(def_id) { - print_const_expr(cx, cx.tcx.hir.body_owned_by(node_id)) - } else { - inline::print_inlined_const(cx, def_id) - } - } else { - format!("{:?}", n) - }; + let n = print_const(cx, n); Array(box ty.clean(cx), n) } ty::TyRawPtr(mt) => RawPointer(mt.mutbl.clean(cx), box mt.ty.clean(cx)), @@ -3634,6 +3622,28 @@ fn name_from_pat(p: &hir::Pat) -> String { } } +fn print_const(cx: &DocContext, n: &ty::Const) -> String { + match n.val { + ConstVal::Unevaluated(def_id, _) => { + if let Some(node_id) = cx.tcx.hir.as_local_node_id(def_id) { + print_const_expr(cx, cx.tcx.hir.body_owned_by(node_id)) + } else { + inline::print_inlined_const(cx, def_id) + } + }, + ConstVal::Value(val) => { + let mut s = String::new(); + ::rustc::mir::print_miri_value(val, n.ty, &mut s).unwrap(); + // array lengths are obviously usize + if s.ends_with("usize") { + let n = s.len() - "usize".len(); + s.truncate(n); + } + s + }, + } +} + fn print_const_expr(cx: &DocContext, body: hir::BodyId) -> String { cx.tcx.hir.node_to_pretty_string(body.node_id) } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 45d82bc7af3..1a790bf78bd 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -217,9 +217,6 @@ declare_features! ( // Allows the definition of `const fn` functions. (active, const_fn, "1.2.0", Some(24111)), - // Allows indexing into constant arrays. - (active, const_indexing, "1.4.0", Some(29947)), - // Allows using #[prelude_import] on glob `use` items. // // rustc internal @@ -490,6 +487,8 @@ declare_features! ( (accepted, augmented_assignments, "1.8.0", Some(28235)), // allow empty structs and enum variants with braces (accepted, braced_empty_structs, "1.8.0", Some(29720)), + // Allows indexing into constant arrays. + (accepted, const_indexing, "1.24.0", Some(29947)), (accepted, default_type_params, "1.0.0", None), (accepted, globs, "1.0.0", None), (accepted, if_let, "1.0.0", None), diff --git a/src/libsyntax_pos/span_encoding.rs b/src/libsyntax_pos/span_encoding.rs index b23e40ce7a9..bf9a832519a 100644 --- a/src/libsyntax_pos/span_encoding.rs +++ b/src/libsyntax_pos/span_encoding.rs @@ -19,16 +19,37 @@ use hygiene::SyntaxContext; use rustc_data_structures::fx::FxHashMap; use std::cell::RefCell; +use std::hash::{Hash, Hasher}; /// A compressed span. /// Contains either fields of `SpanData` inline if they are small, or index into span interner. /// The primary goal of `Span` is to be as small as possible and fit into other structures /// (that's why it uses `packed` as well). Decoding speed is the second priority. /// See `SpanData` for the info on span fields in decoded representation. -#[derive(Clone, Copy, PartialEq, Eq, Hash)] #[repr(packed)] pub struct Span(u32); +impl Copy for Span {} +impl Clone for Span { + fn clone(&self) -> Span { + *self + } +} +impl PartialEq for Span { + fn eq(&self, other: &Span) -> bool { + let a = self.0; + let b = other.0; + a == b + } +} +impl Eq for Span {} +impl Hash for Span { + fn hash(&self, state: &mut H) { + let a = self.0; + a.hash(state) + } +} + /// Dummy span, both position and length are zero, syntax context is zero as well. /// This span is kept inline and encoded with format 0. pub const DUMMY_SP: Span = Span(0); diff --git a/src/test/codegen/consts.rs b/src/test/codegen/consts.rs index a75b8f3992d..007fb7f4596 100644 --- a/src/test/codegen/consts.rs +++ b/src/test/codegen/consts.rs @@ -9,6 +9,7 @@ // except according to those terms. // compile-flags: -C no-prepopulate-passes +// ignore-tidy-linelength #![crate_type = "lib"] @@ -19,12 +20,11 @@ // CHECK: @STATIC = {{.*}}, align 4 // This checks the constants from inline_enum_const -// CHECK: @ref.{{[0-9]+}} = {{.*}}, align 2 +// CHECK: @byte_str.{{[0-9]+}} = {{.*}}, align 2 // This checks the constants from {low,high}_align_const, they share the same // constant, but the alignment differs, so the higher one should be used -// CHECK: [[LOW_HIGH:@ref.[0-9]+]] = {{.*}}, align 4 -// CHECK: [[LOW_HIGH_REF:@const.[0-9]+]] = {{.*}} [[LOW_HIGH]] +// CHECK: [[LOW_HIGH:@byte_str.[0-9]+]] = {{.*}}, align 4 #[derive(Copy, Clone)] @@ -54,7 +54,7 @@ pub fn inline_enum_const() -> E { #[no_mangle] pub fn low_align_const() -> E { // Check that low_align_const and high_align_const use the same constant -// CHECK: load {{.*}} bitcast ({ i16, [0 x i8], i16, [4 x i8] }** [[LOW_HIGH_REF]] +// CHECK: i8* getelementptr inbounds (<{ [8 x i8] }>, <{ [8 x i8] }>* [[LOW_HIGH]], i32 0, i32 0, i32 0), *&E::A(0) } @@ -62,6 +62,6 @@ pub fn low_align_const() -> E { #[no_mangle] pub fn high_align_const() -> E { // Check that low_align_const and high_align_const use the same constant -// CHECK: load {{.*}} bitcast ({ i16, [0 x i8], i16, [4 x i8] }** [[LOW_HIGH_REF]] +// CHECK: i8* getelementptr inbounds (<{ [8 x i8] }>, <{ [8 x i8] }>* [[LOW_HIGH]], i32 0, i32 0, i32 0), *&E::A(0) } diff --git a/src/test/codegen/link_section.rs b/src/test/codegen/link_section.rs index 1879002e7f3..9c56a316b34 100644 --- a/src/test/codegen/link_section.rs +++ b/src/test/codegen/link_section.rs @@ -12,7 +12,7 @@ #![crate_type = "lib"] -// CHECK: @VAR1 = constant i32 1, section ".test_one" +// CHECK: @VAR1 = constant <{ [4 x i8] }> <{ [4 x i8] c"\01\00\00\00" }>, section ".test_one" #[no_mangle] #[link_section = ".test_one"] pub static VAR1: u32 = 1; diff --git a/src/test/codegen/remap_path_prefix/main.rs b/src/test/codegen/remap_path_prefix/main.rs index 2f46b6c5d48..4fb8c37558d 100644 --- a/src/test/codegen/remap_path_prefix/main.rs +++ b/src/test/codegen/remap_path_prefix/main.rs @@ -22,7 +22,7 @@ mod aux_mod; include!("aux_mod.rs"); // Here we check that the expansion of the file!() macro is mapped. -// CHECK: internal constant [34 x i8] c"/the/src/remap_path_prefix/main.rs" +// CHECK: @byte_str.1 = private unnamed_addr constant <{ [34 x i8] }> <{ [34 x i8] c"/the/src/remap_path_prefix/main.rs" }>, align 1 pub static FILE_PATH: &'static str = file!(); fn main() { diff --git a/src/test/compile-fail/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..3de0f1ff61e 100644 --- a/src/test/compile-fail/const-err-early.rs +++ b/src/test/compile-fail/const-err-early.rs @@ -8,17 +8,25 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(const_indexing)] #![deny(const_err)] -pub const A: i8 = -std::i8::MIN; //~ ERROR 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 constant evaluation error +pub const B: u8 = 200u8 + 200u8; //~ ERROR E0080 +//~^ ERROR attempt to add with overflow +pub const C: u8 = 200u8 * 4; //~ ERROR E0080 +//~^ ERROR attempt to multiply with overflow +pub const D: u8 = 42u8 - (42u8 + 1); //~ ERROR E0080 +//~^ ERROR attempt to subtract with overflow pub const E: u8 = [5u8][1]; -//~^ ERROR index out of bounds: the len is 1 but the index is 1 +//~^ ERROR E0080 fn main() { + let _a = A; + let _b = B; + let _c = C; + let _d = D; + let _e = E; 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..d2355f57f17 100644 --- a/src/test/compile-fail/const-err-multi.rs +++ b/src/test/compile-fail/const-err-multi.rs @@ -10,10 +10,17 @@ #![deny(const_err)] -pub const A: i8 = -std::i8::MIN; //~ ERROR attempt to negate with overflow +pub const A: i8 = -std::i8::MIN; +//~^ ERROR E0080 +//~| ERROR attempt to negate with overflow +//~| ERROR constant evaluation error 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() { + let _ = (A, B, C, D); } 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..46b73371e56 100644 --- a/src/test/compile-fail/const-err2.rs +++ b/src/test/compile-fail/const-err2.rs @@ -8,6 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// needed because negating int::MIN will behave differently between +// optimized compilation and unoptimized compilation and thus would +// lead to different lints being emitted +// compile-flags: -O + #![feature(rustc_attrs)] #![allow(exceeding_bitshifts)] #![deny(const_err)] @@ -18,13 +23,13 @@ fn black_box(_: T) { fn main() { let a = -std::i8::MIN; - //~^ ERROR attempt to negate with overflow + //~^ ERROR const_err let b = 200u8 + 200u8 + 200u8; - //~^ ERROR attempt to add with overflow + //~^ ERROR const_err let c = 200u8 * 4; - //~^ ERROR attempt to multiply with overflow + //~^ ERROR const_err let d = 42u8 - (42u8 + 1); - //~^ ERROR attempt to subtract with overflow + //~^ ERROR const_err let _e = [5u8][1]; black_box(a); black_box(b); diff --git a/src/test/compile-fail/const-err3.rs b/src/test/compile-fail/const-err3.rs new file mode 100644 index 00000000000..9656af60024 --- /dev/null +++ b/src/test/compile-fail/const-err3.rs @@ -0,0 +1,29 @@ +// 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 + let c = 200u8 * 4; + //~^ ERROR const_err + let d = 42u8 - (42u8 + 1); + //~^ ERROR const_err + let _e = [5u8][1]; + black_box(b); + black_box(c); + 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..a0d8f9672c0 --- /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 + //~| ERROR attempt to subtract with overflow + ); + +const VALS_I16: (i16,) = + ( + i16::MIN - 1, + //~^ ERROR constant evaluation error + //~| ERROR attempt to subtract with overflow + ); + +const VALS_I32: (i32,) = + ( + i32::MIN - 1, + //~^ ERROR constant evaluation error + //~| ERROR attempt to subtract with overflow + ); + +const VALS_I64: (i64,) = + ( + i64::MIN - 1, + //~^ ERROR constant evaluation error + //~| ERROR attempt to subtract with overflow + ); + +const VALS_U8: (u8,) = + ( + u8::MIN - 1, + //~^ ERROR constant evaluation error + //~| ERROR attempt to subtract with overflow + ); + +const VALS_U16: (u16,) = ( + u16::MIN - 1, + //~^ ERROR constant evaluation error + //~| ERROR attempt to subtract with overflow + ); + +const VALS_U32: (u32,) = ( + u32::MIN - 1, + //~^ ERROR constant evaluation error + //~| ERROR attempt to subtract with overflow + ); + +const VALS_U64: (u64,) = + ( + u64::MIN - 1, + //~^ ERROR constant evaluation error + //~| ERROR attempt to subtract with overflow + ); + +fn main() { + 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..08128f90e53 --- /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 + //~| ERROR attempt to add with overflow + ); + +const VALS_I16: (i16,) = + ( + i16::MAX + 1, + //~^ ERROR constant evaluation error + //~| ERROR attempt to add with overflow + ); + +const VALS_I32: (i32,) = + ( + i32::MAX + 1, + //~^ ERROR constant evaluation error + //~| ERROR attempt to add with overflow + ); + +const VALS_I64: (i64,) = + ( + i64::MAX + 1, + //~^ ERROR constant evaluation error + //~| ERROR attempt to add with overflow + ); + +const VALS_U8: (u8,) = + ( + u8::MAX + 1, + //~^ ERROR constant evaluation error + //~| ERROR attempt to add with overflow + ); + +const VALS_U16: (u16,) = ( + u16::MAX + 1, + //~^ ERROR constant evaluation error + //~| ERROR attempt to add with overflow + ); + +const VALS_U32: (u32,) = ( + u32::MAX + 1, + //~^ ERROR constant evaluation error + //~| ERROR attempt to add with overflow + ); + +const VALS_U64: (u64,) = + ( + u64::MAX + 1, + //~^ ERROR constant evaluation error + //~| ERROR attempt to add with overflow + ); + +fn main() { + 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..31a1638cade --- /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 + //~| ERROR attempt to multiply with overflow + ); + +const VALS_I16: (i16,) = + ( + i16::MIN * 2, + //~^ ERROR constant evaluation error + //~| ERROR attempt to multiply with overflow + ); + +const VALS_I32: (i32,) = + ( + i32::MIN * 2, + //~^ ERROR constant evaluation error + //~| ERROR attempt to multiply with overflow + ); + +const VALS_I64: (i64,) = + ( + i64::MIN * 2, + //~^ ERROR constant evaluation error + //~| ERROR attempt to multiply with overflow + ); + +const VALS_U8: (u8,) = + ( + u8::MAX * 2, + //~^ ERROR constant evaluation error + //~| ERROR attempt to multiply with overflow + ); + +const VALS_U16: (u16,) = ( + u16::MAX * 2, + //~^ ERROR constant evaluation error + //~| ERROR attempt to multiply with overflow + ); + +const VALS_U32: (u32,) = ( + u32::MAX * 2, + //~^ ERROR constant evaluation error + //~| ERROR attempt to multiply with overflow + ); + +const VALS_U64: (u64,) = + ( + u64::MAX * 2, + //~^ ERROR constant evaluation error + //~| ERROR attempt to multiply with overflow + ); + +fn main() { + 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/huge-array.rs b/src/test/compile-fail/huge-array.rs index 029e9651cb3..7de84802e1d 100644 --- a/src/test/compile-fail/huge-array.rs +++ b/src/test/compile-fail/huge-array.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:; 1518599999 +// error-pattern:; 1518600000 fn generic(t: T) { let s: [T; 1518600000] = [t; 1518600000]; diff --git a/src/test/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-31109.rs b/src/test/compile-fail/issue-31109.rs index e3548d74071..74121e3a420 100644 --- a/src/test/compile-fail/issue-31109.rs +++ b/src/test/compile-fail/issue-31109.rs @@ -12,6 +12,5 @@ fn main() { // FIXME(#31407) this error should go away, but in the meantime we test that it // is accompanied by a somewhat useful error message. let _: f64 = 1234567890123456789012345678901234567890e-340; - //~^ ERROR constant evaluation error - //~| unimplemented constant expression: could not evaluate float literal + //~^ ERROR could not evaluate float literal (see issue #31407) } diff --git a/src/test/compile-fail/issue-39559-2.rs b/src/test/compile-fail/issue-39559-2.rs index aa075023064..f01fd1fd8f1 100644 --- a/src/test/compile-fail/issue-39559-2.rs +++ b/src/test/compile-fail/issue-39559-2.rs @@ -22,7 +22,9 @@ impl Dim for Dim3 { fn main() { let array: [usize; Dim3::dim()] - //~^ ERROR calls in constants are limited to constant functions + //~^ ERROR E0015 + //~| ERROR E0080 = [0; Dim3::dim()]; - //~^ ERROR calls in constants are limited to constant functions + //~^ ERROR E0015 + //~| ERROR E0080 } diff --git a/src/test/compile-fail/issue-41255.rs b/src/test/compile-fail/issue-41255.rs index 191b867e7a8..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 literals cannot be used + 5.0 => {}, //~ ERROR floating-point types cannot be used in patterns //~| WARNING hard error - 5.0f32 => {}, //~ ERROR floating-point literals cannot be used + 5.0f32 => {}, //~ ERROR floating-point types cannot be used in patterns //~| WARNING hard error - -5.0 => {}, //~ ERROR floating-point literals cannot be used + -5.0 => {}, //~ ERROR floating-point types cannot be used in patterns //~| WARNING hard error - 1.0 .. 33.0 => {}, //~ ERROR floating-point literals cannot be used + 1.0 .. 33.0 => {}, //~ ERROR floating-point types cannot be used in patterns //~| WARNING hard error - //~| ERROR floating-point literals cannot be used + //~| ERROR floating-point types cannot be used in patterns //~| WARNING hard error - 39.0 ... 70.0 => {}, //~ ERROR floating-point literals cannot be used + 39.0 ... 70.0 => {}, //~ ERROR floating-point types cannot be used in patterns //~| WARNING hard error - //~| ERROR floating-point literals 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 literals 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 literals 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-43105.rs b/src/test/compile-fail/issue-43105.rs index fb419d751b4..c0af3b4b9e6 100644 --- a/src/test/compile-fail/issue-43105.rs +++ b/src/test/compile-fail/issue-43105.rs @@ -12,6 +12,7 @@ fn xyz() -> u8 { 42 } const NUM: u8 = xyz(); //~^ ERROR calls in constants are limited to constant functions, struct and enum constructors +//~| ERROR constant evaluation error fn main() { match 1 { diff --git a/src/test/compile-fail/issue-44578.rs b/src/test/compile-fail/issue-44578.rs index a6ae21c3b54..18cfb35113d 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]; } 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 3e51550d1fa..5ebfcc4926b 100644 --- a/src/test/compile-fail/lint-exceeding-bitshifts.rs +++ b/src/test/compile-fail/lint-exceeding-bitshifts.rs @@ -8,67 +8,50 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![deny(exceeding_bitshifts)] +#![deny(exceeding_bitshifts, const_err)] #![allow(unused_variables)] #![allow(dead_code)] -#![feature(const_indexing)] fn main() { let n = 1u8 << 7; - let n = 1u8 << 8; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u8 << 8; //~ ERROR: attempt to shift left with overflow let n = 1u16 << 15; - let n = 1u16 << 16; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u16 << 16; //~ ERROR: attempt to shift left with overflow let n = 1u32 << 31; - let n = 1u32 << 32; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u32 << 32; //~ ERROR: attempt to shift left with overflow let n = 1u64 << 63; - let n = 1u64 << 64; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u64 << 64; //~ ERROR: attempt to shift left with overflow let n = 1i8 << 7; - let n = 1i8 << 8; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i8 << 8; //~ ERROR: attempt to shift left with overflow let n = 1i16 << 15; - let n = 1i16 << 16; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i16 << 16; //~ ERROR: attempt to shift left with overflow let n = 1i32 << 31; - let n = 1i32 << 32; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i32 << 32; //~ ERROR: attempt to shift left with overflow let n = 1i64 << 63; - let n = 1i64 << 64; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i64 << 64; //~ ERROR: attempt to shift left with overflow let n = 1u8 >> 7; - let n = 1u8 >> 8; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u8 >> 8; //~ ERROR: attempt to shift right with overflow let n = 1u16 >> 15; - let n = 1u16 >> 16; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u16 >> 16; //~ ERROR: attempt to shift right with overflow let n = 1u32 >> 31; - let n = 1u32 >> 32; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u32 >> 32; //~ ERROR: attempt to shift right with overflow let n = 1u64 >> 63; - let n = 1u64 >> 64; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u64 >> 64; //~ ERROR: attempt to shift right with overflow let n = 1i8 >> 7; - let n = 1i8 >> 8; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i8 >> 8; //~ ERROR: attempt to shift right with overflow let n = 1i16 >> 15; - let n = 1i16 >> 16; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i16 >> 16; //~ ERROR: attempt to shift right with overflow let n = 1i32 >> 31; - let n = 1i32 >> 32; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i32 >> 32; //~ ERROR: attempt to shift right with overflow let n = 1i64 >> 63; - let n = 1i64 >> 64; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i64 >> 64; //~ ERROR: attempt to shift right with overflow let n = 1u8; let n = n << 7; - let n = n << 8; //~ ERROR: bitshift exceeds the type's number of bits - - let n = 1u8 << -8; //~ ERROR: bitshift exceeds the type's number of bits - //~^ WARN: attempt to shift by a negative amount - - let n = 1u8 << (4+3); - let n = 1u8 << (4+4); //~ ERROR: bitshift exceeds the type's number of bits - - #[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 = n << 8; //~ ERROR: attempt to shift left with overflow + let n = 1u8 << -8; //~ ERROR: attempt to shift left with overflow 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..3ba300eb7c4 --- /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, const_err)] +#![allow(unused_variables)] +#![allow(dead_code)] + +fn main() { + let n = 1u8 << (4+3); + let n = 1u8 << (4+4); //~ ERROR: attempt to shift left with overflow + let n = 1i64 >> [63][0]; + let n = 1i64 >> [64][0]; // should be linting, needs to wait for const propagation + + #[cfg(target_pointer_width = "32")] + const BITS: usize = 32; + #[cfg(target_pointer_width = "64")] + const BITS: usize = 64; + let n = 1_isize << BITS; //~ ERROR: attempt to shift left with overflow + let n = 1_usize << BITS; //~ ERROR: attempt to shift left with overflow +} diff --git a/src/test/compile-fail/lint-type-overflow2.rs b/src/test/compile-fail/lint-type-overflow2.rs index 55934997583..f7cf8a68d56 100644 --- a/src/test/compile-fail/lint-type-overflow2.rs +++ b/src/test/compile-fail/lint-type-overflow2.rs @@ -17,7 +17,6 @@ #[rustc_error] fn main() { //~ ERROR: compilation successful let x2: i8 = --128; //~ warn: literal out of range for i8 - //~^ warn: attempt to negate with overflow let x = -3.40282357e+38_f32; //~ warn: literal out of range for f32 let x = 3.40282357e+38_f32; //~ warn: literal out of range for f32 diff --git a/src/test/compile-fail/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 679be9ce219..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,8 @@ fn main() { let x = 0.0; match x { f32::INFINITY => { } - //~^ ERROR floating point constants 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/thread-local-in-ctfe.rs b/src/test/compile-fail/thread-local-in-ctfe.rs index 720e15991c0..dc220bd1cc9 100644 --- a/src/test/compile-fail/thread-local-in-ctfe.rs +++ b/src/test/compile-fail/thread-local-in-ctfe.rs @@ -16,7 +16,6 @@ static A: u32 = 1; static B: u32 = A; //~^ ERROR thread-local statics cannot be accessed at compile-time //~| ERROR cannot refer to other statics by value -//~| WARN non-constant path in constant expression static C: &u32 = &A; //~^ ERROR thread-local statics cannot be accessed at compile-time @@ -24,7 +23,6 @@ static C: &u32 = &A; const D: u32 = A; //~^ ERROR thread-local statics cannot be accessed at compile-time //~| ERROR cannot refer to statics by value -//~| WARN non-constant path in constant expression const E: &u32 = &A; //~^ ERROR thread-local statics cannot be accessed at compile-time diff --git a/src/test/mir-opt/end_region_2.rs b/src/test/mir-opt/end_region_2.rs index d6084d5a6da..3fb5621c8ae 100644 --- a/src/test/mir-opt/end_region_2.rs +++ b/src/test/mir-opt/end_region_2.rs @@ -49,7 +49,7 @@ fn main() { // _3 = &'23_1rs _2; // StorageLive(_5); // _5 = _2; -// switchInt(move _5) -> [0u8: bb5, otherwise: bb4]; +// switchInt(move _5) -> [false: bb5, otherwise: bb4]; // } // bb3: { // ... diff --git a/src/test/mir-opt/end_region_3.rs b/src/test/mir-opt/end_region_3.rs index 46548f1cce9..070bde8e3c3 100644 --- a/src/test/mir-opt/end_region_3.rs +++ b/src/test/mir-opt/end_region_3.rs @@ -51,7 +51,7 @@ fn main() { // _3 = &'26_1rs _1; // StorageLive(_5); // _5 = _1; -// switchInt(move _5) -> [0u8: bb5, otherwise: bb4]; +// switchInt(move _5) -> [false: bb5, otherwise: bb4]; // } // bb3: { // ... diff --git a/src/test/mir-opt/end_region_9.rs b/src/test/mir-opt/end_region_9.rs index 0f1d714cc6f..6d9a27eeeb4 100644 --- a/src/test/mir-opt/end_region_9.rs +++ b/src/test/mir-opt/end_region_9.rs @@ -72,7 +72,7 @@ fn main() { // bb4: { // StorageLive(_7); // _7 = _1; -// switchInt(move _7) -> [0u8: bb6, otherwise: bb5]; +// switchInt(move _7) -> [false: bb6, otherwise: bb5]; // } // bb5: { // _0 = (); diff --git a/src/test/mir-opt/end_region_cyclic.rs b/src/test/mir-opt/end_region_cyclic.rs index 2a82e2675b6..83425a72f45 100644 --- a/src/test/mir-opt/end_region_cyclic.rs +++ b/src/test/mir-opt/end_region_cyclic.rs @@ -103,7 +103,7 @@ fn query() -> bool { true } // _11 = const query() -> [return: bb6, unwind: bb3]; // } // bb6: { -// switchInt(move _11) -> [0u8: bb8, otherwise: bb7]; +// switchInt(move _11) -> [false: bb8, otherwise: bb7]; // } // bb7: { // _0 = (); diff --git a/src/test/mir-opt/issue-38669.rs b/src/test/mir-opt/issue-38669.rs index 3151c064307..a9eea26f466 100644 --- a/src/test/mir-opt/issue-38669.rs +++ b/src/test/mir-opt/issue-38669.rs @@ -36,7 +36,7 @@ fn main() { // bb3: { // StorageLive(_4); // _4 = _1; -// switchInt(move _4) -> [0u8: bb5, otherwise: bb4]; +// switchInt(move _4) -> [false: bb5, otherwise: bb4]; // } // bb4: { // _0 = (); diff --git a/src/test/mir-opt/match_false_edges.rs b/src/test/mir-opt/match_false_edges.rs index ba1b54d59f6..53f17861997 100644 --- a/src/test/mir-opt/match_false_edges.rs +++ b/src/test/mir-opt/match_false_edges.rs @@ -93,7 +93,7 @@ fn main() { // _7 = const guard() -> [return: bb10, unwind: bb1]; // } // bb10: { // end of guard -// switchInt(move _7) -> [0u8: bb11, otherwise: bb2]; +// switchInt(move _7) -> [false: bb11, otherwise: bb2]; // } // bb11: { // to pre_binding2 // falseEdges -> [real: bb5, imaginary: bb5]; @@ -157,7 +157,7 @@ fn main() { // _7 = const guard() -> [return: bb10, unwind: bb1]; // } // bb10: { // end of guard -// switchInt(move _7) -> [0u8: bb11, otherwise: bb2]; +// switchInt(move _7) -> [false: bb11, otherwise: bb2]; // } // bb11: { // to pre_binding2 // falseEdges -> [real: bb6, imaginary: bb5]; @@ -219,7 +219,7 @@ fn main() { // _9 = const guard() -> [return: bb10, unwind: bb1]; // } // bb10: { //end of guard -// switchInt(move _9) -> [0u8: bb11, otherwise: bb2]; +// switchInt(move _9) -> [false: bb11, otherwise: bb2]; // } // bb11: { // to pre_binding2 // falseEdges -> [real: bb5, imaginary: bb5]; @@ -240,7 +240,7 @@ fn main() { // } // bb14: { // end of guard2 // StorageDead(_12); -// switchInt(move _11) -> [0u8: bb15, otherwise: bb3]; +// switchInt(move _11) -> [false: bb15, otherwise: bb3]; // } // bb15: { // to pre_binding4 // falseEdges -> [real: bb7, imaginary: bb7]; diff --git a/src/test/mir-opt/nll/region-liveness-basic.rs b/src/test/mir-opt/nll/region-liveness-basic.rs index e9834305550..19d733d4f6b 100644 --- a/src/test/mir-opt/nll/region-liveness-basic.rs +++ b/src/test/mir-opt/nll/region-liveness-basic.rs @@ -41,7 +41,7 @@ fn main() { // | Live variables on entry to bb2[0]: [_1, _3] // _2 = &'_#2r _1[_3]; // | Live variables on entry to bb2[1]: [_2] -// switchInt(const true) -> [0u8: bb4, otherwise: bb3]; +// switchInt(const true) -> [false: bb4, otherwise: bb3]; // } // END rustc.main.nll.0.mir // START rustc.main.nll.0.mir diff --git a/src/test/mir-opt/simplify_if.rs b/src/test/mir-opt/simplify_if.rs index 35786643648..52d5892e656 100644 --- a/src/test/mir-opt/simplify_if.rs +++ b/src/test/mir-opt/simplify_if.rs @@ -17,7 +17,7 @@ fn main() { // END RUST SOURCE // START rustc.main.SimplifyBranches-initial.before.mir // bb0: { -// switchInt(const false) -> [0u8: bb3, otherwise: bb2]; +// switchInt(const false) -> [false: bb3, otherwise: bb2]; // } // END rustc.main.SimplifyBranches-initial.before.mir // START rustc.main.SimplifyBranches-initial.after.mir diff --git a/src/test/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/const-negation.rs b/src/test/run-pass/const-negation.rs index 012fe0d95ec..5c633eb6112 100644 --- a/src/test/run-pass/const-negation.rs +++ b/src/test/run-pass/const-negation.rs @@ -17,11 +17,13 @@ fn main() { const I: isize = -9223372036854775808isize; assert_eq!(::std::i32::MIN as u64, 0xffffffff80000000); assert_eq!(-2147483648isize as u64, 0xffffffff80000000); + assert_eq!(-2147483648i32 as u64, 0xffffffff80000000); assert_eq!(::std::i64::MIN as u64, 0x8000000000000000); #[cfg(target_pointer_width = "64")] assert_eq!(-9223372036854775808isize as u64, 0x8000000000000000); #[cfg(target_pointer_width = "32")] assert_eq!(-9223372036854775808isize as u64, 0); + assert_eq!(-9223372036854775808i32 as u64, 0); const J: usize = ::std::i32::MAX as usize; const K: usize = -1i32 as u32 as usize; const L: usize = ::std::i32::MIN as usize; diff --git a/src/test/run-pass/ctfe/chained-constants-stackoverflow.rs b/src/test/run-pass/ctfe/chained-constants-stackoverflow.rs new file mode 100644 index 00000000000..813dd5fbb99 --- /dev/null +++ b/src/test/run-pass/ctfe/chained-constants-stackoverflow.rs @@ -0,0 +1,364 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// https://github.com/rust-lang/rust/issues/34997 + +pub const CST_1: u32 = 0; +pub const CST_2: u32 = CST_1+1; +pub const CST_3: u32 = CST_2+1; +pub const CST_4: u32 = CST_3+1; +pub const CST_5: u32 = CST_4+1; +pub const CST_6: u32 = CST_5+1; +pub const CST_7: u32 = CST_6+1; +pub const CST_8: u32 = CST_7+1; +pub const CST_9: u32 = CST_8+1; +pub const CST_10: u32 = CST_9+1; +pub const CST_11: u32 = CST_10+1; +pub const CST_12: u32 = CST_11+1; +pub const CST_13: u32 = CST_12+1; +pub const CST_14: u32 = CST_13+1; +pub const CST_15: u32 = CST_14+1; +pub const CST_16: u32 = CST_15+1; +pub const CST_17: u32 = CST_16+1; +pub const CST_18: u32 = CST_17+1; +pub const CST_19: u32 = CST_18+1; +pub const CST_20: u32 = CST_19+1; +pub const CST_21: u32 = CST_20+1; +pub const CST_22: u32 = CST_21+1; +pub const CST_23: u32 = CST_22+1; +pub const CST_24: u32 = CST_23+1; +pub const CST_25: u32 = CST_24+1; +pub const CST_26: u32 = CST_25+1; +pub const CST_27: u32 = CST_26+1; +pub const CST_28: u32 = CST_27+1; +pub const CST_29: u32 = CST_28+1; +pub const CST_30: u32 = CST_29+1; +pub const CST_31: u32 = CST_30+1; +pub const CST_32: u32 = CST_31+1; +pub const CST_33: u32 = CST_32+1; +pub const CST_34: u32 = CST_33+1; +pub const CST_35: u32 = CST_34+1; +pub const CST_36: u32 = CST_35+1; +pub const CST_37: u32 = CST_36+1; +pub const CST_38: u32 = CST_37+1; +pub const CST_39: u32 = CST_38+1; +pub const CST_40: u32 = CST_39+1; +pub const CST_41: u32 = CST_40+1; +pub const CST_42: u32 = CST_41+1; +pub const CST_43: u32 = CST_42+1; +pub const CST_44: u32 = CST_43+1; +pub const CST_45: u32 = CST_44+1; +pub const CST_46: u32 = CST_45+1; +pub const CST_47: u32 = CST_46+1; +pub const CST_48: u32 = CST_47+1; +pub const CST_49: u32 = CST_48+1; +pub const CST_50: u32 = CST_49+1; +pub const CST_51: u32 = CST_50+1; +pub const CST_52: u32 = CST_51+1; +pub const CST_53: u32 = CST_52+1; +pub const CST_54: u32 = CST_53+1; +pub const CST_55: u32 = CST_54+1; +pub const CST_56: u32 = CST_55+1; +pub const CST_57: u32 = CST_56+1; +pub const CST_58: u32 = CST_57+1; +pub const CST_59: u32 = CST_58+1; +pub const CST_60: u32 = CST_59+1; +pub const CST_61: u32 = CST_60+1; +pub const CST_62: u32 = CST_61+1; +pub const CST_63: u32 = CST_62+1; +pub const CST_64: u32 = CST_63+1; +pub const CST_65: u32 = CST_64+1; +pub const CST_66: u32 = CST_65+1; +pub const CST_67: u32 = CST_66+1; +pub const CST_68: u32 = CST_67+1; +pub const CST_69: u32 = CST_68+1; +pub const CST_70: u32 = CST_69+1; +pub const CST_71: u32 = CST_70+1; +pub const CST_72: u32 = CST_71+1; +pub const CST_73: u32 = CST_72+1; +pub const CST_74: u32 = CST_73+1; +pub const CST_75: u32 = CST_74+1; +pub const CST_76: u32 = CST_75+1; +pub const CST_77: u32 = CST_76+1; +pub const CST_78: u32 = CST_77+1; +pub const CST_79: u32 = CST_78+1; +pub const CST_80: u32 = CST_79+1; +pub const CST_81: u32 = CST_80+1; +pub const CST_82: u32 = CST_81+1; +pub const CST_83: u32 = CST_82+1; +pub const CST_84: u32 = CST_83+1; +pub const CST_85: u32 = CST_84+1; +pub const CST_86: u32 = CST_85+1; +pub const CST_87: u32 = CST_86+1; +pub const CST_88: u32 = CST_87+1; +pub const CST_89: u32 = CST_88+1; +pub const CST_90: u32 = CST_89+1; +pub const CST_91: u32 = CST_90+1; +pub const CST_92: u32 = CST_91+1; +pub const CST_93: u32 = CST_92+1; +pub const CST_94: u32 = CST_93+1; +pub const CST_95: u32 = CST_94+1; +pub const CST_96: u32 = CST_95+1; +pub const CST_97: u32 = CST_96+1; +pub const CST_98: u32 = CST_97+1; +pub const CST_99: u32 = CST_98+1; +pub const CST_100: u32 = CST_99+1; +pub const CST_101: u32 = CST_100+1; +pub const CST_102: u32 = CST_101+1; +pub const CST_103: u32 = CST_102+1; +pub const CST_104: u32 = CST_103+1; +pub const CST_105: u32 = CST_104+1; +pub const CST_106: u32 = CST_105+1; +pub const CST_107: u32 = CST_106+1; +pub const CST_108: u32 = CST_107+1; +pub const CST_109: u32 = CST_108+1; +pub const CST_110: u32 = CST_109+1; +pub const CST_111: u32 = CST_110+1; +pub const CST_112: u32 = CST_111+1; +pub const CST_113: u32 = CST_112+1; +pub const CST_114: u32 = CST_113+1; +pub const CST_115: u32 = CST_114+1; +pub const CST_116: u32 = CST_115+1; +pub const CST_117: u32 = CST_116+1; +pub const CST_118: u32 = CST_117+1; +pub const CST_119: u32 = CST_118+1; +pub const CST_120: u32 = CST_119+1; +pub const CST_121: u32 = CST_120+1; +pub const CST_122: u32 = CST_121+1; +pub const CST_123: u32 = CST_122+1; +pub const CST_124: u32 = CST_123+1; +pub const CST_125: u32 = CST_124+1; +pub const CST_126: u32 = CST_125+1; +pub const CST_127: u32 = CST_126+1; +pub const CST_128: u32 = CST_127+1; +pub const CST_129: u32 = CST_128+1; +pub const CST_130: u32 = CST_129+1; +pub const CST_131: u32 = CST_130+1; +pub const CST_132: u32 = CST_131+1; +pub const CST_133: u32 = CST_132+1; +pub const CST_134: u32 = CST_133+1; +pub const CST_135: u32 = CST_134+1; +pub const CST_136: u32 = CST_135+1; +pub const CST_137: u32 = CST_136+1; +pub const CST_138: u32 = CST_137+1; +pub const CST_139: u32 = CST_138+1; +pub const CST_140: u32 = CST_139+1; +pub const CST_141: u32 = CST_140+1; +pub const CST_142: u32 = CST_141+1; +pub const CST_143: u32 = CST_142+1; +pub const CST_144: u32 = CST_143+1; +pub const CST_145: u32 = CST_144+1; +pub const CST_146: u32 = CST_145+1; +pub const CST_147: u32 = CST_146+1; +pub const CST_148: u32 = CST_147+1; +pub const CST_149: u32 = CST_148+1; +pub const CST_150: u32 = CST_149+1; +pub const CST_151: u32 = CST_150+1; +pub const CST_152: u32 = CST_151+1; +pub const CST_153: u32 = CST_152+1; +pub const CST_154: u32 = CST_153+1; +pub const CST_155: u32 = CST_154+1; +pub const CST_156: u32 = CST_155+1; +pub const CST_157: u32 = CST_156+1; +pub const CST_158: u32 = CST_157+1; +pub const CST_159: u32 = CST_158+1; +pub const CST_160: u32 = CST_159+1; +pub const CST_161: u32 = CST_160+1; +pub const CST_162: u32 = CST_161+1; +pub const CST_163: u32 = CST_162+1; +pub const CST_164: u32 = CST_163+1; +pub const CST_165: u32 = CST_164+1; +pub const CST_166: u32 = CST_165+1; +pub const CST_167: u32 = CST_166+1; +pub const CST_168: u32 = CST_167+1; +pub const CST_169: u32 = CST_168+1; +pub const CST_170: u32 = CST_169+1; +pub const CST_171: u32 = CST_170+1; +pub const CST_172: u32 = CST_171+1; +pub const CST_173: u32 = CST_172+1; +pub const CST_174: u32 = CST_173+1; +pub const CST_175: u32 = CST_174+1; +pub const CST_176: u32 = CST_175+1; +pub const CST_177: u32 = CST_176+1; +pub const CST_178: u32 = CST_177+1; +pub const CST_179: u32 = CST_178+1; +pub const CST_180: u32 = CST_179+1; +pub const CST_181: u32 = CST_180+1; +pub const CST_182: u32 = CST_181+1; +pub const CST_183: u32 = CST_182+1; +pub const CST_184: u32 = CST_183+1; +pub const CST_185: u32 = CST_184+1; +pub const CST_186: u32 = CST_185+1; +pub const CST_187: u32 = CST_186+1; +pub const CST_188: u32 = CST_187+1; +pub const CST_189: u32 = CST_188+1; +pub const CST_190: u32 = CST_189+1; +pub const CST_191: u32 = CST_190+1; +pub const CST_192: u32 = CST_191+1; +pub const CST_193: u32 = CST_192+1; +pub const CST_194: u32 = CST_193+1; +pub const CST_195: u32 = CST_194+1; +pub const CST_196: u32 = CST_195+1; +pub const CST_197: u32 = CST_196+1; +pub const CST_198: u32 = CST_197+1; +pub const CST_199: u32 = CST_198+1; +pub const CST_200: u32 = CST_199+1; +pub const CST_201: u32 = CST_200+1; +pub const CST_202: u32 = CST_201+1; +pub const CST_203: u32 = CST_202+1; +pub const CST_204: u32 = CST_203+1; +pub const CST_205: u32 = CST_204+1; +pub const CST_206: u32 = CST_205+1; +pub const CST_207: u32 = CST_206+1; +pub const CST_208: u32 = CST_207+1; +pub const CST_209: u32 = CST_208+1; +pub const CST_210: u32 = CST_209+1; +pub const CST_211: u32 = CST_210+1; +pub const CST_212: u32 = CST_211+1; +pub const CST_213: u32 = CST_212+1; +pub const CST_214: u32 = CST_213+1; +pub const CST_215: u32 = CST_214+1; +pub const CST_216: u32 = CST_215+1; +pub const CST_217: u32 = CST_216+1; +pub const CST_218: u32 = CST_217+1; +pub const CST_219: u32 = CST_218+1; +pub const CST_220: u32 = CST_219+1; +pub const CST_221: u32 = CST_220+1; +pub const CST_222: u32 = CST_221+1; +pub const CST_223: u32 = CST_222+1; +pub const CST_224: u32 = CST_223+1; +pub const CST_225: u32 = CST_224+1; +pub const CST_226: u32 = CST_225+1; +pub const CST_227: u32 = CST_226+1; +pub const CST_228: u32 = CST_227+1; +pub const CST_229: u32 = CST_228+1; +pub const CST_230: u32 = CST_229+1; +pub const CST_231: u32 = CST_230+1; +pub const CST_232: u32 = CST_231+1; +pub const CST_233: u32 = CST_232+1; +pub const CST_234: u32 = CST_233+1; +pub const CST_235: u32 = CST_234+1; +pub const CST_236: u32 = CST_235+1; +pub const CST_237: u32 = CST_236+1; +pub const CST_238: u32 = CST_237+1; +pub const CST_239: u32 = CST_238+1; +pub const CST_240: u32 = CST_239+1; +pub const CST_241: u32 = CST_240+1; +pub const CST_242: u32 = CST_241+1; +pub const CST_243: u32 = CST_242+1; +pub const CST_244: u32 = CST_243+1; +pub const CST_245: u32 = CST_244+1; +pub const CST_246: u32 = CST_245+1; +pub const CST_247: u32 = CST_246+1; +pub const CST_248: u32 = CST_247+1; +pub const CST_249: u32 = CST_248+1; +pub const CST_250: u32 = CST_249+1; +pub const CST_251: u32 = CST_250+1; +pub const CST_252: u32 = CST_251+1; +pub const CST_253: u32 = CST_252+1; +pub const CST_254: u32 = CST_253+1; +pub const CST_255: u32 = CST_254+1; +pub const CST_256: u32 = CST_255+1; +pub const CST_257: u32 = CST_256+1; +pub const CST_258: u32 = CST_257+1; +pub const CST_259: u32 = CST_258+1; +pub const CST_260: u32 = CST_259+1; +pub const CST_261: u32 = CST_260+1; +pub const CST_262: u32 = CST_261+1; +pub const CST_263: u32 = CST_262+1; +pub const CST_264: u32 = CST_263+1; +pub const CST_265: u32 = CST_264+1; +pub const CST_266: u32 = CST_265+1; +pub const CST_267: u32 = CST_266+1; +pub const CST_268: u32 = CST_267+1; +pub const CST_269: u32 = CST_268+1; +pub const CST_270: u32 = CST_269+1; +pub const CST_271: u32 = CST_270+1; +pub const CST_272: u32 = CST_271+1; +pub const CST_273: u32 = CST_272+1; +pub const CST_274: u32 = CST_273+1; +pub const CST_275: u32 = CST_274+1; +pub const CST_276: u32 = CST_275+1; +pub const CST_277: u32 = CST_276+1; +pub const CST_278: u32 = CST_277+1; +pub const CST_279: u32 = CST_278+1; +pub const CST_280: u32 = CST_279+1; +pub const CST_281: u32 = CST_280+1; +pub const CST_282: u32 = CST_281+1; +pub const CST_283: u32 = CST_282+1; +pub const CST_284: u32 = CST_283+1; +pub const CST_285: u32 = CST_284+1; +pub const CST_286: u32 = CST_285+1; +pub const CST_287: u32 = CST_286+1; +pub const CST_288: u32 = CST_287+1; +pub const CST_289: u32 = CST_288+1; +pub const CST_290: u32 = CST_289+1; +pub const CST_291: u32 = CST_290+1; +pub const CST_292: u32 = CST_291+1; +pub const CST_293: u32 = CST_292+1; +pub const CST_294: u32 = CST_293+1; +pub const CST_295: u32 = CST_294+1; +pub const CST_296: u32 = CST_295+1; +pub const CST_297: u32 = CST_296+1; +pub const CST_298: u32 = CST_297+1; +pub const CST_299: u32 = CST_298+1; +pub const CST_300: u32 = CST_299+1; +pub const CST_301: u32 = CST_300+1; +pub const CST_302: u32 = CST_301+1; +pub const CST_303: u32 = CST_302+1; +pub const CST_304: u32 = CST_303+1; +pub const CST_305: u32 = CST_304+1; +pub const CST_306: u32 = CST_305+1; +pub const CST_307: u32 = CST_306+1; +pub const CST_308: u32 = CST_307+1; +pub const CST_309: u32 = CST_308+1; +pub const CST_310: u32 = CST_309+1; +pub const CST_311: u32 = CST_310+1; +pub const CST_312: u32 = CST_311+1; +pub const CST_313: u32 = CST_312+1; +pub const CST_314: u32 = CST_313+1; +pub const CST_315: u32 = CST_314+1; +pub const CST_316: u32 = CST_315+1; +pub const CST_317: u32 = CST_316+1; +pub const CST_318: u32 = CST_317+1; +pub const CST_319: u32 = CST_318+1; +pub const CST_320: u32 = CST_319+1; +pub const CST_321: u32 = CST_320+1; +pub const CST_322: u32 = CST_321+1; +pub const CST_323: u32 = CST_322+1; +pub const CST_324: u32 = CST_323+1; +pub const CST_325: u32 = CST_324+1; +pub const CST_326: u32 = CST_325+1; +pub const CST_327: u32 = CST_326+1; +pub const CST_328: u32 = CST_327+1; +pub const CST_329: u32 = CST_328+1; +pub const CST_330: u32 = CST_329+1; +pub const CST_331: u32 = CST_330+1; +pub const CST_332: u32 = CST_331+1; +pub const CST_333: u32 = CST_332+1; +pub const CST_334: u32 = CST_333+1; +pub const CST_335: u32 = CST_334+1; +pub const CST_336: u32 = CST_335+1; +pub const CST_337: u32 = CST_336+1; +pub const CST_338: u32 = CST_337+1; +pub const CST_339: u32 = CST_338+1; +pub const CST_340: u32 = CST_339+1; +pub const CST_341: u32 = CST_340+1; +pub const CST_342: u32 = CST_341+1; +pub const CST_343: u32 = CST_342+1; +pub const CST_344: u32 = CST_343+1; +pub const CST_345: u32 = CST_344+1; +pub const CST_346: u32 = CST_345+1; +pub const CST_347: u32 = CST_346+1; +pub const CST_348: u32 = CST_347+1; +pub const CST_349: u32 = CST_348+1; +pub const CST_350: u32 = CST_349+1; + +fn main() {} diff --git a/src/test/run-pass/ctfe/deref_in_pattern.rs b/src/test/run-pass/ctfe/deref_in_pattern.rs new file mode 100644 index 00000000000..4ccfa0338f3 --- /dev/null +++ b/src/test/run-pass/ctfe/deref_in_pattern.rs @@ -0,0 +1,20 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// https://github.com/rust-lang/rust/issues/25574 + +const A: [u8; 4] = *b"fooo"; + +fn main() { + match *b"xxxx" { + A => {}, + _ => {} + } +} diff --git a/src/test/run-pass/ctfe/ice-48279.rs b/src/test/run-pass/ctfe/ice-48279.rs new file mode 100644 index 00000000000..c435e5fdaab --- /dev/null +++ b/src/test/run-pass/ctfe/ice-48279.rs @@ -0,0 +1,34 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// https://github.com/rust-lang/rust/issues/48279 + +#![feature(const_fn)] + +#[derive(PartialEq, Eq)] +pub struct NonZeroU32 { + value: u32 +} + +impl NonZeroU32 { + const unsafe fn new_unchecked(value: u32) -> Self { + NonZeroU32 { value } + } +} + +//pub const FOO_ATOM: NonZeroU32 = unsafe { NonZeroU32::new_unchecked(7) }; +pub const FOO_ATOM: NonZeroU32 = unsafe { NonZeroU32 { value: 7 } }; + +fn main() { + match None { + Some(FOO_ATOM) => {} + _ => {} + } +} diff --git a/src/test/run-pass/ctfe/issue-broken-mir.rs b/src/test/run-pass/ctfe/issue-broken-mir.rs new file mode 100644 index 00000000000..6ed0c7c0d5d --- /dev/null +++ b/src/test/run-pass/ctfe/issue-broken-mir.rs @@ -0,0 +1,18 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// https://github.com/rust-lang/rust/issues/27918 + +fn main() { + match b" " { + b"1234" => {}, + _ => {}, + } +} diff --git a/src/test/run-pass/ctfe/match-const-fn-structs.rs b/src/test/run-pass/ctfe/match-const-fn-structs.rs new file mode 100644 index 00000000000..0bb253d1a64 --- /dev/null +++ b/src/test/run-pass/ctfe/match-const-fn-structs.rs @@ -0,0 +1,31 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// https://github.com/rust-lang/rust/issues/46114 + +#![feature(const_fn)] + +#[derive(Eq, PartialEq)] +struct A { value: u32 } + +const fn new(value: u32) -> A { + A { value } +} + +const A_1: A = new(1); +const A_2: A = new(2); + +fn main() { + let a_str = match new(42) { + A_1 => "A 1", + A_2 => "A 2", + _ => "Unknown A", + }; +} diff --git a/src/test/run-pass/ctfe/mozjs-error.rs b/src/test/run-pass/ctfe/mozjs-error.rs new file mode 100644 index 00000000000..9c8a4b5ae6a --- /dev/null +++ b/src/test/run-pass/ctfe/mozjs-error.rs @@ -0,0 +1,37 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct CustomAutoRooterVFTable { + trace: unsafe extern "C" fn(this: *mut i32, trc: *mut u32), +} + +unsafe trait CustomAutoTraceable: Sized { + const vftable: CustomAutoRooterVFTable = CustomAutoRooterVFTable { + trace: Self::trace, + }; + + unsafe extern "C" fn trace(this: *mut i32, trc: *mut u32) { + let this = this as *const Self; + let this = this.as_ref().unwrap(); + Self::do_trace(this, trc); + } + + fn do_trace(&self, trc: *mut u32); +} + +unsafe impl CustomAutoTraceable for () { + fn do_trace(&self, _: *mut u32) { + // nop + } +} + +fn main() { + let _ = <()>::vftable; +} diff --git a/src/test/run-pass/ctfe/non-scalar-cast.rs b/src/test/run-pass/ctfe/non-scalar-cast.rs new file mode 100644 index 00000000000..ff4474f47c9 --- /dev/null +++ b/src/test/run-pass/ctfe/non-scalar-cast.rs @@ -0,0 +1,17 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// https://github.com/rust-lang/rust/issues/37448 + +fn main() { + struct A; + const FOO: &A = &(A as A); + let _x = FOO; +} diff --git a/src/test/run-pass/ctfe/promotion.rs b/src/test/run-pass/ctfe/promotion.rs new file mode 100644 index 00000000000..2d228408254 --- /dev/null +++ b/src/test/run-pass/ctfe/promotion.rs @@ -0,0 +1,17 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn foo(_: &'static [&'static str]) {} +fn bar(_: &'static [&'static str; 3]) {} + +fn main() { + foo(&["a", "b", "c"]); + bar(&["d", "e", "f"]); +} diff --git a/src/test/run-pass/ctfe/references.rs b/src/test/run-pass/ctfe/references.rs new file mode 100644 index 00000000000..ad7dbeb79c7 --- /dev/null +++ b/src/test/run-pass/ctfe/references.rs @@ -0,0 +1,34 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +const FOO: &[u8] = b"foo"; +const BAR: &[u8] = &[1, 2, 3]; + +const BOO: &i32 = &42; + +fn main() { + match &[1u8, 2, 3] as &[u8] { + FOO => panic!("a"), + BAR => println!("b"), + _ => panic!("c"), + } + + match b"foo" as &[u8] { + FOO => println!("a"), + BAR => panic!("b"), + _ => panic!("c"), + } + + match &43 { + &42 => panic!(), + BOO => panic!(), + _ => println!("d"), + } +} diff --git a/src/test/run-pass/ctfe/repeat_match.rs b/src/test/run-pass/ctfe/repeat_match.rs new file mode 100644 index 00000000000..dedf5defebb --- /dev/null +++ b/src/test/run-pass/ctfe/repeat_match.rs @@ -0,0 +1,20 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// https://github.com/rust-lang/rust/issues/45044 + +const X: [u8; 1] = [0; 1]; + +fn main() { + match &X { + &X => println!("a"), + _ => println!("b"), + }; +} diff --git a/src/test/run-pass/ctfe/return-in-const-fn.rs b/src/test/run-pass/ctfe/return-in-const-fn.rs new file mode 100644 index 00000000000..d57d3bcb49a --- /dev/null +++ b/src/test/run-pass/ctfe/return-in-const-fn.rs @@ -0,0 +1,19 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// https://github.com/rust-lang/rust/issues/43754 + +#![feature(const_fn)] +const fn foo(x: usize) -> usize { + return x; +} +fn main() { + [0; foo(2)]; +} diff --git a/src/test/run-pass/ctfe/tuple-struct-constructors.rs b/src/test/run-pass/ctfe/tuple-struct-constructors.rs new file mode 100644 index 00000000000..ecc5d376636 --- /dev/null +++ b/src/test/run-pass/ctfe/tuple-struct-constructors.rs @@ -0,0 +1,20 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// https://github.com/rust-lang/rust/issues/41898 + +#![feature(nonzero, const_fn)] +extern crate core; +use core::nonzero::NonZero; + +fn main() { + const FOO: NonZero = unsafe { NonZero::new_unchecked(2) }; + if let FOO = FOO {} +} diff --git a/src/test/run-pass/ctfe/union-ice.rs b/src/test/run-pass/ctfe/union-ice.rs new file mode 100644 index 00000000000..f83f49f298b --- /dev/null +++ b/src/test/run-pass/ctfe/union-ice.rs @@ -0,0 +1,45 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(const_fn)] + +type Field1 = i32; +type Field2 = f32; +type Field3 = i64; + +union DummyUnion { + field1: Field1, + field2: Field2, + field3: Field3, +} + +const FLOAT1_AS_I32: i32 = 1065353216; +const UNION: DummyUnion = DummyUnion { field1: FLOAT1_AS_I32 }; + +const fn read_field1() -> Field1 { + const FIELD1: Field1 = unsafe { UNION.field1 }; + FIELD1 +} + +const fn read_field2() -> Field2 { + const FIELD2: Field2 = unsafe { UNION.field2 }; + FIELD2 +} + +const fn read_field3() -> Field3 { + const FIELD3: Field3 = unsafe { UNION.field3 }; + FIELD3 +} + +fn main() { + assert_eq!(read_field1(), FLOAT1_AS_I32); + assert_eq!(read_field2(), 1.0); + assert_eq!(read_field3(), unsafe { UNION.field3 }); +} diff --git a/src/test/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-overflow-2.rs b/src/test/ui/const-eval-overflow-2.rs index 6b7f631ff4c..885edb55ed8 100644 --- a/src/test/ui/const-eval-overflow-2.rs +++ b/src/test/ui/const-eval-overflow-2.rs @@ -19,8 +19,7 @@ use std::{u8, u16, u32, u64, usize}; const NEG_128: i8 = -128; const NEG_NEG_128: i8 = -NEG_128; -//~^ ERROR constant evaluation error -//~| attempt to negate with overflow +//~^ ERROR E0080 fn main() { match -128i8 { diff --git a/src/test/ui/const-eval-overflow-2.stderr b/src/test/ui/const-eval-overflow-2.stderr index 05a286d4e7e..f376de7cc4c 100644 --- a/src/test/ui/const-eval-overflow-2.stderr +++ b/src/test/ui/const-eval-overflow-2.stderr @@ -5,7 +5,7 @@ LL | const NEG_NEG_128: i8 = -NEG_128; | ^^^^^^^^ attempt to negate with overflow | note: for pattern here - --> $DIR/const-eval-overflow-2.rs:27:9 + --> $DIR/const-eval-overflow-2.rs:26:9 | LL | NEG_NEG_128 => println!("A"), | ^^^^^^^^^^^ diff --git a/src/test/ui/const-eval-overflow-4.rs b/src/test/ui/const-eval-overflow-4.rs index 4423fdec33a..24e178152ee 100644 --- a/src/test/ui/const-eval-overflow-4.rs +++ b/src/test/ui/const-eval-overflow-4.rs @@ -21,8 +21,8 @@ use std::{u8, u16, u32, u64, usize}; const A_I8_T : [u32; (i8::MAX as i8 + 1i8) as usize] - //~^ ERROR constant evaluation error - //~| WARNING constant evaluation error + //~^ ERROR E0080 + //~| WARN attempt to add with overflow = [0; (i8::MAX as usize) + 1]; fn main() { diff --git a/src/test/ui/const-eval-overflow-4.stderr b/src/test/ui/const-eval-overflow-4.stderr index b907a47afb9..14753038fef 100644 --- a/src/test/ui/const-eval-overflow-4.stderr +++ b/src/test/ui/const-eval-overflow-4.stderr @@ -1,8 +1,8 @@ -warning: constant evaluation error: attempt to add with overflow +warning: attempt to add with overflow --> $DIR/const-eval-overflow-4.rs:23:13 | LL | : [u32; (i8::MAX as i8 + 1i8) as usize] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ | = note: #[warn(const_err)] on by default diff --git a/src/test/ui/const-eval/conditional_array_execution.rs b/src/test/ui/const-eval/conditional_array_execution.rs new file mode 100644 index 00000000000..324bcf60e8f --- /dev/null +++ b/src/test/ui/const-eval/conditional_array_execution.rs @@ -0,0 +1,18 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +const X: u32 = 5; +const Y: u32 = 6; +const FOO: u32 = [X - Y, Y - X][(X < Y) as usize]; +//~^ WARN attempt to subtract with overflow + +fn main() { + println!("{}", FOO); //~ E0080 +} diff --git a/src/test/ui/const-eval/conditional_array_execution.stderr b/src/test/ui/const-eval/conditional_array_execution.stderr new file mode 100644 index 00000000000..4cf12e22283 --- /dev/null +++ b/src/test/ui/const-eval/conditional_array_execution.stderr @@ -0,0 +1,17 @@ +warning: attempt to subtract with overflow + --> $DIR/conditional_array_execution.rs:13:19 + | +LL | const FOO: u32 = [X - Y, Y - X][(X < Y) as usize]; + | ^^^^^ + | + = note: #[warn(const_err)] on by default + +error[E0080]: constant evaluation error + --> $DIR/conditional_array_execution.rs:17:20 + | +LL | println!("{}", FOO); //~ E0080 + | ^^^ referenced constant has errors + +error: aborting due to previous error + +If you want more information on this error, try using "rustc --explain E0080" diff --git a/src/test/ui/const-eval/const_transmute.rs b/src/test/ui/const-eval/const_transmute.rs new file mode 100644 index 00000000000..a64a1d212ab --- /dev/null +++ b/src/test/ui/const-eval/const_transmute.rs @@ -0,0 +1,62 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// must-compile-successfully +// run-pass + +union Transmute { + t: T, + u: U, +} + +trait Bar { + fn bar(&self) -> u32; +} + +struct Foo { + foo: u32, + bar: bool, +} + +impl Bar for Foo { + fn bar(&self) -> u32 { + self.foo + } +} + +impl Drop for Foo { + fn drop(&mut self) { + assert!(!self.bar); + self.bar = true; + println!("dropping Foo"); + } +} + +#[derive(Copy, Clone)] +struct Fat<'a>(&'a Foo, &'static VTable); + +struct VTable { + drop: Option fn(&'a mut Foo)>, + size: usize, + align: usize, + bar: for<'a> fn(&'a Foo) -> u32, +} + +const FOO: &Bar = &Foo { foo: 128, bar: false }; +const G: Fat = unsafe { Transmute { t: FOO }.u }; +const F: Option fn(&'a mut Foo)> = G.1.drop; +const H: for<'a> fn(&'a Foo) -> u32 = G.1.bar; + +fn main() { + let mut foo = Foo { foo: 99, bar: false }; + (F.unwrap())(&mut foo); + std::mem::forget(foo); // already ran the drop impl + assert_eq!(H(&Foo { foo: 42, bar: false }), 42); +} diff --git a/src/test/ui/const-eval/const_transmute.stderr b/src/test/ui/const-eval/const_transmute.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/test/ui/const-eval/index_out_of_bound.rs b/src/test/ui/const-eval/index_out_of_bound.rs new file mode 100644 index 00000000000..e7ffbe81b9a --- /dev/null +++ b/src/test/ui/const-eval/index_out_of_bound.rs @@ -0,0 +1,14 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +static FOO: i32 = [][0]; +//~^ ERROR E0080 + +fn main() {} diff --git a/src/test/ui/const-eval/index_out_of_bound.stderr b/src/test/ui/const-eval/index_out_of_bound.stderr new file mode 100644 index 00000000000..7651fb257e3 --- /dev/null +++ b/src/test/ui/const-eval/index_out_of_bound.stderr @@ -0,0 +1,9 @@ +error[E0080]: constant evaluation error + --> $DIR/index_out_of_bound.rs:11:19 + | +LL | static FOO: i32 = [][0]; + | ^^^^^ index out of bounds: the len is 0 but the index is 0 at $DIR/index_out_of_bound.rs:11:19: 11:24 + +error: aborting due to previous error + +If you want more information on this error, try using "rustc --explain E0080" diff --git a/src/test/ui/const-eval/issue-43197.rs b/src/test/ui/const-eval/issue-43197.rs index 85ab2a00521..d5c4796d0b4 100644 --- a/src/test/ui/const-eval/issue-43197.rs +++ b/src/test/ui/const-eval/issue-43197.rs @@ -15,9 +15,11 @@ const fn foo(x: u32) -> u32 { } fn main() { - const X: u32 = 0-1; //~ ERROR constant evaluation error - //~^ WARN constant evaluation error - const Y: u32 = foo(0-1); //~ ERROR constant evaluation error - //~^ WARN constant evaluation error + const X: u32 = 0-1; + //~^ WARN attempt to subtract with overflow + const Y: u32 = foo(0-1); + //~^ WARN attempt to subtract with overflow println!("{} {}", X, Y); + //~^ ERROR constant evaluation error + //~| ERROR constant evaluation error } diff --git a/src/test/ui/const-eval/issue-43197.stderr b/src/test/ui/const-eval/issue-43197.stderr index c523b749d37..3f108268747 100644 --- a/src/test/ui/const-eval/issue-43197.stderr +++ b/src/test/ui/const-eval/issue-43197.stderr @@ -1,28 +1,28 @@ -warning: constant evaluation error: attempt to subtract with overflow +warning: attempt to subtract with overflow --> $DIR/issue-43197.rs:18:20 | -LL | const X: u32 = 0-1; //~ ERROR constant evaluation error +LL | const X: u32 = 0-1; | ^^^ | = note: #[warn(const_err)] on by default -warning: constant evaluation error: attempt to subtract with overflow - --> $DIR/issue-43197.rs:20:20 - | -LL | const Y: u32 = foo(0-1); //~ ERROR constant evaluation error - | ^^^^^^^^ - error[E0080]: constant evaluation error - --> $DIR/issue-43197.rs:18:20 + --> $DIR/issue-43197.rs:22:23 | -LL | const X: u32 = 0-1; //~ ERROR constant evaluation error - | ^^^ attempt to subtract with overflow +LL | println!("{} {}", X, Y); + | ^ referenced constant has errors -error[E0080]: constant evaluation error +warning: attempt to subtract with overflow --> $DIR/issue-43197.rs:20:24 | -LL | const Y: u32 = foo(0-1); //~ ERROR constant evaluation error - | ^^^ attempt to subtract with overflow +LL | const Y: u32 = foo(0-1); + | ^^^ + +error[E0080]: constant evaluation error + --> $DIR/issue-43197.rs:22:26 + | +LL | println!("{} {}", X, Y); + | ^ referenced constant has errors error: aborting due to 2 previous errors diff --git a/src/test/ui/const-eval/issue-47971.rs b/src/test/ui/const-eval/issue-47971.rs new file mode 100644 index 00000000000..4a2f0a7f38c --- /dev/null +++ b/src/test/ui/const-eval/issue-47971.rs @@ -0,0 +1,21 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// must-compile-successfully + +#![feature(const_fn)] + +struct S(pub &'static u32, pub u32); + +const fn g(ss: &S) -> &u32 { &ss.1 } + +static T: S = S(g(&T), 0); + +fn main () { } diff --git a/src/test/ui/const-eval/no_lint_for_statically_known_error.rs b/src/test/ui/const-eval/no_lint_for_statically_known_error.rs new file mode 100644 index 00000000000..968a7de4691 --- /dev/null +++ b/src/test/ui/const-eval/no_lint_for_statically_known_error.rs @@ -0,0 +1,28 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// must-compile-successfully + +// if `X` were used instead of `x`, `X - 10` would result in a lint. +// This file should never produce a lint, no matter how the const +// propagator is improved. + +#![deny(warnings)] + +const X: u32 = 5; + +fn main() { + let x = X; + if x > 10 { + println!("{}", x - 10); + } else { + println!("{}", 10 - x); + } +} diff --git a/src/test/ui/const-eval/pub_const_err.rs b/src/test/ui/const-eval/pub_const_err.rs new file mode 100644 index 00000000000..bdb9f5b19a8 --- /dev/null +++ b/src/test/ui/const-eval/pub_const_err.rs @@ -0,0 +1,16 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![deny(const_err)] + +#![crate_type = "lib"] + +pub const Z: u32 = 0 - 1; +//~^ ERROR attempt to subtract with overflow diff --git a/src/test/ui/const-eval/pub_const_err.stderr b/src/test/ui/const-eval/pub_const_err.stderr new file mode 100644 index 00000000000..b77ec38ca16 --- /dev/null +++ b/src/test/ui/const-eval/pub_const_err.stderr @@ -0,0 +1,14 @@ +error: attempt to subtract with overflow + --> $DIR/pub_const_err.rs:15:20 + | +LL | pub const Z: u32 = 0 - 1; + | ^^^^^ + | +note: lint level defined here + --> $DIR/pub_const_err.rs:11:9 + | +LL | #![deny(const_err)] + | ^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/const-expr-addr-operator.rs b/src/test/ui/const-expr-addr-operator.rs index 24d4457f01d..bfd6a409064 100644 --- a/src/test/ui/const-expr-addr-operator.rs +++ b/src/test/ui/const-expr-addr-operator.rs @@ -9,10 +9,11 @@ // except according to those terms. // Encountered while testing #44614. +// must-compile-successfully pub fn main() { // Constant of generic type (int) - const X: &'static u32 = &22; //~ ERROR constant evaluation error + const X: &'static u32 = &22; assert_eq!(0, match &22 { X => 0, _ => 1, diff --git a/src/test/ui/const-expr-addr-operator.stderr b/src/test/ui/const-expr-addr-operator.stderr index bd86f270f31..e69de29bb2d 100644 --- a/src/test/ui/const-expr-addr-operator.stderr +++ b/src/test/ui/const-expr-addr-operator.stderr @@ -1,15 +0,0 @@ -error[E0080]: constant evaluation error - --> $DIR/const-expr-addr-operator.rs:15:29 - | -LL | const X: &'static u32 = &22; //~ ERROR constant evaluation error - | ^^^ unimplemented constant expression: address operator - | -note: for pattern here - --> $DIR/const-expr-addr-operator.rs:17:9 - | -LL | X => 0, - | ^ - -error: aborting due to previous error - -If you want more information on this error, try using "rustc --explain E0080" diff --git a/src/test/ui/const-fn-error.rs b/src/test/ui/const-fn-error.rs index ac1c2fe5432..9e09f66776c 100644 --- a/src/test/ui/const-fn-error.rs +++ b/src/test/ui/const-fn-error.rs @@ -13,17 +13,18 @@ const X : usize = 2; const fn f(x: usize) -> usize { - let mut sum = 0; //~ ERROR blocks in constant functions are limited - for i in 0..x { //~ ERROR calls in constant functions - //~| ERROR constant function contains unimplemented + let mut sum = 0; + //~^ ERROR E0016 + for i in 0..x { + //~^ ERROR E0015 + //~| ERROR E0019 + //~| ERROR E0080 sum += i; } - sum //~ ERROR E0080 - //~| non-constant path in constant + sum } #[allow(unused_variables)] fn main() { let a : [i32; f(X)]; - //~^ WARNING constant evaluation error: non-constant path } diff --git a/src/test/ui/const-fn-error.stderr b/src/test/ui/const-fn-error.stderr index e738a5e4ff3..d57efce0dff 100644 --- a/src/test/ui/const-fn-error.stderr +++ b/src/test/ui/const-fn-error.stderr @@ -1,37 +1,32 @@ -warning: constant evaluation error: non-constant path in constant expression - --> $DIR/const-fn-error.rs:27:19 - | -LL | let a : [i32; f(X)]; - | ^^^^ - | - = note: #[warn(const_err)] on by default - error[E0016]: blocks in constant functions are limited to items and tail expressions --> $DIR/const-fn-error.rs:16:19 | -LL | let mut sum = 0; //~ ERROR blocks in constant functions are limited +LL | let mut sum = 0; | ^ error[E0015]: calls in constant functions are limited to constant functions, struct and enum constructors - --> $DIR/const-fn-error.rs:17:14 + --> $DIR/const-fn-error.rs:18:14 | -LL | for i in 0..x { //~ ERROR calls in constant functions +LL | for i in 0..x { | ^^^^ error[E0019]: constant function contains unimplemented expression type - --> $DIR/const-fn-error.rs:17:14 + --> $DIR/const-fn-error.rs:18:14 | -LL | for i in 0..x { //~ ERROR calls in constant functions +LL | for i in 0..x { | ^^^^ error[E0080]: constant evaluation error - --> $DIR/const-fn-error.rs:21:5 + --> $DIR/const-fn-error.rs:18:14 | -LL | sum //~ ERROR E0080 - | ^^^ non-constant path in constant expression +LL | for i in 0..x { + | ^^^^ calling non-const fn `>::into_iter` +... +LL | let a : [i32; f(X)]; + | ---- inside call to `f` | note: for constant expression here - --> $DIR/const-fn-error.rs:27:13 + --> $DIR/const-fn-error.rs:29:13 | LL | let a : [i32; f(X)]; | ^^^^^^^^^^^ diff --git a/src/test/ui/const-len-underflow-separate-spans.rs b/src/test/ui/const-len-underflow-separate-spans.rs index 823cc988947..ee07dabab1f 100644 --- a/src/test/ui/const-len-underflow-separate-spans.rs +++ b/src/test/ui/const-len-underflow-separate-spans.rs @@ -15,9 +15,10 @@ const ONE: usize = 1; const TWO: usize = 2; const LEN: usize = ONE - TWO; -//~^ ERROR constant evaluation error [E0080] +//~^ ERROR E0080 //~| WARN attempt to subtract with overflow fn main() { let a: [i8; LEN] = unimplemented!(); +//~^ ERROR E0080 } diff --git a/src/test/ui/const-len-underflow-separate-spans.stderr b/src/test/ui/const-len-underflow-separate-spans.stderr index 6391433ebaf..98f4ac9e83f 100644 --- a/src/test/ui/const-len-underflow-separate-spans.stderr +++ b/src/test/ui/const-len-underflow-separate-spans.stderr @@ -1,4 +1,4 @@ -warning: constant evaluation error: attempt to subtract with overflow +warning: attempt to subtract with overflow --> $DIR/const-len-underflow-separate-spans.rs:17:20 | LL | const LEN: usize = ONE - TWO; @@ -11,13 +11,13 @@ error[E0080]: constant evaluation error | LL | const LEN: usize = ONE - TWO; | ^^^^^^^^^ attempt to subtract with overflow - | -note: for constant expression here - --> $DIR/const-len-underflow-separate-spans.rs:22:12 + +error[E0080]: constant evaluation error + --> $DIR/const-len-underflow-separate-spans.rs:22:17 | LL | let a: [i8; LEN] = unimplemented!(); - | ^^^^^^^^^ + | ^^^ referenced constant has errors -error: aborting due to previous error +error: aborting due to 2 previous errors If you want more information on this error, try using "rustc --explain E0080" diff --git a/src/test/ui/const-pattern-not-const-evaluable.rs b/src/test/ui/const-pattern-not-const-evaluable.rs index 263c0bdc64c..09b24d1ffa2 100644 --- a/src/test/ui/const-pattern-not-const-evaluable.rs +++ b/src/test/ui/const-pattern-not-const-evaluable.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// must-compile-successfully + #![feature(const_fn)] #[derive(PartialEq, Eq)] @@ -20,8 +22,6 @@ use Cake::*; struct Pair(A, B); const BOO: Pair = Pair(Marmor, BlackForest); -//~^ ERROR: constant evaluation error [E0080] -//~| unimplemented constant expression: tuple struct constructors const FOO: Cake = BOO.1; const fn foo() -> Cake { diff --git a/src/test/ui/const-pattern-not-const-evaluable.stderr b/src/test/ui/const-pattern-not-const-evaluable.stderr index 74677c7f117..e69de29bb2d 100644 --- a/src/test/ui/const-pattern-not-const-evaluable.stderr +++ b/src/test/ui/const-pattern-not-const-evaluable.stderr @@ -1,15 +0,0 @@ -error[E0080]: constant evaluation error - --> $DIR/const-pattern-not-const-evaluable.rs:22:31 - | -LL | const BOO: Pair = Pair(Marmor, BlackForest); - | ^^^^ unimplemented constant expression: tuple struct constructors - | -note: for pattern here - --> $DIR/const-pattern-not-const-evaluable.rs:37:9 - | -LL | FOO => println!("hi"), - | ^^^ - -error: aborting due to previous error - -If you want more information on this error, try using "rustc --explain E0080" diff --git a/src/test/ui/discrim-overflow-2.stderr b/src/test/ui/discrim-overflow-2.stderr index 1facda94cd6..6162766b587 100644 --- a/src/test/ui/discrim-overflow-2.stderr +++ b/src/test/ui/discrim-overflow-2.stderr @@ -2,65 +2,65 @@ error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow-2.rs:27:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 127i8 + | ^^^^ overflowed on value after 127 | - = note: explicitly set `OhNo = -128i8` if that is desired outcome + = note: explicitly set `OhNo = -128` if that is desired outcome error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow-2.rs:36:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 255u8 + | ^^^^ overflowed on value after 255 | - = note: explicitly set `OhNo = 0u8` if that is desired outcome + = note: explicitly set `OhNo = 0` if that is desired outcome error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow-2.rs:45:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 32767i16 + | ^^^^ overflowed on value after 32767 | - = note: explicitly set `OhNo = -32768i16` if that is desired outcome + = note: explicitly set `OhNo = -32768` if that is desired outcome error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow-2.rs:54:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 65535u16 + | ^^^^ overflowed on value after 65535 | - = note: explicitly set `OhNo = 0u16` if that is desired outcome + = note: explicitly set `OhNo = 0` if that is desired outcome error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow-2.rs:63:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 2147483647i32 + | ^^^^ overflowed on value after 2147483647 | - = note: explicitly set `OhNo = -2147483648i32` if that is desired outcome + = note: explicitly set `OhNo = -2147483648` if that is desired outcome error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow-2.rs:72:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 4294967295u32 + | ^^^^ overflowed on value after 4294967295 | - = note: explicitly set `OhNo = 0u32` if that is desired outcome + = note: explicitly set `OhNo = 0` if that is desired outcome error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow-2.rs:81:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 9223372036854775807i64 + | ^^^^ overflowed on value after 9223372036854775807 | - = note: explicitly set `OhNo = -9223372036854775808i64` if that is desired outcome + = note: explicitly set `OhNo = -9223372036854775808` if that is desired outcome error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow-2.rs:90:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 18446744073709551615u64 + | ^^^^ overflowed on value after 18446744073709551615 | - = note: explicitly set `OhNo = 0u64` if that is desired outcome + = note: explicitly set `OhNo = 0` if that is desired outcome error: aborting due to 8 previous errors diff --git a/src/test/ui/discrim-overflow.rs b/src/test/ui/discrim-overflow.rs index 0b31d9f97f1..16b417c61a5 100644 --- a/src/test/ui/discrim-overflow.rs +++ b/src/test/ui/discrim-overflow.rs @@ -56,7 +56,7 @@ fn f_u16() { Ok = u16::MAX - 1, Ok2, OhNo, //~ ERROR enum discriminant overflowed [E0370] - //~| overflowed on value after 65535u16 + //~| overflowed on value after 65535 } let x = A::Ok; @@ -68,7 +68,7 @@ fn f_i32() { Ok = i32::MAX - 1, Ok2, OhNo, //~ ERROR enum discriminant overflowed [E0370] - //~| overflowed on value after 2147483647i32 + //~| overflowed on value after 2147483647 } let x = A::Ok; @@ -80,7 +80,7 @@ fn f_u32() { Ok = u32::MAX - 1, Ok2, OhNo, //~ ERROR enum discriminant overflowed [E0370] - //~| overflowed on value after 4294967295u32 + //~| overflowed on value after 4294967295 } let x = A::Ok; @@ -92,7 +92,7 @@ fn f_i64() { Ok = i64::MAX - 1, Ok2, OhNo, //~ ERROR enum discriminant overflowed [E0370] - //~| overflowed on value after 9223372036854775807i64 + //~| overflowed on value after 9223372036854775807 } let x = A::Ok; @@ -104,7 +104,7 @@ fn f_u64() { Ok = u64::MAX - 1, Ok2, OhNo, //~ ERROR enum discriminant overflowed [E0370] - //~| overflowed on value after 18446744073709551615u64 + //~| overflowed on value after 18446744073709551615 } let x = A::Ok; diff --git a/src/test/ui/discrim-overflow.stderr b/src/test/ui/discrim-overflow.stderr index 43c032b12a6..a713aea1b21 100644 --- a/src/test/ui/discrim-overflow.stderr +++ b/src/test/ui/discrim-overflow.stderr @@ -2,65 +2,65 @@ error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow.rs:25:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 127i8 + | ^^^^ overflowed on value after 127 | - = note: explicitly set `OhNo = -128i8` if that is desired outcome + = note: explicitly set `OhNo = -128` if that is desired outcome error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow.rs:36:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 255u8 + | ^^^^ overflowed on value after 255 | - = note: explicitly set `OhNo = 0u8` if that is desired outcome + = note: explicitly set `OhNo = 0` if that is desired outcome error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow.rs:47:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 32767i16 + | ^^^^ overflowed on value after 32767 | - = note: explicitly set `OhNo = -32768i16` if that is desired outcome + = note: explicitly set `OhNo = -32768` if that is desired outcome error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow.rs:58:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 65535u16 + | ^^^^ overflowed on value after 65535 | - = note: explicitly set `OhNo = 0u16` if that is desired outcome + = note: explicitly set `OhNo = 0` if that is desired outcome error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow.rs:70:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 2147483647i32 + | ^^^^ overflowed on value after 2147483647 | - = note: explicitly set `OhNo = -2147483648i32` if that is desired outcome + = note: explicitly set `OhNo = -2147483648` if that is desired outcome error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow.rs:82:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 4294967295u32 + | ^^^^ overflowed on value after 4294967295 | - = note: explicitly set `OhNo = 0u32` if that is desired outcome + = note: explicitly set `OhNo = 0` if that is desired outcome error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow.rs:94:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 9223372036854775807i64 + | ^^^^ overflowed on value after 9223372036854775807 | - = note: explicitly set `OhNo = -9223372036854775808i64` if that is desired outcome + = note: explicitly set `OhNo = -9223372036854775808` if that is desired outcome error[E0370]: enum discriminant overflowed --> $DIR/discrim-overflow.rs:106:9 | LL | OhNo, //~ ERROR enum discriminant overflowed [E0370] - | ^^^^ overflowed on value after 18446744073709551615u64 + | ^^^^ overflowed on value after 18446744073709551615 | - = note: explicitly set `OhNo = 0u64` if that is desired outcome + = note: explicitly set `OhNo = 0` if that is desired outcome error: aborting due to 8 previous errors diff --git a/src/test/ui/error-codes/E0080.rs b/src/test/ui/error-codes/E0080.rs index 2f199c48e46..c8e42571128 100644 --- a/src/test/ui/error-codes/E0080.rs +++ b/src/test/ui/error-codes/E0080.rs @@ -10,9 +10,12 @@ 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 + //~| const_err + //~| const_err + //~| divide by zero } fn main() { diff --git a/src/test/ui/error-codes/E0080.stderr b/src/test/ui/error-codes/E0080.stderr index 01cfa0c375b..6db53acd6b9 100644 --- a/src/test/ui/error-codes/E0080.stderr +++ b/src/test/ui/error-codes/E0080.stderr @@ -1,10 +1,10 @@ -warning: constant evaluation error: attempt to shift left with overflow +error: attempt to shift left with overflow --> $DIR/E0080.rs:12:9 | LL | X = (1 << 500), //~ ERROR E0080 | ^^^^^^^^^^ | - = note: #[warn(const_err)] on by default + = note: #[deny(exceeding_bitshifts)] on by default error[E0080]: constant evaluation error --> $DIR/E0080.rs:12:9 @@ -12,11 +12,19 @@ error[E0080]: constant evaluation error LL | X = (1 << 500), //~ ERROR E0080 | ^^^^^^^^^^ attempt to shift left with overflow -warning: constant evaluation error: attempt to divide by zero +warning: attempt to divide by zero --> $DIR/E0080.rs:14:9 | LL | Y = (1 / 0) //~ ERROR E0080 | ^^^^^^^ + | + = note: #[warn(const_err)] on by default + +warning: constant evaluation error + --> $DIR/E0080.rs:14:9 + | +LL | Y = (1 / 0) //~ ERROR E0080 + | ^^^^^^^ attempted to do overflowing math error[E0080]: constant evaluation error --> $DIR/E0080.rs:14:9 @@ -24,6 +32,6 @@ error[E0080]: constant evaluation error LL | Y = (1 / 0) //~ ERROR E0080 | ^^^^^^^ attempt to divide by zero -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors If you want more information on this error, try using "rustc --explain E0080" diff --git a/src/test/ui/error-codes/E0081.rs b/src/test/ui/error-codes/E0081.rs index 3b571667336..c0cdad25868 100644 --- a/src/test/ui/error-codes/E0081.rs +++ b/src/test/ui/error-codes/E0081.rs @@ -11,7 +11,7 @@ enum Enum { P = 3, X = 3, - //~^ ERROR discriminant value `3isize` already exists + //~^ ERROR discriminant value `3` already exists Y = 5 } diff --git a/src/test/ui/error-codes/E0081.stderr b/src/test/ui/error-codes/E0081.stderr index 42d0769bb57..e1133c1fc58 100644 --- a/src/test/ui/error-codes/E0081.stderr +++ b/src/test/ui/error-codes/E0081.stderr @@ -1,10 +1,10 @@ -error[E0081]: discriminant value `3isize` already exists +error[E0081]: discriminant value `3` already exists --> $DIR/E0081.rs:13:9 | LL | P = 3, - | - first use of `3isize` + | - first use of `3` LL | X = 3, - | ^ enum already has `3isize` + | ^ enum already has `3` error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0370.stderr b/src/test/ui/error-codes/E0370.stderr index 1c7a18bd6d8..60e077e063e 100644 --- a/src/test/ui/error-codes/E0370.stderr +++ b/src/test/ui/error-codes/E0370.stderr @@ -2,9 +2,9 @@ error[E0370]: enum discriminant overflowed --> $DIR/E0370.rs:17:5 | LL | Y, //~ ERROR E0370 - | ^ overflowed on value after 9223372036854775807i64 + | ^ overflowed on value after 9223372036854775807 | - = note: explicitly set `Y = -9223372036854775808i64` if that is desired outcome + = note: explicitly set `Y = -9223372036854775808` if that is desired outcome error: aborting due to previous error diff --git a/src/test/ui/feature-gate-const-indexing.rs b/src/test/ui/feature-gate-const-indexing.rs index 0d61878cd80..eb5f746774c 100644 --- a/src/test/ui/feature-gate-const-indexing.rs +++ b/src/test/ui/feature-gate-const-indexing.rs @@ -8,10 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// must-compile-successfully fn main() { const ARR: [i32; 6] = [42, 43, 44, 45, 46, 47]; const IDX: usize = 3; const VAL: i32 = ARR[IDX]; - const BLUB: [i32; (ARR[0] - 41) as usize] = [5]; //~ ERROR constant evaluation error + const BLUB: [i32; (ARR[0] - 41) as usize] = [5]; } diff --git a/src/test/ui/feature-gate-const-indexing.stderr b/src/test/ui/feature-gate-const-indexing.stderr index 419400bb0ac..e69de29bb2d 100644 --- a/src/test/ui/feature-gate-const-indexing.stderr +++ b/src/test/ui/feature-gate-const-indexing.stderr @@ -1,9 +0,0 @@ -error[E0080]: constant evaluation error - --> $DIR/feature-gate-const-indexing.rs:16:24 - | -LL | const BLUB: [i32; (ARR[0] - 41) as usize] = [5]; //~ ERROR constant evaluation error - | ^^^^^^ the index operation on const values is unstable - -error: aborting due to previous error - -If you want more information on this error, try using "rustc --explain E0080" diff --git a/src/test/ui/infinite-recursion-const-fn.rs b/src/test/ui/infinite-recursion-const-fn.rs new file mode 100644 index 00000000000..f98074bc554 --- /dev/null +++ b/src/test/ui/infinite-recursion-const-fn.rs @@ -0,0 +1,18 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//https://github.com/rust-lang/rust/issues/31364 + +#![feature(const_fn)] +const fn a() -> usize { b() } //~ ERROR constant evaluation error +const fn b() -> usize { a() } +const ARR: [i32; a()] = [5; 6]; + +fn main(){} diff --git a/src/test/ui/infinite-recursion-const-fn.stderr b/src/test/ui/infinite-recursion-const-fn.stderr new file mode 100644 index 00000000000..bc062ecce25 --- /dev/null +++ b/src/test/ui/infinite-recursion-const-fn.stderr @@ -0,0 +1,72 @@ +error[E0080]: constant evaluation error + --> $DIR/infinite-recursion-const-fn.rs:14:25 + | +LL | const fn a() -> usize { b() } //~ ERROR constant evaluation error + | ^^^ + | | + | reached the configured maximum number of stack frames + | inside call to `b` +LL | const fn b() -> usize { a() } + | --- + | | + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` + | inside call to `a` +LL | const ARR: [i32; a()] = [5; 6]; + | --- inside call to `a` + | +note: for constant expression here + --> $DIR/infinite-recursion-const-fn.rs:16:1 + | +LL | const ARR: [i32; a()] = [5; 6]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +If you want more information on this error, try using "rustc --explain E0080" diff --git a/src/test/ui/issue-15524.rs b/src/test/ui/issue-15524.rs index 85214bd8633..7e696c97595 100644 --- a/src/test/ui/issue-15524.rs +++ b/src/test/ui/issue-15524.rs @@ -13,13 +13,13 @@ const N: isize = 1; enum Foo { A = 1, B = 1, - //~^ ERROR discriminant value `1isize` already exists + //~^ ERROR discriminant value `1` already exists C = 0, D, - //~^ ERROR discriminant value `1isize` already exists + //~^ ERROR discriminant value `1` already exists E = N, - //~^ ERROR discriminant value `1isize` already exists + //~^ ERROR discriminant value `1` already exists } diff --git a/src/test/ui/issue-15524.stderr b/src/test/ui/issue-15524.stderr index 1cb16dfe19a..e26766eca37 100644 --- a/src/test/ui/issue-15524.stderr +++ b/src/test/ui/issue-15524.stderr @@ -1,28 +1,28 @@ -error[E0081]: discriminant value `1isize` already exists +error[E0081]: discriminant value `1` already exists --> $DIR/issue-15524.rs:15:9 | LL | A = 1, - | - first use of `1isize` + | - first use of `1` LL | B = 1, - | ^ enum already has `1isize` + | ^ enum already has `1` -error[E0081]: discriminant value `1isize` already exists +error[E0081]: discriminant value `1` already exists --> $DIR/issue-15524.rs:18:5 | LL | A = 1, - | - first use of `1isize` + | - first use of `1` ... LL | D, - | ^ enum already has `1isize` + | ^ enum already has `1` -error[E0081]: discriminant value `1isize` already exists +error[E0081]: discriminant value `1` already exists --> $DIR/issue-15524.rs:21:9 | LL | A = 1, - | - first use of `1isize` + | - first use of `1` ... LL | E = N, - | ^ enum already has `1isize` + | ^ enum already has `1` error: aborting due to 3 previous errors diff --git a/src/test/ui/issue-23302-1.stderr b/src/test/ui/issue-23302-1.stderr index 13a2a23cc52..087eae43a29 100644 --- a/src/test/ui/issue-23302-1.stderr +++ b/src/test/ui/issue-23302-1.stderr @@ -5,10 +5,15 @@ LL | A = X::A as isize, //~ ERROR E0391 | ^^^^^^^^^^^^^ cyclic reference | note: the cycle begins when const-evaluating `X::A::{{initializer}}`... - --> $DIR/issue-23302-1.rs:14:5 + --> $DIR/issue-23302-1.rs:14:9 | LL | A = X::A as isize, //~ ERROR E0391 - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ +note: ...which then requires computing layout of `X`... + --> $DIR/issue-23302-1.rs:14:9 + | +LL | A = X::A as isize, //~ ERROR E0391 + | ^^^^ = note: ...which then again requires const-evaluating `X::A::{{initializer}}`, completing the cycle. error: aborting due to previous error diff --git a/src/test/ui/issue-23302-2.stderr b/src/test/ui/issue-23302-2.stderr index f303fa7c671..66ba5c32582 100644 --- a/src/test/ui/issue-23302-2.stderr +++ b/src/test/ui/issue-23302-2.stderr @@ -5,10 +5,15 @@ LL | A = Y::B as isize, //~ ERROR E0391 | ^^^^^^^^^^^^^ cyclic reference | note: the cycle begins when const-evaluating `Y::A::{{initializer}}`... - --> $DIR/issue-23302-2.rs:14:5 + --> $DIR/issue-23302-2.rs:14:9 | LL | A = Y::B as isize, //~ ERROR E0391 - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ +note: ...which then requires computing layout of `Y`... + --> $DIR/issue-23302-2.rs:14:9 + | +LL | A = Y::B as isize, //~ ERROR E0391 + | ^^^^ = note: ...which then again requires const-evaluating `Y::A::{{initializer}}`, completing the cycle. error: aborting due to previous error diff --git a/src/test/ui/issue-23302-3.rs b/src/test/ui/issue-23302-3.rs index 1d750b09025..5903acc8b7a 100644 --- a/src/test/ui/issue-23302-3.rs +++ b/src/test/ui/issue-23302-3.rs @@ -8,8 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -const A: i32 = B; //~ ERROR E0391 +const A: i32 = B; -const B: i32 = A; +const B: i32 = A; //~ ERROR cyclic dependency detected fn main() { } diff --git a/src/test/ui/issue-23302-3.stderr b/src/test/ui/issue-23302-3.stderr index ec615809749..31168579394 100644 --- a/src/test/ui/issue-23302-3.stderr +++ b/src/test/ui/issue-23302-3.stderr @@ -1,20 +1,30 @@ error[E0391]: cyclic dependency detected - --> $DIR/issue-23302-3.rs:11:16 - | -LL | const A: i32 = B; //~ ERROR E0391 - | ^ cyclic reference - | -note: the cycle begins when processing `B`... - --> $DIR/issue-23302-3.rs:13:1 - | -LL | const B: i32 = A; - | ^^^^^^^^^^^^^^^^^ -note: ...which then requires processing `A`... --> $DIR/issue-23302-3.rs:13:16 | -LL | const B: i32 = A; +LL | const B: i32 = A; //~ ERROR cyclic dependency detected + | ^ cyclic reference + | +note: the cycle begins when const checking if rvalue is promotable to static `A`... + --> $DIR/issue-23302-3.rs:11:1 + | +LL | const A: i32 = B; + | ^^^^^^^^^^^^^^^^^ +note: ...which then requires checking which parts of `A` are promotable to static... + --> $DIR/issue-23302-3.rs:11:1 + | +LL | const A: i32 = B; + | ^^^^^^^^^^^^^^^^^ +note: ...which then requires const checking if rvalue is promotable to static `B`... + --> $DIR/issue-23302-3.rs:11:16 + | +LL | const A: i32 = B; | ^ - = note: ...which then again requires processing `B`, completing the cycle. +note: ...which then requires checking which parts of `B` are promotable to static... + --> $DIR/issue-23302-3.rs:13:1 + | +LL | const B: i32 = A; //~ ERROR cyclic dependency detected + | ^^^^^^^^^^^^^^^^^ + = note: ...which then again requires const checking if rvalue is promotable to static `A`, completing the cycle. error: aborting due to previous error diff --git a/src/test/ui/issue-36163.stderr b/src/test/ui/issue-36163.stderr index c511b576a21..4323eb4858f 100644 --- a/src/test/ui/issue-36163.stderr +++ b/src/test/ui/issue-36163.stderr @@ -5,15 +5,25 @@ LL | B = A, //~ ERROR E0391 | ^ cyclic reference | note: the cycle begins when const-evaluating `Foo::B::{{initializer}}`... - --> $DIR/issue-36163.rs:14:5 - | -LL | B = A, //~ ERROR E0391 - | ^^^^^ -note: ...which then requires const-evaluating `A`... --> $DIR/issue-36163.rs:14:9 | LL | B = A, //~ ERROR E0391 | ^ +note: ...which then requires processing `Foo::B::{{initializer}}`... + --> $DIR/issue-36163.rs:14:9 + | +LL | B = A, //~ ERROR E0391 + | ^ +note: ...which then requires const-evaluating `A`... + --> $DIR/issue-36163.rs:11:1 + | +LL | const A: isize = Foo::B as isize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which then requires computing layout of `Foo`... + --> $DIR/issue-36163.rs:11:18 + | +LL | const A: isize = Foo::B as isize; + | ^^^^^^ = note: ...which then again requires const-evaluating `Foo::B::{{initializer}}`, completing the cycle. error: aborting due to previous error diff --git a/src/test/ui/issue-38875/issue_38875.rs b/src/test/ui/issue-38875/issue_38875.rs index 42e3c05a38c..24cd20a84a9 100644 --- a/src/test/ui/issue-38875/issue_38875.rs +++ b/src/test/ui/issue-38875/issue_38875.rs @@ -9,6 +9,7 @@ // except according to those terms. // aux-build:issue_38875_b.rs +// must-compile-successfully extern crate issue_38875_b; diff --git a/src/test/ui/issue-38875/issue_38875.stderr b/src/test/ui/issue-38875/issue_38875.stderr index 9a412be6a67..e69de29bb2d 100644 --- a/src/test/ui/issue-38875/issue_38875.stderr +++ b/src/test/ui/issue-38875/issue_38875.stderr @@ -1,15 +0,0 @@ -error[E0080]: constant evaluation error - --> $DIR/auxiliary/issue_38875_b.rs:11:24 - | -LL | pub const FOO: usize = *&0; - | ^^^ unimplemented constant expression: deref operation - | -note: for constant expression here - --> $DIR/issue_38875.rs:16:22 - | -LL | let test_x = [0; issue_38875_b::FOO]; - | ^^^^^^^^^^^^^^^^^^ - -error: aborting due to previous error - -If you want more information on this error, try using "rustc --explain E0080" diff --git a/src/test/ui/union/union-const-eval.rs b/src/test/ui/union/union-const-eval.rs index a4c969ba20c..aeafb45e6a5 100644 --- a/src/test/ui/union/union-const-eval.rs +++ b/src/test/ui/union/union-const-eval.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// must-compile-successfully + union U { a: usize, b: usize, @@ -16,9 +18,6 @@ union U { const C: U = U { a: 10 }; fn main() { - unsafe { - let a: [u8; C.a]; // OK - let b: [u8; C.b]; //~ ERROR constant evaluation error - //~| WARNING constant evaluation error - } + let a: [u8; unsafe { C.a }]; + let b: [u8; unsafe { C.b }]; } diff --git a/src/test/ui/union/union-const-eval.stderr b/src/test/ui/union/union-const-eval.stderr index afd661337c6..e69de29bb2d 100644 --- a/src/test/ui/union/union-const-eval.stderr +++ b/src/test/ui/union/union-const-eval.stderr @@ -1,17 +0,0 @@ -warning: constant evaluation error: nonexistent struct field - --> $DIR/union-const-eval.rs:21:21 - | -LL | let b: [u8; C.b]; //~ ERROR constant evaluation error - | ^^^ - | - = note: #[warn(const_err)] on by default - -error[E0080]: constant evaluation error - --> $DIR/union-const-eval.rs:21:21 - | -LL | let b: [u8; C.b]; //~ ERROR constant evaluation error - | ^^^ nonexistent struct field - -error: aborting due to previous error - -If you want more information on this error, try using "rustc --explain E0080"