diff options
| author | bors <bors@rust-lang.org> | 2018-03-08 08:52:23 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2018-03-08 08:52:23 +0000 |
| commit | c90f68224b069f5bb2a80e30e2737e4bb17c1466 (patch) | |
| tree | f2f6522832596d658d2644411abfcbdd1840b829 /src | |
| parent | cdcca786468a71375584bb48e3093790c91084f7 (diff) | |
| parent | 52dec0e1c9e0fe5bbbe81385531e69c2c46ef56e (diff) | |
Auto merge of #46882 - oli-obk:miri3, r=eddyb
Replace all const evaluation with miri
* error reporting in constants prints a stacktrace through all called const fns
* Trivial constant propagation and folding in MIR (always active, irrelevant of the optimization level)
* can now use floating constants in patterns (previously only floating point literals were allowed)
* the future compat lint is still produced for both cases
* can index into constant arrays during const eval (previously feature gated)
* can create a constant union value with field `a` and read from field `b`
* can dereference references into constants
* can create references inside constants (`const X: &u32 = &22`)
* Tuple struct constructors can be used in constants
* regression in const eval errors spans (some of these need improvements in mir debug info)
* can cast floats to ints and vice versa (in constants, and even nan/inf constants)
* Mir dump prints false/true instead of 0u8/1u8
* `1i8 >> [8][0]` does not lint about exceeding bitshifts anymore.
* Needs const propagation across projections
* `foo[I]` produces a const eval lint if `foo: [T; N]` and `N < I`
* Essentially all builtin panics produce lints if they can be statically proven to trigger at runtime. This is on a best effort basis, so there might be some complex cases that don't trigger. (The runtime panic stays there, irrelevant of whether the lint is produced or not)
* can use `union`s to implement `transmute` for `Copy` types in constants without a feature gate. With all the greatness and nasal demons that come with this.
* can convert integers to `&'static T` in constants (useful for embedded)
fixes #34997 (stack overflow with many constants)
fixes #25574 (deref byte strings in patterns)
fixes #27918 (broken mir ICE)
fixes #46114 (ICE on struct constructors in patterns)
fixes #37448 (`SomeStruct { foo } as SomeStruct`)
fixes #43754 (`return` in const fn)
fixes #41898 (tuple struct constructors)
fixes #31364 (infinite recursion with const fn, fixed by miri's recursion limit)
closes #29947 (const indexing stabilization)
fixes #45044 (pattern matching repeat expressions)
fixes #47971 (ICE on const fn + references)
fixes #48081 (ICE on cyclic assoc const error)
fixes #48746 (nonhelpful error message with unions)
r? @eddyb
even though 1k loc are added in tests, this PR reduces the loc in this repository by 700
Diffstat (limited to 'src')
251 files changed, 6638 insertions, 7166 deletions
diff --git a/src/Cargo.lock b/src/Cargo.lock index b70a01ebb3c..34c077a6f7a 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -1859,20 +1859,6 @@ dependencies = [ ] [[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" dependencies = [ @@ -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<T: Ord> Ord for Reverse<T> { /// } /// } /// ``` +#[cfg_attr(not(stage0), lang = "ord")] #[stable(feature = "rust1", since = "1.0.0")] pub trait Ord: Eq + PartialOrd<Self> { /// 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<Rhs: ?Sized = Self>: PartialEq<Rhs> { 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<StableHashingContext<'gcx>> + fmt::Debug + where T: HashStable<StableHashingContext<'a>> + 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<Trait>` 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<T> { hash_bodies: bool, } -impl<'hir, T> HashStable<StableHashingContext<'hir>> for HirItemLike<T> +impl<'a, 'hir, T> HashStable<StableHashingContext<'hir>> for HirItemLike<T> where T: HashStable<StableHashingContext<'hir>> { fn hash_stable<W: StableHasherResult>(&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<Symbol> { /// 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<CachingCodemapView<'gcx>>, + raw_codemap: &'a CodeMap, + caching_codemap: Option<CachingCodemapView<'a>>, } #[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<StableHashingContext<'gcx>> for hir::BodyId { +impl<'a> HashStable<StableHashingContext<'a>> for hir::BodyId { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { if hcx.hash_bodies() { hcx.body_resolver.body(*self).hash_stable(hcx, hasher); @@ -221,10 +221,10 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::BodyId { } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::HirId { +impl<'a> HashStable<StableHashingContext<'a>> for hir::HirId { #[inline] fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { match hcx.node_id_hashing_mode { NodeIdHashingMode::Ignore => { @@ -243,21 +243,21 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::HirId { } } -impl<'gcx> ToStableHashKey<StableHashingContext<'gcx>> for hir::HirId { +impl<'a> ToStableHashKey<StableHashingContext<'a>> 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<StableHashingContext<'gcx>> for ast::NodeId { +impl<'a> HashStable<StableHashingContext<'a>> for ast::NodeId { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { match hcx.node_id_hashing_mode { NodeIdHashingMode::Ignore => { @@ -270,18 +270,18 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for ast::NodeId { } } -impl<'gcx> ToStableHashKey<StableHashingContext<'gcx>> for ast::NodeId { +impl<'a> ToStableHashKey<StableHashingContext<'a>> 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<StableHashingContext<'gcx>> for Span { +impl<'a> HashStable<StableHashingContext<'a>> 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<StableHashingContext<'gcx>> 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<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { const TAG_VALID_SPAN: u8 = 0; const TAG_INVALID_SPAN: u8 = 1; @@ -373,8 +373,8 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> 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<W>, blanket_impls: &Vec<DefId>, non_blanket_impls: &HashMap<fast_reject::SimplifiedType, Vec<DefId>, 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<StableHashingContext<'gcx>> for DefId { +impl<'a> HashStable<StableHashingContext<'a>> for DefId { #[inline] fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { hcx.def_path_hash(*self).hash_stable(hcx, hasher); } } -impl<'gcx> ToStableHashKey<StableHashingContext<'gcx>> for DefId { +impl<'a> ToStableHashKey<StableHashingContext<'a>> 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<StableHashingContext<'gcx>> for LocalDefId { +impl<'a> HashStable<StableHashingContext<'a>> for LocalDefId { #[inline] fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { hcx.def_path_hash(self.to_def_id()).hash_stable(hcx, hasher); } } -impl<'gcx> ToStableHashKey<StableHashingContext<'gcx>> for LocalDefId { +impl<'a> ToStableHashKey<StableHashingContext<'a>> 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<StableHashingContext<'gcx>> for CrateNum { +impl<'a> HashStable<StableHashingContext<'a>> for CrateNum { #[inline] fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { hcx.def_path_hash(DefId { krate: *self, @@ -69,11 +69,11 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for CrateNum { } } -impl<'gcx> ToStableHashKey<StableHashingContext<'gcx>> for CrateNum { +impl<'a> ToStableHashKey<StableHashingContext<'a>> 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<StableHashingContext<'gcx>> for CrateNum { impl_stable_hash_for!(tuple_struct hir::ItemLocalId { index }); -impl<'gcx> ToStableHashKey<StableHashingContext<'gcx>> +impl<'a> ToStableHashKey<StableHashingContext<'a>> 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<StableHashingContext<'gcx>> for hir::ItemId { +impl<'a> HashStable<StableHashingContext<'a>> for hir::ItemId { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { let hir::ItemId { id @@ -114,9 +114,9 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::ItemId { } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::TraitItemId { +impl<'a> HashStable<StableHashingContext<'a>> for hir::TraitItemId { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { let hir::TraitItemId { node_id @@ -128,9 +128,9 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::TraitItemId { } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::ImplItemId { +impl<'a> HashStable<StableHashingContext<'a>> for hir::ImplItemId { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { let hir::ImplItemId { node_id @@ -271,9 +271,9 @@ impl_stable_hash_for!(struct hir::TypeBinding { span }); -impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::Ty { +impl<'a> HashStable<StableHashingContext<'a>> for hir::Ty { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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<StableHashingContext<'gcx>> for hir::TraitRef { +impl<'a> HashStable<StableHashingContext<'a>> for hir::TraitRef { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { let hir::TraitRef { ref path, @@ -376,9 +376,9 @@ impl_stable_hash_for!(struct hir::MacroDef { }); -impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::Block { +impl<'a> HashStable<StableHashingContext<'a>> for hir::Block { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { let hir::Block { ref stmts, @@ -400,9 +400,9 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::Block { } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::Pat { +impl<'a> HashStable<StableHashingContext<'a>> for hir::Pat { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { let hir::Pat { id: _, @@ -527,9 +527,9 @@ impl_stable_hash_for!(enum hir::UnsafeSource { UserProvided }); -impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::Expr { +impl<'a> HashStable<StableHashingContext<'a>> for hir::Expr { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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<StableHashingContext<'gcx>> for hir::MatchSource { +impl<'a> HashStable<StableHashingContext<'a>> for hir::MatchSource { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { use hir::MatchSource; @@ -647,9 +647,9 @@ impl_stable_hash_for!(enum hir::ScopeTarget { Loop(loop_id_result) }); -impl<'gcx> HashStable<StableHashingContext<'gcx>> for ast::Ident { +impl<'a> HashStable<StableHashingContext<'a>> for ast::Ident { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { let ast::Ident { ref name, @@ -660,9 +660,9 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for ast::Ident { } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::TraitItem { +impl<'a> HashStable<StableHashingContext<'a>> for hir::TraitItem { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { let hir::TraitItem { id: _, @@ -695,9 +695,9 @@ impl_stable_hash_for!(enum hir::TraitItemKind { Type(bounds, rhs) }); -impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::ImplItem { +impl<'a> HashStable<StableHashingContext<'a>> for hir::ImplItem { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { let hir::ImplItem { id: _, @@ -729,9 +729,9 @@ impl_stable_hash_for!(enum hir::ImplItemKind { Type(t) }); -impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::Visibility { +impl<'a> HashStable<StableHashingContext<'a>> for hir::Visibility { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -750,9 +750,9 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::Visibility { } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::Defaultness { +impl<'a> HashStable<StableHashingContext<'a>> for hir::Defaultness { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -771,9 +771,9 @@ impl_stable_hash_for!(enum hir::ImplPolarity { Negative }); -impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::Mod { +impl<'a> HashStable<StableHashingContext<'a>> for hir::Mod { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { let hir::Mod { inner, @@ -826,9 +826,9 @@ impl_stable_hash_for!(enum hir::VariantData { Unit(id) }); -impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::Item { +impl<'a> HashStable<StableHashingContext<'a>> for hir::Item { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { let hir::Item { name, @@ -885,10 +885,10 @@ impl_stable_hash_for!(struct hir::ImplItemRef { defaultness }); -impl<'gcx> HashStable<StableHashingContext<'gcx>> +impl<'a> HashStable<StableHashingContext<'a>> for hir::AssociatedItemKind { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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<StableHashingContext<'gcx>> for hir::Body { +impl<'a> HashStable<StableHashingContext<'a>> for hir::Body { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { let hir::Body { ref arguments, @@ -948,12 +948,12 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::Body { } } -impl<'gcx> ToStableHashKey<StableHashingContext<'gcx>> for hir::BodyId { +impl<'a> ToStableHashKey<StableHashingContext<'a>> 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<StableHashingContext<'gcx>> for hir::GlobalAsm { +impl<'a> HashStable<StableHashingContext<'a>> for hir::GlobalAsm { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { let hir::GlobalAsm { asm, @@ -979,9 +979,9 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::GlobalAsm { } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::InlineAsm { +impl<'a> HashStable<StableHashingContext<'a>> for hir::InlineAsm { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { let hir::InlineAsm { asm, @@ -1062,22 +1062,22 @@ impl_stable_hash_for!(enum hir::Constness { NotConst }); -impl<'gcx> HashStable<StableHashingContext<'gcx>> +impl<'a> HashStable<StableHashingContext<'a>> for hir::def_id::DefIndex { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { hcx.local_def_path_hash(*self).hash_stable(hcx, hasher); } } -impl<'gcx> ToStableHashKey<StableHashingContext<'gcx>> +impl<'a> ToStableHashKey<StableHashingContext<'a>> 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<StableHashingContext<'gcx>> +impl<'a> HashStable<StableHashingContext<'a>> for ::middle::lang_items::LangItem { fn hash_stable<W: StableHasherResult>(&self, - _: &mut StableHashingContext<'gcx>, + _: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { ::std::hash::Hash::hash(self, hasher); } @@ -1104,10 +1104,10 @@ impl_stable_hash_for!(struct ::middle::lang_items::LanguageItems { missing }); -impl<'gcx> HashStable<StableHashingContext<'gcx>> +impl<'a> HashStable<StableHashingContext<'a>> for hir::TraitCandidate { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { let hir::TraitCandidate { @@ -1121,11 +1121,11 @@ for hir::TraitCandidate { } } -impl<'gcx> ToStableHashKey<StableHashingContext<'gcx>> for hir::TraitCandidate { +impl<'a> ToStableHashKey<StableHashingContext<'a>> 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<StableHashingContext<'gcx>> +impl<'a> HashStable<StableHashingContext<'a>> for mir::BorrowKind { #[inline] fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -54,11 +54,11 @@ for mir::BorrowKind { } -impl<'gcx> HashStable<StableHashingContext<'gcx>> +impl<'a> HashStable<StableHashingContext<'a>> for mir::UnsafetyViolationKind { #[inline] fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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<StableHashingContext<'gcx>> for mir::ClearCrossCrate<T> - where T: HashStable<StableHashingContext<'gcx>> +impl<'a, 'gcx, T> HashStable<StableHashingContext<'a>> for mir::ClearCrossCrate<T> + where T: HashStable<StableHashingContext<'a>> { #[inline] fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -96,61 +96,61 @@ impl<'gcx, T> HashStable<StableHashingContext<'gcx>> for mir::ClearCrossCrate<T> } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> for mir::Local { +impl<'a> HashStable<StableHashingContext<'a>> for mir::Local { #[inline] fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { use rustc_data_structures::indexed_vec::Idx; self.index().hash_stable(hcx, hasher); } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> for mir::BasicBlock { +impl<'a> HashStable<StableHashingContext<'a>> for mir::BasicBlock { #[inline] fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { use rustc_data_structures::indexed_vec::Idx; self.index().hash_stable(hcx, hasher); } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> for mir::Field { +impl<'a> HashStable<StableHashingContext<'a>> for mir::Field { #[inline] fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { use rustc_data_structures::indexed_vec::Idx; self.index().hash_stable(hcx, hasher); } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> +impl<'a> HashStable<StableHashingContext<'a>> for mir::VisibilityScope { #[inline] fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { use rustc_data_structures::indexed_vec::Idx; self.index().hash_stable(hcx, hasher); } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> for mir::Promoted { +impl<'a> HashStable<StableHashingContext<'a>> for mir::Promoted { #[inline] fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { use rustc_data_structures::indexed_vec::Idx; self.index().hash_stable(hcx, hasher); } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> +impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for mir::TerminatorKind<'gcx> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -227,10 +227,10 @@ for mir::TerminatorKind<'gcx> { } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> +impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for mir::AssertMessage<'gcx> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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<StableHashingContext<'gcx>> +impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for mir::StatementKind<'gcx> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -287,12 +287,12 @@ for mir::StatementKind<'gcx> { } } -impl<'gcx, T> HashStable<StableHashingContext<'gcx>> +impl<'a, 'gcx, T> HashStable<StableHashingContext<'a>> for mir::ValidationOperand<'gcx, T> - where T: HashStable<StableHashingContext<'gcx>> + where T: HashStable<StableHashingContext<'a>> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { self.place.hash_stable(hcx, hasher); @@ -304,9 +304,9 @@ impl<'gcx, T> HashStable<StableHashingContext<'gcx>> impl_stable_hash_for!(enum mir::ValidationOp { Acquire, Release, Suspend(region_scope) }); -impl<'gcx> HashStable<StableHashingContext<'gcx>> for mir::Place<'gcx> { +impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for mir::Place<'gcx> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -323,14 +323,14 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for mir::Place<'gcx> { } } -impl<'gcx, B, V, T> HashStable<StableHashingContext<'gcx>> +impl<'a, 'gcx, B, V, T> HashStable<StableHashingContext<'a>> for mir::Projection<'gcx, B, V, T> - where B: HashStable<StableHashingContext<'gcx>>, - V: HashStable<StableHashingContext<'gcx>>, - T: HashStable<StableHashingContext<'gcx>> + where B: HashStable<StableHashingContext<'a>>, + V: HashStable<StableHashingContext<'a>>, + T: HashStable<StableHashingContext<'a>> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { let mir::Projection { ref base, @@ -342,13 +342,13 @@ for mir::Projection<'gcx, B, V, T> } } -impl<'gcx, V, T> HashStable<StableHashingContext<'gcx>> +impl<'a, 'gcx, V, T> HashStable<StableHashingContext<'a>> for mir::ProjectionElem<'gcx, V, T> - where V: HashStable<StableHashingContext<'gcx>>, - T: HashStable<StableHashingContext<'gcx>> + where V: HashStable<StableHashingContext<'a>>, + T: HashStable<StableHashingContext<'a>> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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<StableHashingContext<'gcx>> for mir::Safety { +impl<'a> HashStable<StableHashingContext<'a>> for mir::Safety { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -399,9 +399,9 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for mir::Safety { } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> for mir::Operand<'gcx> { +impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for mir::Operand<'gcx> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -419,9 +419,9 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for mir::Operand<'gcx> { } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> for mir::Rvalue<'gcx> { +impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for mir::Rvalue<'gcx> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -479,10 +479,10 @@ impl_stable_hash_for!(enum mir::CastKind { Unsize }); -impl<'gcx> HashStable<StableHashingContext<'gcx>> +impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for mir::AggregateKind<'gcx> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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<StableHashingContext<'gcx>> for mir::Literal<'gcx> { +impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for mir::Literal<'gcx> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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<StableHashingContext<'gcx>> for mir::ClosureOutlivesSubject<'gcx> { +impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for mir::ClosureOutlivesSubject<'gcx> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -585,3 +585,5 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> 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<StableHashingContext<'gcx>> for InternedString { +impl<'a> HashStable<StableHashingContext<'a>> for InternedString { #[inline] fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { let s: &str = &**self; s.hash_stable(hcx, hasher); } } -impl<'gcx> ToStableHashKey<StableHashingContext<'gcx>> for InternedString { +impl<'a> ToStableHashKey<StableHashingContext<'a>> for InternedString { type KeyType = InternedString; #[inline] fn to_stable_hash_key(&self, - _: &StableHashingContext<'gcx>) + _: &StableHashingContext<'a>) -> InternedString { self.clone() } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> for ast::Name { +impl<'a> HashStable<StableHashingContext<'a>> for ast::Name { #[inline] fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { self.as_str().hash_stable(hcx, hasher); } } -impl<'gcx> ToStableHashKey<StableHashingContext<'gcx>> for ast::Name { +impl<'a> ToStableHashKey<StableHashingContext<'a>> 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<StableHashingContext<'gcx>> +impl<'a> HashStable<StableHashingContext<'a>> for ::syntax::attr::StabilityLevel { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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<StableHashingContext<'gcx>> for [ast::Attribute] { +impl<'a> HashStable<StableHashingContext<'a>> for [ast::Attribute] { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { if self.len() == 0 { self.len().hash_stable(hcx, hasher); @@ -191,9 +191,9 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for [ast::Attribute] { } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> for ast::Attribute { +impl<'a> HashStable<StableHashingContext<'a>> for ast::Attribute { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { // 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<StableHashingContext<'gcx>> for ast::Attribute { } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> +impl<'a> HashStable<StableHashingContext<'a>> for tokenstream::TokenTree { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -242,10 +242,10 @@ for tokenstream::TokenTree { } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> +impl<'a> HashStable<StableHashingContext<'a>> for tokenstream::TokenStream { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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<W>) { +fn hash_token<'a, 'gcx, W: StableHasherResult>( + token: &token::Token, + hcx: &mut StableHashingContext<'a>, + hasher: &mut StableHasher<W>, +) { 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<StableHashingContext<'gcx>> for FileMap { +impl<'a> HashStable<StableHashingContext<'a>> for FileMap { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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<StableHashingContext<'gcx>> +impl<'a, 'gcx, T> HashStable<StableHashingContext<'a>> for &'gcx ty::Slice<T> - where T: HashStable<StableHashingContext<'gcx>> { + where T: HashStable<StableHashingContext<'a>> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { thread_local! { static CACHE: RefCell<FxHashMap<(usize, usize), Fingerprint>> = @@ -51,19 +52,19 @@ for &'gcx ty::Slice<T> } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> +impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for ty::subst::Kind<'gcx> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { self.unpack().hash_stable(hcx, hasher); } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> +impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for ty::subst::UnpackedKind<'gcx> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { match self { ty::subst::UnpackedKind::Lifetime(lt) => lt.hash_stable(hcx, hasher), @@ -72,10 +73,10 @@ for ty::subst::UnpackedKind<'gcx> { } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> +impl<'a> HashStable<StableHashingContext<'a>> for ty::RegionKind { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -119,20 +120,20 @@ for ty::RegionKind { } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> for ty::RegionVid { +impl<'a> HashStable<StableHashingContext<'a>> for ty::RegionVid { #[inline] fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { use rustc_data_structures::indexed_vec::Idx; self.index().hash_stable(hcx, hasher); } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> +impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for ty::adjustment::AutoBorrow<'gcx> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -147,10 +148,10 @@ for ty::adjustment::AutoBorrow<'gcx> { } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> +impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for ty::adjustment::Adjust<'gcx> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -196,10 +197,10 @@ impl_stable_hash_for!(enum ty::BorrowKind { MutBorrow }); -impl<'gcx> HashStable<StableHashingContext<'gcx>> +impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for ty::UpvarCapture<'gcx> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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<StableHashingContext<'gcx>> for ty::Binder<T> - where T: HashStable<StableHashingContext<'gcx>> +impl<'a, 'gcx, T> HashStable<StableHashingContext<'a>> for ty::Binder<T> + where T: HashStable<StableHashingContext<'a>> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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<StableHashingContext<'gcx>> +impl<'a, 'gcx, A, B> HashStable<StableHashingContext<'a>> for ty::OutlivesPredicate<A, B> - where A: HashStable<StableHashingContext<'gcx>>, - B: HashStable<StableHashingContext<'gcx>>, + where A: HashStable<StableHashingContext<'a>>, + B: HashStable<StableHashingContext<'a>>, { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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<StableHashingContext<'gcx>> for ty::Predicate<'gcx> { +impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for ty::Predicate<'gcx> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -304,9 +305,9 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for ty::Predicate<'gcx> { } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> for ty::AdtFlags { +impl<'a> HashStable<StableHashingContext<'a>> for ty::AdtFlags { fn hash_stable<W: StableHasherResult>(&self, - _: &mut StableHashingContext<'gcx>, + _: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { std_hash::Hash::hash(self, hasher); } @@ -331,69 +332,102 @@ impl_stable_hash_for!(struct ty::FieldDef { vis }); -impl<'gcx> HashStable<StableHashingContext<'gcx>> +impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for ::middle::const_val::ConstVal<'gcx> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { use middle::const_val::ConstVal::*; - use middle::const_val::ConstAggregate::*; mem::discriminant(self).hash_stable(hcx, hasher); match *self { - Integral(ref value) => { - value.hash_stable(hcx, hasher); - } - Float(ref value) => { - value.hash_stable(hcx, hasher); - } - Str(ref value) => { - value.hash_stable(hcx, hasher); - } - ByteStr(ref value) => { - value.hash_stable(hcx, hasher); - } - Bool(value) => { - value.hash_stable(hcx, hasher); - } - Char(value) => { - value.hash_stable(hcx, hasher); - } - Variant(def_id) => { - def_id.hash_stable(hcx, hasher); - } - Function(def_id, substs) => { + Unevaluated(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); + substs.hash_stable(hcx, hasher); } - Aggregate(Repeat(ref value, times)) => { + Value(ref value) => { value.hash_stable(hcx, hasher); - times.hash_stable(hcx, hasher); } - Unevaluated(def_id, substs) => { + } + } +} + +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<StableHashingContext<'a>> for mir::interpret::AllocId { + fn hash_stable<W: StableHasherResult>( + &self, + hcx: &mut StableHashingContext<'a>, + hasher: &mut StableHasher<W>, + ) { + 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); - substs.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<StableHashingContext<'a>> for mir::interpret::Allocation { + fn hash_stable<W: StableHasherResult>( + &self, + hcx: &mut StableHashingContext<'a>, + hasher: &mut StableHasher<W>, + ) { + 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!(struct ::middle::const_val::ByteArray<'tcx> { - data +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<StableHashingContext<'gcx>> +impl_stable_hash_for!(struct ::middle::const_val::FrameInfo { + span, + location +}); + +impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for ::middle::const_val::ErrKind<'gcx> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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<StableHashingContext<'a>> +for ::mir::interpret::EvalError<'gcx> { + fn hash_stable<W: StableHasherResult>(&self, + hcx: &mut StableHashingContext<'a>, + hasher: &mut StableHasher<W>) { + 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<StableHashingContext<'gcx>> for ty::Generics { +impl<'a> HashStable<StableHashingContext<'a>> for ty::Generics { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { let ty::Generics { parent, @@ -498,10 +690,10 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for ty::Generics { } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> +impl<'a> HashStable<StableHashingContext<'a>> for ty::RegionParameterDef { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { let ty::RegionParameterDef { name, @@ -527,12 +719,12 @@ impl_stable_hash_for!(struct ty::TypeParameterDef { synthetic }); -impl<'gcx, T> HashStable<StableHashingContext<'gcx>> +impl<'a, 'gcx, T> HashStable<StableHashingContext<'a>> for ::middle::resolve_lifetime::Set1<T> - where T: HashStable<StableHashingContext<'gcx>> + where T: HashStable<StableHashingContext<'a>> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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<StableHashingContext<'gcx>> for region::Scope { +impl<'a> ToStableHashKey<StableHashingContext<'a>> 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<StableHashingContext<'gcx>> +impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for ty::TypeVariants<'gcx> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { use ty::TypeVariants::*; @@ -714,11 +906,11 @@ impl_stable_hash_for!(struct ty::TypeAndMut<'tcx> { mutbl }); -impl<'gcx> HashStable<StableHashingContext<'gcx>> +impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for ty::ExistentialPredicate<'gcx> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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<StableHashingContext<'gcx>> for ty::InstanceDef<'gcx> { +impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for ty::InstanceDef<'gcx> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -775,21 +967,21 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> 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<StableHashingContext<'gcx>> for ty::TraitDef { +impl<'a> HashStable<StableHashingContext<'a>> for ty::TraitDef { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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<StableHashingContext<'gcx>> for ty::CrateVariancesMap { +impl<'a> HashStable<StableHashingContext<'a>> for ty::CrateVariancesMap { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { let ty::CrateVariancesMap { ref variances, @@ -853,12 +1045,12 @@ impl_stable_hash_for!(enum ty::AssociatedItemContainer { }); -impl<'gcx, T> HashStable<StableHashingContext<'gcx>> +impl<'a, 'gcx, T> HashStable<StableHashingContext<'a>> for ty::steal::Steal<T> - where T: HashStable<StableHashingContext<'gcx>> + where T: HashStable<StableHashingContext<'a>> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { self.borrow().hash_stable(hcx, hasher); } @@ -881,10 +1073,10 @@ impl_stable_hash_for!(enum ::middle::privacy::AccessLevel { Public }); -impl<'gcx> HashStable<StableHashingContext<'gcx>> +impl<'a> HashStable<StableHashingContext<'a>> for ::middle::privacy::AccessLevels { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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<StableHashingContext<'gcx>> -for traits::Vtable<'gcx, N> where N: HashStable<StableHashingContext<'gcx>> { +impl<'a, 'gcx, N> HashStable<StableHashingContext<'a>> +for traits::Vtable<'gcx, N> where N: HashStable<StableHashingContext<'a>> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { use traits::Vtable::*; @@ -933,10 +1125,10 @@ for traits::Vtable<'gcx, N> where N: HashStable<StableHashingContext<'gcx>> { } } -impl<'gcx, N> HashStable<StableHashingContext<'gcx>> -for traits::VtableImplData<'gcx, N> where N: HashStable<StableHashingContext<'gcx>> { +impl<'a, 'gcx, N> HashStable<StableHashingContext<'a>> +for traits::VtableImplData<'gcx, N> where N: HashStable<StableHashingContext<'a>> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { let traits::VtableImplData { impl_def_id, @@ -949,10 +1141,10 @@ for traits::VtableImplData<'gcx, N> where N: HashStable<StableHashingContext<'gc } } -impl<'gcx, N> HashStable<StableHashingContext<'gcx>> -for traits::VtableAutoImplData<N> where N: HashStable<StableHashingContext<'gcx>> { +impl<'a, 'gcx, N> HashStable<StableHashingContext<'a>> +for traits::VtableAutoImplData<N> where N: HashStable<StableHashingContext<'a>> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { let traits::VtableAutoImplData { trait_def_id, @@ -963,10 +1155,10 @@ for traits::VtableAutoImplData<N> where N: HashStable<StableHashingContext<'gcx> } } -impl<'gcx, N> HashStable<StableHashingContext<'gcx>> -for traits::VtableObjectData<'gcx, N> where N: HashStable<StableHashingContext<'gcx>> { +impl<'a, 'gcx, N> HashStable<StableHashingContext<'a>> +for traits::VtableObjectData<'gcx, N> where N: HashStable<StableHashingContext<'a>> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { let traits::VtableObjectData { upcast_trait_ref, @@ -979,10 +1171,10 @@ for traits::VtableObjectData<'gcx, N> where N: HashStable<StableHashingContext<' } } -impl<'gcx, N> HashStable<StableHashingContext<'gcx>> -for traits::VtableBuiltinData<N> where N: HashStable<StableHashingContext<'gcx>> { +impl<'a, 'gcx, N> HashStable<StableHashingContext<'a>> +for traits::VtableBuiltinData<N> where N: HashStable<StableHashingContext<'a>> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { let traits::VtableBuiltinData { ref nested, @@ -991,10 +1183,10 @@ for traits::VtableBuiltinData<N> where N: HashStable<StableHashingContext<'gcx>> } } -impl<'gcx, N> HashStable<StableHashingContext<'gcx>> -for traits::VtableClosureData<'gcx, N> where N: HashStable<StableHashingContext<'gcx>> { +impl<'a, 'gcx, N> HashStable<StableHashingContext<'a>> +for traits::VtableClosureData<'gcx, N> where N: HashStable<StableHashingContext<'a>> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { let traits::VtableClosureData { closure_def_id, @@ -1007,10 +1199,10 @@ for traits::VtableClosureData<'gcx, N> where N: HashStable<StableHashingContext< } } -impl<'gcx, N> HashStable<StableHashingContext<'gcx>> -for traits::VtableFnPointerData<'gcx, N> where N: HashStable<StableHashingContext<'gcx>> { +impl<'a, 'gcx, N> HashStable<StableHashingContext<'a>> +for traits::VtableFnPointerData<'gcx, N> where N: HashStable<StableHashingContext<'a>> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { let traits::VtableFnPointerData { fn_ty, @@ -1021,10 +1213,10 @@ for traits::VtableFnPointerData<'gcx, N> where N: HashStable<StableHashingContex } } -impl<'gcx, N> HashStable<StableHashingContext<'gcx>> -for traits::VtableGeneratorData<'gcx, N> where N: HashStable<StableHashingContext<'gcx>> { +impl<'a, 'gcx, N> HashStable<StableHashingContext<'a>> +for traits::VtableGeneratorData<'gcx, N> where N: HashStable<StableHashingContext<'a>> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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 @@ -21,6 +21,12 @@ 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, "constant evaluation detected erroneous expression" @@ -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<StableHashingContext<'gcx>> for LintLevelMap { +impl<'a> HashStable<StableHashingContext<'a>> for LintLevelMap { #[inline] fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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<W: ::rustc_data_structures::stable_hasher::StableHasherResult>(&self, - __ctx: &mut $crate::ich::StableHashingContext<'tcx>, + __ctx: &mut $crate::ich::StableHashingContext<'a>, __hasher: &mut ::rustc_data_structures::stable_hasher::StableHasher<W>) { 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<W: ::rustc_data_structures::stable_hasher::StableHasherResult>(&self, - __ctx: &mut $crate::ich::StableHashingContext<'tcx>, + __ctx: &mut $crate::ich::StableHashingContext<'a>, __hasher: &mut ::rustc_data_structures::stable_hasher::StableHasher<W>) { 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<W: ::rustc_data_structures::stable_hasher::StableHasherResult>(&self, - __ctx: &mut $crate::ich::StableHashingContext<'tcx>, + __ctx: &mut $crate::ich::StableHashingContext<'a>, __hasher: &mut ::rustc_data_structures::stable_hasher::StableHasher<W>) { 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<StableHashingContext<'tcx>> for ::syntax::codemap::Spanned<$T> + impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ::syntax::codemap::Spanned<$T> { #[inline] fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'tcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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<HirId>, } -impl<'gcx> HashStable<StableHashingContext<'gcx>> for BorrowCheckResult { +impl<'a> HashStable<StableHashingContext<'a>> for BorrowCheckResult { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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<S: Encoder>(&self, _: &mut S) -> Result<(), S::Error> { - bug!("should never encode ConstAggregate::{:?}", self) - } -} - -impl<'tcx> Decodable for ConstAggregate<'tcx> { - fn decode<D: Decoder>(_: &mut D) -> Result<Self, D::Error> { - bug!("should never decode ConstAggregate") - } + Value(Value), } impl<'tcx> ConstVal<'tcx> { - pub fn to_const_int(&self) -> Option<ConstInt> { + pub fn to_raw_bits(&self) -> Option<u128> { 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<ErrKind<'tcx>>, } #[derive(Clone, Debug)] pub enum ErrKind<'tcx> { - CannotCast, - MissingStructField, NonConstPath, UnimplementedConstVal(&'static str), - ExpectedConstTuple, - ExpectedConstStruct, - IndexedNonVec, - IndexNotUsize, IndexOutOfBounds { len: u64, index: u64 }, - MiscBinaryOp, - MiscCatchAll, - - IndexOpFeatureGated, Math(ConstMathErr), LayoutError(layout::LayoutError<'tcx>), - ErroneousReferencedConstant(Box<ConstEvalErr<'tcx>>), - TypeckError, CheckMatchError, + Miri(::mir::interpret::EvalError<'tcx>, Vec<FrameInfo>), +} + +#[derive(Clone, Debug)] +pub struct FrameInfo { + pub span: Span, + pub location: String, } impl<'tcx> From<ConstMathErr> for ErrKind<'tcx> { @@ -120,21 +86,23 @@ impl<'tcx> From<ConstMathErr> 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<StableHashingContext<'gcx>> for ScopeTree { +impl<'a> HashStable<StableHashingContext<'a>> for ScopeTree { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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<StableHashingContext<'gcx>> for Cache { +impl<'a> HashStable<StableHashingContext<'a>> for Cache { fn hash_stable<W: StableHasherResult>(&self, - _: &mut StableHashingContext<'gcx>, + _: &mut StableHashingContext<'a>, _: &mut StableHasher<W>) { // 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<Backtrace>, @@ -31,11 +31,11 @@ impl<'tcx> From<EvalErrorKind<'tcx>> 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<dyn Error>), + 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<T, EvalError<'tcx>>; @@ -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<T: layout::HasDataLayout> 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<Block>, 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<V: ty::fold::TypeVisitor<'tcx>>(&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<MemoryPointer> 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<W: Write>(fmt: &mut W, const_val: &ConstVal) -> fmt::Result { +fn fmt_const_val<W: Write>(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<W: Write>(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<V: TypeVisitor<'tcx>>(&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<StableHashingContext<'tcx>> for MonoItem<'tcx> { +impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for MonoItem<'tcx> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'tcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { ::std::mem::discriminant(self).hash_stable(hcx, hasher); @@ -171,9 +171,9 @@ impl<'tcx> CodegenUnit<'tcx> { } } -impl<'tcx> HashStable<StableHashingContext<'tcx>> for CodegenUnit<'tcx> { +impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for CodegenUnit<'tcx> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'tcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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<StableHashingContext<'tcx>> for OutputType { +impl<'a, 'tcx> ToStableHashKey<StableHashingContext<'a>> 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<usize>, + /// The maximum number of stackframes allowed in const eval + pub const_eval_stack_frame_limit: Cell<usize>, + /// The maximum number miri steps per constant + pub const_eval_step_limit: Cell<usize>, + /// 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<StableHashingContext<'gcx>> for Children { +impl<'a> HashStable<StableHashingContext<'a>> for Children { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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; @@ -241,17 +240,6 @@ pub fn decode_existential_predicate_slice<'a, 'tcx, D>(decoder: &mut D) } #[inline] -pub fn decode_byte_array<'a, 'tcx, D>(decoder: &mut D) - -> Result<ByteArray<'tcx>, 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> where D: TyDecoder<'a, 'tcx>, @@ -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<ByteArray<'tcx>> - for $DecoderName<$($typaram),*> { - fn specialized_decode(&mut self) -> Result<ByteArray<'tcx>, 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<StableHashingContext<'gcx>> for TypeckTables<'gcx> { +impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for TypeckTables<'gcx> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { let ty::TypeckTables { local_id_root, @@ -868,7 +868,7 @@ pub struct GlobalCtxt<'tcx> { stability_interner: RefCell<FxHashSet<&'tcx attr::Stability>>, - pub interpret_interner: RefCell<InterpretInterner<'tcx>>, + pub interpret_interner: InterpretInterner<'tcx>, layout_interner: RefCell<FxHashSet<&'tcx LayoutDetails>>, @@ -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<InterpretInternerInner<'tcx>>, +} + +#[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<interpret::AllocId, &'tcx interpret::Allocation>, + /// Reverse map of `alloc_cache` + global_cache: FxHashMap<interpret::AllocId, DefId>, + /// 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::GlobalId<'tcx>, 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<DefId, interpret::AllocId>, /// 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<Instance<'tcx>> { - 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<interpret::AllocId> { - 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<DefId> { + 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<stability::Index<'tcx>> { 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<D: Copy + Debug + Ord + Eq + Hash> SimplifiedTypeGen<D> { } } -impl<'gcx, D> HashStable<StableHashingContext<'gcx>> for SimplifiedTypeGen<D> +impl<'a, 'gcx, D> HashStable<StableHashingContext<'a>> for SimplifiedTypeGen<D> where D: Copy + Debug + Ord + Eq + Hash + - HashStable<StableHashingContext<'gcx>>, + HashStable<StableHashingContext<'a>>, { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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<sup>(2<sup>8</sup> - 1)</sup>, which is limited by LLVM to a i32, with /// a maximum capacity of 2<sup>31</sup> - 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<StableHashingContext<'gcx>> for Variants { +impl<'a> HashStable<StableHashingContext<'a>> for Variants { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { use ty::layout::Variants::*; mem::discriminant(self).hash_stable(hcx, hasher); @@ -2405,9 +2416,9 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for Variants { } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> for FieldPlacement { +impl<'a> HashStable<StableHashingContext<'a>> for FieldPlacement { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { use ty::layout::FieldPlacement::*; mem::discriminant(self).hash_stable(hcx, hasher); @@ -2428,9 +2439,9 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for FieldPlacement { } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> for Abi { +impl<'a> HashStable<StableHashingContext<'a>> for Abi { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { use ty::layout::Abi::*; mem::discriminant(self).hash_stable(hcx, hasher); @@ -2455,9 +2466,9 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for Abi { } } -impl<'gcx> HashStable<StableHashingContext<'gcx>> for Scalar { +impl<'a> HashStable<StableHashingContext<'a>> for Scalar { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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<StableHashingContext<'gcx>> for LayoutError<'gcx> +impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for LayoutError<'gcx> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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<FxHashMap<AbsoluteBytePos, SyntaxContext>>, file_index_to_file: &'x RefCell<FxHashMap<FileMapIndex, Lrc<FileMap>>>, file_index_to_stable_id: &'x FxHashMap<FileMapIndex, StableFilemapId>, + interpret_alloc_cache: FxHashMap<usize, interpret::AllocId>, } 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<interpret::AllocId> for CacheDecoder<'a, 'tcx, 'x> { + fn specialized_decode(&mut self) -> Result<interpret::AllocId, Self::Error> { + 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::<DefId>::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<Span> for CacheDecoder<'a, 'tcx, 'x> { fn specialized_decode(&mut self) -> Result<Span, Self::Error> { let tag: u8 = Decodable::decode(self)?; @@ -703,6 +750,7 @@ struct CacheEncoder<'enc, 'a, 'tcx, E> type_shorthands: FxHashMap<ty::Ty<'tcx>, usize>, predicate_shorthands: FxHashMap<ty::Predicate<'tcx>, usize>, expn_info_shorthands: FxHashMap<Mark, AbsoluteBytePos>, + interpret_alloc_shorthands: FxHashMap<interpret::AllocId, usize>, 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<interpret::AllocId> 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<Span> 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<StableHashingContext<'gcx>> for ty::TyS<'gcx> { +impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for ty::TyS<'gcx> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { let ty::TyS { ref sty, @@ -1439,11 +1439,11 @@ impl<'tcx, T> ParamEnvAnd<'tcx, T> { } } -impl<'gcx, T> HashStable<StableHashingContext<'gcx>> for ParamEnvAnd<'gcx, T> - where T: HashStable<StableHashingContext<'gcx>> +impl<'a, 'gcx, T> HashStable<StableHashingContext<'a>> for ParamEnvAnd<'gcx, T> + where T: HashStable<StableHashingContext<'a>> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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<StableHashingContext<'gcx>> for AdtDef { +impl<'a> HashStable<StableHashingContext<'a>> for AdtDef { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { thread_local! { static CACHE: RefCell<FxHashMap<usize, Fingerprint>> = @@ -1824,27 +1824,79 @@ impl<'a, 'gcx, 'tcx> AdtDef { } #[inline] - pub fn discriminants(&'a self, tcx: TyCtxt<'a, 'gcx, 'tcx>) - -> impl Iterator<Item=ConstInt> + 'a { + pub fn eval_explicit_discr( + &self, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + expr_did: DefId, + ) -> Option<Discr<'tcx>> { 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<Item=Discr<'tcx>> + 'a { + let repr_type = self.repr.discr_type(); let initial = repr_type.initial_discriminant(tcx.global_tcx()); - let mut prev_discr = None::<ConstInt>; + let mut prev_discr = None::<Discr<'tcx>>; 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<Destructor> { 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<u64, ErrorReported> { match x.val { - ConstVal::Integral(x) => Ok(x.to_u64().unwrap()), + ConstVal::Value(Value::ByVal(prim)) => Ok(prim.to_u64().unwrap()), ConstVal::Unevaluated(def_id, substs) => { // FIXME(eddyb) get the right param_env. 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<Self::Lifted> { - 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<Self::Lifted> { + 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<Self::Lifted> { 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<Self::Lifted> { + 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<Ty<'tcx>> { } } +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<V: TypeVisitor<'tcx>>(&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<V: TypeVisitor<'tcx>>(&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<V: TypeVisitor<'tcx>>(&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<StableHashingContext<'gcx>> for TraitImpls { +impl<'a> HashStable<StableHashingContext<'a>> for TraitImpls { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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,60 +22,98 @@ 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; - -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<Disr>) - -> Option<Disr>; - fn assert_ty_matches(&self, val: Disr); - fn initial_discriminant<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Disr; +#[derive(Copy, Clone, Debug)] +pub struct Discr<'tcx> { + pub val: u128, + pub ty: Ty<'tcx> } +impl<'tcx> fmt::Display for Discr<'tcx> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + if self.ty.is_signed() { + write!(fmt, "{}", self.val as i128) + } else { + write!(fmt, "{}", self.val) + } + } +} -macro_rules! typed_literal { - ($tcx:expr, $ty:expr, $lit:expr) => { - match $ty { - SignedInt(ast::IntTy::I8) => ConstInt::I8($lit), - SignedInt(ast::IntTy::I16) => ConstInt::I16($lit), - SignedInt(ast::IntTy::I32) => ConstInt::I32($lit), - SignedInt(ast::IntTy::I64) => ConstInt::I64($lit), - SignedInt(ast::IntTy::I128) => ConstInt::I128($lit), - SignedInt(ast::IntTy::Isize) => match $tcx.sess.target.isize_ty { - ast::IntTy::I16 => ConstInt::Isize(ConstIsize::Is16($lit)), - ast::IntTy::I32 => ConstInt::Isize(ConstIsize::Is32($lit)), - ast::IntTy::I64 => ConstInt::Isize(ConstIsize::Is64($lit)), - _ => bug!(), - }, - UnsignedInt(ast::UintTy::U8) => ConstInt::U8($lit), - UnsignedInt(ast::UintTy::U16) => ConstInt::U16($lit), - UnsignedInt(ast::UintTy::U32) => ConstInt::U32($lit), - UnsignedInt(ast::UintTy::U64) => ConstInt::U64($lit), - UnsignedInt(ast::UintTy::U128) => ConstInt::U128($lit), - UnsignedInt(ast::UintTy::Usize) => match $tcx.sess.target.usize_ty { - ast::UintTy::U16 => ConstInt::Usize(ConstUsize::Us16($lit)), - ast::UintTy::U32 => ConstInt::Usize(ConstUsize::Us32($lit)), - ast::UintTy::U64 => ConstInt::Usize(ConstUsize::Us64($lit)), - _ => bug!(), - }, +impl<'tcx> Discr<'tcx> { + /// Adds 1 to the value and wraps around if the maximum for the type is reached + pub fn wrap_incr<'a, 'gcx>(self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Self { + self.checked_add(tcx, 1).0 + } + pub fn checked_add<'a, 'gcx>(self, tcx: TyCtxt<'a, 'gcx, 'tcx>, n: u128) -> (Self, bool) { + let (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<Discr<'tcx>>) + -> Option<Discr<'tcx>>; + fn initial_discriminant<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Discr<'tcx>; +} + impl IntTypeExt for attr::IntType { fn to_ty<'a, 'gcx, 'tcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> { match *self { @@ -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<Disr>) - -> Option<Disr> { + fn disr_incr<'a, 'tcx>( + &self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + val: Option<Discr<'tcx>>, + ) -> Option<Discr<'tcx>> { 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<hir::Mutability> { 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 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![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<String>) { - 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<String>` 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<Option<i32>> = vec![Some(1), None]; - -// This fails because `None` is not covered. -for Some(x) in xs { - // ... -} -``` - -Match inside the loop instead: - -``` -let xs : Vec<Option<i32>> = vec![Some(1), None]; - -for item in xs { - match item { - Some(x) => {}, - None => {}, - } -} -``` - -Or use `if let`: - -``` -let xs : Vec<Option<i32>> = 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 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -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<NodeMap<&'tcx ty::Const<'tcx>>> -} - -impl<'a, 'tcx> ConstContext<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, - param_env_and_substs: ty::ParamEnvAnd<'tcx, &'tcx Substs<'tcx>>, - tables: &'a ty::TypeckTables<'tcx>) - -> Self { - ConstContext { - tcx, - param_env: param_env_and_substs.param_env, - tables, - substs: param_env_and_substs.value, - fn_args: None - } - } - - /// Evaluate a constant expression in a context where the expression isn't - /// guaranteed to be evaluable. - pub fn eval(&self, e: &'tcx Expr) -> EvalResult<'tcx> { - if self.tables.tainted_by_errors { - signal!(e, TypeckError); - } - eval_const_expr_partial(self, e) - } -} - -type CastResult<'tcx> = Result<ConstVal<'tcx>, ErrKind<'tcx>>; - -fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, - e: &'tcx Expr) -> EvalResult<'tcx> { - trace!("eval_const_expr_partial: {:?}", e); - let tcx = cx.tcx; - let ty = cx.tables.expr_ty(e).subst(tcx, cx.substs); - let mk_const = |val| tcx.mk_const(ty::Const { val, ty }); - - let result = match e.node { - hir::ExprUnary(hir::UnNeg, ref inner) => { - // unary neg literals already got their sign during creation - if let hir::ExprLit(ref lit) = inner.node { - 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::<Vec<_>>(); - assert_eq!(arg_ids.len(), args.len()); - - let mut call_args = NodeMap(); - for (arg, arg_expr) in arg_ids.into_iter().zip(args.iter()) { - let arg_val = cx.eval(arg_expr)?; - debug!("const call arg: {:?}", arg); - if let Some(id) = arg { - assert!(call_args.insert(id, arg_val).is_none()); - } - } - debug!("const call({:?})", call_args); - let callee_cx = ConstContext { - tcx, - param_env: cx.param_env, - tables: tcx.typeck_tables_of(def_id), - substs, - fn_args: Some(call_args) - }; - callee_cx.eval(&body.value)? - }, - hir::ExprLit(ref lit) => match lit_to_const(&lit.node, tcx, ty) { - Ok(val) => mk_const(val), - Err(err) => signal!(e, err), - }, - hir::ExprBlock(ref block) => { - match block.expr { - Some(ref expr) => cx.eval(expr)?, - None => mk_const(Aggregate(Tuple(&[]))), - } - } - hir::ExprType(ref e, _) => cx.eval(e)?, - hir::ExprTup(ref fields) => { - let values = fields.iter().map(|e| cx.eval(e)).collect::<Result<Vec<_>, _>>()?; - mk_const(Aggregate(Tuple(tcx.alloc_const_slice(&values)))) - } - hir::ExprStruct(_, ref fields, _) => { - mk_const(Aggregate(Struct(tcx.alloc_name_const_slice(&fields.iter().map(|f| { - cx.eval(&f.expr).map(|v| (f.name.node, v)) - }).collect::<Result<Vec<_>, _>>()?)))) - } - hir::ExprIndex(ref arr, ref idx) => { - if !tcx.features().const_indexing { - signal!(e, IndexOpFeatureGated); - } - let arr = cx.eval(arr)?; - let idx = match cx.eval(idx)?.val { - Integral(Usize(i)) => i.as_u64(), - _ => signal!(idx, IndexNotUsize), - }; - assert_eq!(idx as usize as u64, idx); - match arr.val { - Aggregate(Array(v)) => { - if let Some(&elem) = v.get(idx as usize) { - elem - } else { - let n = v.len() as u64; - signal!(e, IndexOutOfBounds { len: n, index: idx }) - } - } - - Aggregate(Repeat(.., n)) if idx >= n => { - signal!(e, IndexOutOfBounds { len: n, index: idx }) - } - Aggregate(Repeat(elem, _)) => elem, - - ByteStr(b) if idx >= b.data.len() as u64 => { - signal!(e, IndexOutOfBounds { len: b.data.len() as u64, index: idx }) - } - ByteStr(b) => { - mk_const(Integral(U8(b.data[idx as usize]))) - }, - - _ => signal!(e, IndexedNonVec), - } - } - hir::ExprArray(ref v) => { - let values = v.iter().map(|e| cx.eval(e)).collect::<Result<Vec<_>, _>>()?; - mk_const(Aggregate(Array(tcx.alloc_const_slice(&values)))) - } - hir::ExprRepeat(ref elem, _) => { - let n = match ty.sty { - ty::TyArray(_, n) => n.val.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<ConstVal<'tcx>, 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, ErrKind<'tcx>> { - 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<Ordering, ErrorReported> -{ - 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<Ordering, ErrorReported> { - 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 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -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<ConstInt> { - 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<ConstInt> { - 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<u32> { - 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<u64> { - 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<u128> { - 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<Ordering> { - 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<Self, ConstMathErr>; - fn $func(self, rhs: Self) -> Result<Self, ConstMathErr> { - 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<Self, ConstMathErr>; - fn $func(self, rhs: Self) -> Result<Self, ConstMathErr> { - 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<Self, ConstMathErr>; - fn div(self, rhs: Self) -> Result<Self, ConstMathErr> { - 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<Self, ConstMathErr>; - fn rem(self, rhs: Self) -> Result<Self, ConstMathErr> { - 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<ConstInt> for ConstInt { - type Output = Result<Self, ConstMathErr>; - fn shl(self, rhs: Self) -> Result<Self, ConstMathErr> { - 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<ConstInt> for ConstInt { - type Output = Result<Self, ConstMathErr>; - fn shr(self, rhs: Self) -> Result<Self, ConstMathErr> { - 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<Self, ConstMathErr>; - fn neg(self) -> Result<Self, ConstMathErr> { - 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<Self, ConstMathErr>; - fn not(self) -> Result<Self, ConstMathErr> { - 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 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -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<Self, ConstMathErr> { - 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 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -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<Self, ConstMathErr> { - 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<CTX> HashStable<CTX> for f64 { } } +impl<CTX> HashStable<CTX> for ::std::cmp::Ordering { + fn hash_stable<W: StableHasherResult>(&self, + ctx: &mut CTX, + hasher: &mut StableHasher<W>) { + (*self as i8).hash_stable(ctx, hasher); + } +} + impl<T1: HashStable<CTX>, CTX> HashStable<CTX> for (T1,) { fn hash_stable<W: StableHasherResult>(&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 @@ -683,78 +683,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, "detects doc comments that aren't used by rustdoc" 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; @@ -43,12 +41,6 @@ declare_lint! { } declare_lint! { - EXCEEDING_BITSHIFTS, - Deny, - "shift exceeds the type's number of bits" -} - -declare_lint! { VARIANT_SIZE_DIFFERENCES, Allow, "detects enums with widely varying variant sizes" @@ -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<usize, interpret::AllocId>, } /// 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<DefIndex> for DecodeContext<'a, 'tcx> { } } +impl<'a, 'tcx> SpecializedDecoder<LocalDefId> for DecodeContext<'a, 'tcx> { + #[inline] + fn specialized_decode(&mut self) -> Result<LocalDefId, Self::Error> { + self.specialized_decode().map(|i| LocalDefId::from_def_id(i)) + } +} + +impl<'a, 'tcx> SpecializedDecoder<interpret::AllocId> for DecodeContext<'a, 'tcx> { + fn specialized_decode(&mut self) -> Result<interpret::AllocId, Self::Error> { + 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::<DefId>::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<Span> for DecodeContext<'a, 'tcx> { fn specialized_decode(&mut self) -> Result<Span, Self::Error> { 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<Ty<'tcx>, usize>, predicate_shorthands: FxHashMap<ty::Predicate<'tcx>, usize>, + interpret_alloc_shorthands: FxHashMap<interpret::AllocId, usize>, // This is used to speed up Span encoding. filemap_cache: Lrc<FileMap>, @@ -180,12 +181,48 @@ impl<'a, 'tcx> SpecializedEncoder<Span> for EncodeContext<'a, 'tcx> { } } +impl<'a, 'tcx> SpecializedEncoder<LocalDefId> 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<Ty<'tcx>> 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<interpret::AllocId> 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<ty::GenericPredicates<'tcx>> 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<DefIndex>, } -impl<'gcx> HashStable<StableHashingContext<'gcx>> for TraitImpls { +impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for TraitImpls { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { let TraitImpls { trait_id: (krate, def_index), @@ -310,9 +310,9 @@ pub enum EntryKind<'tcx> { AssociatedConst(AssociatedContainer, u8), } -impl<'gcx> HashStable<StableHashingContext<'gcx>> for EntryKind<'gcx> { +impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for EntryKind<'gcx> { fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher<W>) { 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<u128>, 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<u128>, 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<String>) { + 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<String>` 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<Option<i32>> = vec![Some(1), None]; + +// This fails because `None` is not covered. +for Some(x) in xs { + // ... +} +``` + +Match inside the loop instead: + +``` +let xs : Vec<Option<i32>> = vec![Some(1), None]; + +for item in xs { + match item { + Some(x) => {}, + None => {}, + } +} +``` + +Or use `if let`: + +``` +let xs : Vec<Option<i32>> = 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<ExprRef<'tcx>>, diff --git a/src/librustc_const_eval/_match.rs b/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<Item=&'p Pattern<'tcx>> { @@ -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<Constructor> = 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<Constructor> = 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<Ty<'tcx>> { - 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<Pattern>, suffix: &[Pattern]) -> Result<bool, ErrorReported> { - 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<bool, ErrorReported> { - 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<Vec<&'p Pattern<'tcx>>> { @@ -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 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 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<PatternError<'tcx>>, + pub errors: Vec<PatternError>, } 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<FieldPattern<'tcx>>) -> 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::<Vec<_>>() + }; + 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); + 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, + } + }, + ConstVal::Unevaluated(..) => + span_bug!(span, "{:#?} is not a valid enum constant", cv), } - } - _ => { } - } - let kind = match expr.node { - hir::ExprTup(ref exprs) => { + }, + ty::TyAdt(adt_def, _) => { + let struct_var = adt_def.non_enum_variant(); PatternKind::Leaf { - subpatterns: exprs.iter().enumerate().map(|(i, expr)| { - FieldPattern { - field: Field::new(i), - pattern: self.lower_const_expr(expr, pat_id, span) - } - }).collect() + 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<Ordering> { + 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<ConstVal<'tcx>, ()> { + 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, ()> { + 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()? {} +pub fn eval_body<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + cid: GlobalId<'tcx>, + param_env: ty::ParamEnv<'tcx>, +) -> Option<(Value, Pointer, Ty<'tcx>)> { + let (res, ecx) = eval_body_and_ecx(tcx, cid, None, param_env); + match res { + Ok(val) => Some(val), + Err(mut err) => { + ecx.report(&mut err, true, None); + None + } } - 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>( +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>, - 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(), - ) +) -> (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<EvalError<'tcx>> 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<usize>, + 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(); + + 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, + })) + } - let tables = tcx.typeck_tables_of(def_id); - let body = if let Some(id) = tcx.hir.as_local_node_id(def_id) { - let body_id = tcx.hir.body_owned_by(id); + if let Some(id) = tcx.hir.as_local_node_id(def_id) { + let tables = tcx.typeck_tables_of(def_id); + 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 - }; - - // 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) - } + if let hir::BodyOwnerKind::Const = tcx.hir.body_owner_kind(id) { + tcx.mir_const_qualif(def_id); } - } 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 + // 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, + }); + } }; - 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, EvalError<'tcx>>, -) -> T { - match res { - Ok(val) => val, - Err(mut err) => { - ecx.report(&mut err); - ecx.tcx.sess.abort_if_errors(); - bug!("{:#?}", err); + 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); } - } + 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<Frame<'tcx>>, + pub(crate) stack: Vec<Frame<'mir, 'tcx>>, /// The maximum number of stack frames allowed pub(crate) stack_limit: usize, - /// The maximum number of operations that may be executed. + /// The maximum number of terminators that may be evaluated. /// This prevents infinite loops and huge computations from freezing up const eval. /// Remove once halting problem is solved. - pub(crate) steps_remaining: 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>, @@ -103,23 +104,6 @@ pub enum StackPopCleanup { } #[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>, pub packed: bool, @@ -131,6 +115,15 @@ pub struct ValTy<'tcx> { pub ty: Ty<'tcx>, } +impl<'tcx> ValTy<'tcx> { + pub fn from(val: &ty::Const<'tcx>) -> Option<Self> { + 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<Ty<'tcx>> for &'a EvalContext<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> LayoutOf<Ty<'tcx>> 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<Ty<'tcx>> for &'a EvalContext<'a, 'tcx } } -impl<'c, 'b, 'a, 'tcx, M: Machine<'tcx>> LayoutOf<Ty<'tcx>> - for &'c &'b mut EvalContext<'a, 'tcx, M> { +impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> LayoutOf<Ty<'tcx>> + 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<Ty<'tcx>> } } -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<mir::Local> { + fn collect_storage_annotations<'mir, 'tcx>(mir: &'mir mir::Mir<'tcx>) -> HashSet<mir::Local> { 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::<Vec<_>>()); - 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::<Vec<_>>()); + 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<Span>) -> (Vec<FrameInfo>, 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<Span>) { + 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<T> { /// 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<T> { // 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<AllocId, Allocation>, - /// 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<MemoryKind<M::MemoryKinds>>, ) -> 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)?; - } - } - return Ok(()); - } - // We are marking the static as initialized, so move it out of the uninit map - if let Some(uninit) = self.uninitialized_statics.remove(&alloc_id) { - self.alloc_map.insert(alloc_id, uninit); - } - // do not use `self.get_mut(alloc_id)` here, because we might have already marked a - // sub-element or have circular pointers (e.g. `Rc`-cycles) - let relocations = match self.alloc_map.get_mut(&alloc_id) { - Some(&mut Allocation { - ref mut relocations, - .. - }) => { - match self.alloc_kind.get(&alloc_id) { - // const eval results can refer to "locals". - // E.g. `const Foo: &u32 = &1;` refers to the temp local that stores the `1` - None | - Some(&MemoryKind::Stack) => {}, - Some(&MemoryKind::Machine(m)) => M::mark_static_initialized(m)?, - Some(&MemoryKind::MutableStatic) => { - trace!("mark_static_initalized: skipping already initialized static referred to by static currently being initialized"); - return Ok(()); - }, - } - // overwrite or insert - self.alloc_kind.insert(alloc_id, MemoryKind::MutableStatic); - // take out the relocations vector to free the borrow on self, so we can call - // mark recursively - mem::replace(relocations, Default::default()) + // 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)?; } - None => return err!(DanglingPointerDeref), - }; - // recurse into inner allocations - for &alloc in relocations.values() { - self.mark_inner_allocation_initialized(alloc, mutability)?; + } else { + bug!("no allocation found for {:?}", alloc_id); } - // 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::<BigEndian>(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<u128, io::Error> { +pub fn read_target_uint(endianness: layout::Endian, mut source: &[u8]) -> Result<u128, io::Error> { match endianness { layout::Endian::Little => source.read_uint128::<LittleEndian>(source.len()), layout::Endian::Big => source.read_uint128::<BigEndian>(source.len()), } } -fn read_target_int(endianness: layout::Endian, mut source: &[u8]) -> Result<i128, io::Error> { - match endianness { - layout::Endian::Little => source.read_int128::<LittleEndian>(source.len()), - layout::Endian::Big => source.read_int128::<BigEndian>(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), - - - (Eq, _) => PrimVal::from_bool(l == r), - (Ne, _) => PrimVal::from_bool(l != r), - - (Lt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) < (r as i128)), - (Lt, _) => PrimVal::from_bool(l < r), - (Le, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) <= (r as i128)), - (Le, _) => PrimVal::from_bool(l <= r), - (Gt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) > (r as i128)), - (Gt, _) => PrimVal::from_bool(l > r), - (Ge, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) >= (r as i128)), - (Ge, _) => PrimVal::from_bool(l >= r), + if left_layout.abi.is_signed() { + let op: Option<fn(&i128, &i128) -> 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<fn(i128, i128) -> (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)); + } + } - (BitOr, _) => PrimVal::Bytes(l | r), - (BitAnd, _) => PrimVal::Bytes(l & r), - (BitXor, _) => PrimVal::Bytes(l ^ r), + if let ty::TyFloat(fty) = left_ty.sty { + return Ok((float_op(bin_op, l, r, fty), false)); + } - (Add, k) if k.is_int() => return int_arithmetic!(k, overflowing_add, l, r), - (Sub, k) if k.is_int() => return int_arithmetic!(k, overflowing_sub, l, r), - (Mul, k) if k.is_int() => return int_arithmetic!(k, overflowing_mul, l, r), - (Div, k) if k.is_int() => return int_arithmetic!(k, overflowing_div, l, r), - (Rem, k) if k.is_int() => return int_arithmetic!(k, overflowing_rem, l, r), + // only ints left + let val = match bin_op { + Eq => PrimVal::from_bool(l == r), + Ne => PrimVal::from_bool(l != r), + + Lt => PrimVal::from_bool(l < r), + Le => PrimVal::from_bool(l <= r), + Gt => PrimVal::from_bool(l > r), + Ge => PrimVal::from_bool(l >= r), + + BitOr => PrimVal::Bytes(l | r), + BitAnd => PrimVal::Bytes(l & r), + BitXor => PrimVal::Bytes(l ^ r), + + Add | Sub | Mul | Rem | Div => { + let op: fn(u128, u128) -> (u128, bool) = match bin_op { + Add => u128::overflowing_add, + Sub => u128::overflowing_sub, + Mul => u128::overflowing_mul, + Rem | Div if r == 0 => return Ok((PrimVal::Bytes(l), true)), + Div => u128::overflowing_div, + Rem => u128::overflowing_rem, + _ => bug!(), + }; + let (result, oflo) = op(l, r); + let truncated = self.truncate(result, left_ty)?; + return Ok((PrimVal::Bytes(truncated), oflo || truncated != result)); + } _ => { let msg = format!( "unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, - left_kind, + left_ty, right, - right_kind + right_ty, ); return err!(Unimplemented(msg)); } @@ -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()?; + pub fn unary_op( + &self, + un_op: mir::UnOp, + val: PrimVal, + ty: Ty<'tcx>, + ) -> EvalResult<'tcx, PrimVal> { + use rustc::mir::UnOp::*; + use rustc_apfloat::ieee::{Single, Double}; + use rustc_apfloat::Float; - let result_bytes = match (un_op, val_kind) { - (Not, Bool) => !val.to_bool()? as u128, + let bytes = val.to_bytes()?; + let size = self.layout_of(ty)?.size.bits(); - (Not, U8) => !(bytes as u8) as u128, - (Not, U16) => !(bytes as u16) as u128, - (Not, U32) => !(bytes as u32) as u128, - (Not, U64) => !(bytes as u64) as u128, - (Not, U128) => !bytes, + let result_bytes = match (un_op, &ty.sty) { - (Not, I8) => !(bytes as i8) as u128, - (Not, I16) => !(bytes as i16) as u128, - (Not, I32) => !(bytes as i32) as u128, - (Not, I64) => !(bytes as i64) as u128, - (Not, I128) => !(bytes as i128) as u128, + (Not, ty::TyBool) => !val.to_bool()? as u128, - (Neg, I8) => -(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, + (Not, _) => !bytes, - (Neg, F32) => (-bytes_to_f32(bytes)).bits, - (Neg, F64) => (-bytes_to_f64(bytes)).bits, + (Neg, ty::TyFloat(FloatTy::F32)) => Single::to_bits(-Single::from_bits(bytes)), + (Neg, ty::TyFloat(FloatTy::F64)) => Double::to_bits(-Double::from_bits(bytes)), - _ => { - let msg = format!("unimplemented unary op: {:?}, {:?}", un_op, val); - return err!(Unimplemented(msg)); - } - }; + (Neg, _) if bytes == (1 << (size - 1)) => return err!(OverflowingMath), + (Neg, _) => (-(bytes as i128)) as u128, + }; - Ok(PrimVal::Bytes(result_bytes)) + Ok(PrimVal::Bytes(self.truncate(result_bytes, ty)?)) + } } diff --git a/src/librustc_mir/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<usize>, + 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<F: FnOnce(&mut Self) -> 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<MonoItem<'tcx>>, 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<MonoItem<'tcx>>, +) { + 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<MonoItem<'tcx>>) { 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<MonoItem<'tcx>>, +) { + 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<Constant<'tcx>> { - 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! 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<Local, Option<Const<'tcx>>>, + can_const_prop: IndexVec<Local, bool>, + param_env: ParamEnv<'tcx>, +} + +impl<'a, 'b, 'tcx> LayoutOf<ty::Ty<'tcx>> for &'a ConstPropagator<'a, 'b, 'tcx> { + type TyLayout = Result<TyLayout<'tcx>, 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<Const<'tcx>> { + 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<Const<'tcx>> { + 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<Const<'tcx>> { + 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<Const<'tcx>> { + 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<Const<'tcx>> { + 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<u64> { + tcx.layout_of(param_env.and(ty)).ok().map(|layout| layout.size.bytes()) +} + +struct CanConstProp { + can_const_prop: IndexVec<Local, bool>, + // false at the beginning, once set, there are not allowed to be any more assignments + found_assignment: IndexVec<Local, bool>, +} + +impl CanConstProp { + /// returns true if `local` can be propagated + fn check(mir: &Mir) -> IndexVec<Local, bool> { + 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::<Vec<_>>()), + values: Cow::from(cases.iter().map(|&(i, _)| i.into()).collect::<Vec<_>>()), 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<BasicBlock>, - 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 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<F, G>(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<ValueRef, ConstEvalErr<'tcx>> { + 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) - } - 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<mir::Local, Option<Result<Const<'tcx>, ConstEvalErr<'tcx>>>> -} - -fn add_err<'tcx, U, V>(failure: &mut Result<U, ConstEvalErr<'tcx>>, - value: &Result<V, ConstEvalErr<'tcx>>) -{ - 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<mir::Local, Result<Const<'tcx>, 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<mir::Local, Result<Const<'tcx>, ConstEvalErr<'tcx>>>) - -> Result<Const<'tcx>, 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<T>(&self, value: &T) -> T - where T: TransNormalize<'tcx> - { - self.cx.tcx.trans_apply_param_substs(self.substs, value) - } - - fn trans(&mut self) -> Result<Const<'tcx>, 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); - } - } +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) } - - 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); - } - } - _ => 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<Const<'tcx>, 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<ConstPlace<'tcx>, 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<Const<'tcx>, 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<Const<'tcx>, 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) - } - } + consts::addr_of(cx, init, alloc.align, "byte_str") } - 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<ConstInt> { - 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) -} -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()) - } + C_struct(cx, &llvals, true) } -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<ValueRef, ConstEvalErr<'tcx>> { - 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::<Vec<_>>()), 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<MiriValue, ConstEvalErr<'tcx>> { + 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>>) - -> 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<MiriValue, ConstEvalErr<'tcx>> { + 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)) } - } - _ => {} - } - - // 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; - } - - 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; + mir::Literal::Value { value } => { + Ok(self.monomorphize(&value)) + } + }.and_then(|c| self.const_to_miri_value(bx, c)) + } + + /// 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<Vec<ValueRef>, _> = (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) + }) } - - // 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<OperandRef<'tcx>, 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<ConstInt> = Vec::new(); + let mut disr_vals: Vec<Discr<'tcx>> = 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::<ConstInt>; + let mut prev_discr = None::<Discr<'tcx>>; // 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<Type> 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<Type> 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<H: Hasher>(&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<i8, i16> { #[no_mangle] pub fn low_align_const() -> E<i16, [i16; 3]> { // 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<i16, [i16; 3]> { #[no_mangle] pub fn high_align_const() -> E<i16, i32> { // 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>(_: 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_attrs)] +#![deny(const_err)] + +fn black_box<T>(_: 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 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![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<T:fmt::Debug>(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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![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>(_: 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![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>(_: 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![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>(_: 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 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![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>(_: 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: Copy>(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<A, B> { } impl<A: Foo, B: Foo> Foo for Bar<A, B> { - 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!("{}", <Bar<u16, u8> as Foo>::AMT); + println!("{}", <Bar<u16, u8> 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![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 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// https://github.com/rust-lang/rust/issues/41898 + +#![feature(nonzero, const_fn)] +extern crate core; +use core::nonzero::NonZero; + +fn main() { + const FOO: NonZero<u64> = 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(i128_type)] +#![deny(const_err)] + +use std::{f32, f64}; + +// Forces evaluation of constants, triggering hard error +fn force<T>(_: 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// must-compile-successfully +// run-pass + +union Transmute<T: Copy, U: Copy> { + 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<for<'a> 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<for<'a> 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 --- /dev/null +++ b/src/test/ui/const-eval/const_transmute.stderr 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +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 +error[E0080]: constant evaluation error + --> $DIR/issue-43197.rs:22:23 | -LL | const Y: u32 = foo(0-1); //~ ERROR constant evaluation error - | ^^^^^^^^ +LL | println!("{} {}", X, Y); + | ^ referenced constant has errors -error[E0080]: constant evaluation error - --> $DIR/issue-43197.rs:18:20 +warning: attempt to subtract with overflow + --> $DIR/issue-43197.rs:20:24 | -LL | const X: u32 = 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:20:24 + --> $DIR/issue-43197.rs:22:26 | -LL | const Y: u32 = foo(0-1); //~ ERROR constant evaluation error - | ^^^ attempt to subtract with overflow +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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![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 `<I as std::iter::IntoIterator><std::ops::Range<usize>>::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>(A, B); const BOO: Pair<Cake, Cake> = 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<Cake, Cake> = 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//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 + --> $DIR/issue-23302-3.rs:13:16 | -LL | const A: i32 = B; //~ ERROR E0391 +LL | const B: i32 = A; //~ ERROR cyclic dependency detected | ^ cyclic reference | -note: the cycle begins when processing `B`... - --> $DIR/issue-23302-3.rs:13:1 +note: the cycle begins when const checking if rvalue is promotable to static `A`... + --> $DIR/issue-23302-3.rs:11:1 | -LL | const B: i32 = A; +LL | const A: i32 = B; | ^^^^^^^^^^^^^^^^^ -note: ...which then requires processing `A`... - --> $DIR/issue-23302-3.rs:13:16 +note: ...which then requires checking which parts of `A` are promotable to static... + --> $DIR/issue-23302-3.rs:11:1 + | +LL | const A: i32 = B; + | ^^^^^^^^^^^^^^^^^ +note: ...which then requires const checking if rvalue is promotable to static `B`... + --> $DIR/issue-23302-3.rs:11:16 | -LL | const B: i32 = A; +LL | const A: i32 = B; | ^ - = note: ...which then again requires processing `B`, completing the cycle. +note: ...which then requires checking which parts of `B` are promotable to static... + --> $DIR/issue-23302-3.rs:13:1 + | +LL | const B: i32 = A; //~ ERROR cyclic dependency detected + | ^^^^^^^^^^^^^^^^^ + = note: ...which then again requires const checking if rvalue is promotable to static `A`, completing the cycle. error: aborting due to previous error diff --git a/src/test/ui/issue-36163.stderr b/src/test/ui/issue-36163.stderr index 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 + --> $DIR/issue-36163.rs:14:9 | LL | B = A, //~ ERROR E0391 - | ^^^^^ -note: ...which then requires const-evaluating `A`... + | ^ +note: ...which then requires processing `Foo::B::{{initializer}}`... --> $DIR/issue-36163.rs:14:9 | LL | B = A, //~ ERROR E0391 | ^ +note: ...which then requires const-evaluating `A`... + --> $DIR/issue-36163.rs:11:1 + | +LL | const A: isize = Foo::B as isize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which then requires computing layout of `Foo`... + --> $DIR/issue-36163.rs:11:18 + | +LL | const A: isize = Foo::B as isize; + | ^^^^^^ = note: ...which then again requires const-evaluating `Foo::B::{{initializer}}`, completing the cycle. error: aborting due to previous error diff --git a/src/test/ui/issue-38875/issue_38875.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" |
