diff --git a/src/Cargo.lock b/src/Cargo.lock index 6ce1e3a7bc6af..62be97a6c9f92 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -1779,20 +1779,6 @@ dependencies = [ "syntax_pos 0.0.0", ] -[[package]] -name = "rustc_const_eval" -version = "0.0.0" -dependencies = [ - "arena 0.0.0", - "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc 0.0.0", - "rustc_const_math 0.0.0", - "rustc_data_structures 0.0.0", - "rustc_errors 0.0.0", - "syntax 0.0.0", - "syntax_pos 0.0.0", -] - [[package]] name = "rustc_const_math" version = "0.0.0" @@ -1834,7 +1820,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", @@ -1885,7 +1870,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", ] @@ -1932,6 +1917,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", @@ -1940,7 +1926,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", @@ -1967,9 +1952,9 @@ 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_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 323d49e469120..b62762ef69e01 100644 --- a/src/doc/rustc-ux-guidelines.md +++ b/src/doc/rustc-ux-guidelines.md @@ -64,7 +64,6 @@ for details on how to format and write long error codes. [librustc](https://github.com/rust-lang/rust/blob/master/src/librustc/diagnostics.rs), [libsyntax](https://github.com/rust-lang/rust/blob/master/src/libsyntax/diagnostics.rs), [librustc_borrowck](https://github.com/rust-lang/rust/blob/master/src/librustc_borrowck/diagnostics.rs), - [librustc_const_eval](https://github.com/rust-lang/rust/blob/master/src/librustc_const_eval/diagnostics.rs), [librustc_metadata](https://github.com/rust-lang/rust/blob/master/src/librustc_metadata/diagnostics.rs), [librustc_mir](https://github.com/rust-lang/rust/blob/master/src/librustc_mir/diagnostics.rs), [librustc_passes](https://github.com/rust-lang/rust/blob/master/src/librustc_passes/diagnostics.rs), diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs index 266cae3c122f4..9a37970b34da2 100644 --- a/src/libcore/cmp.rs +++ b/src/libcore/cmp.rs @@ -427,6 +427,7 @@ impl Ord for Reverse { /// } /// } /// ``` +#[cfg_attr(not(stage0), lang = "ord")] #[stable(feature = "rust1", since = "1.0.0")] pub trait Ord: Eq + PartialOrd { /// This method returns an `Ordering` between `self` and `other`. @@ -596,7 +597,8 @@ impl PartialOrd for Ordering { /// assert_eq!(x < y, true); /// assert_eq!(x.lt(&y), true); /// ``` -#[lang = "ord"] +#[cfg_attr(stage0, lang = "ord")] +#[cfg_attr(not(stage0), lang = "partial_ord")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented = "can't compare `{Self}` with `{Rhs}`"] pub trait PartialOrd: PartialEq { diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 14f54fbffac2e..78724d4f0cc1b 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; @@ -515,7 +515,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> }, @@ -657,7 +657,7 @@ trait DepNodeParams<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> : fmt::Debug { } impl<'a, 'gcx: 'tcx + 'a, 'tcx: 'a, T> DepNodeParams<'a, 'gcx, 'tcx> for T - where T: HashStable> + fmt::Debug + where T: HashStable> + fmt::Debug { default const CAN_RECONSTRUCT_QUERY_KEY: bool = false; diff --git a/src/librustc/hir/def_id.rs b/src/librustc/hir/def_id.rs index 637b156ceef5d..34b3aa53d6bcf 100644 --- a/src/librustc/hir/def_id.rs +++ b/src/librustc/hir/def_id.rs @@ -220,7 +220,6 @@ impl serialize::UseSpecializedDecodable for DefId {} pub struct LocalDefId(DefIndex); impl LocalDefId { - #[inline] pub fn from_def_id(def_id: DefId) -> LocalDefId { assert!(def_id.is_local()); diff --git a/src/librustc/hir/map/collector.rs b/src/librustc/hir/map/collector.rs index 0e1c66277163e..d8619ad9299ac 100644 --- a/src/librustc/hir/map/collector.rs +++ b/src/librustc/hir/map/collector.rs @@ -513,7 +513,7 @@ struct HirItemLike { hash_bodies: bool, } -impl<'hir, T> HashStable> for HirItemLike +impl<'a, 'hir, T> HashStable> for HirItemLike where T: HashStable> { fn hash_stable(&self, diff --git a/src/librustc/ich/hcx.rs b/src/librustc/ich/hcx.rs index 2945b1ab91245..5e7b2cce24bf9 100644 --- a/src/librustc/ich/hcx.rs +++ b/src/librustc/ich/hcx.rs @@ -44,19 +44,19 @@ thread_local!(static IGNORED_ATTR_NAMES: RefCell> = /// 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 CrateStore, - body_resolver: BodyResolver<'gcx>, +pub struct StableHashingContext<'a> { + sess: &'a Session, + definitions: &'a Definitions, + cstore: &'a CrateStore, + body_resolver: BodyResolver<'a>, hash_spans: bool, hash_bodies: bool, node_id_hashing_mode: NodeIdHashingMode, // Very often, we are hashing something that does not need the // CachingCodemapView, so we initialize it lazily. - raw_codemap: &'gcx CodeMap, - caching_codemap: Option>, + raw_codemap: &'a CodeMap, + caching_codemap: Option>, } #[derive(PartialEq, Eq, Clone, Copy)] @@ -79,14 +79,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 CrateStore) + pub fn new(sess: &'a Session, + krate: &'a hir::Crate, + definitions: &'a Definitions, + cstore: &'a CrateStore) -> Self { let hash_spans_initial = !sess.opts.debugging_opts.incremental_ignore_spans; @@ -113,7 +113,7 @@ impl<'gcx> StableHashingContext<'gcx> { } #[inline] - pub fn sess(&self) -> &'gcx Session { + pub fn sess(&self) -> &'a Session { self.sess } @@ -172,7 +172,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 @@ -202,27 +202,27 @@ impl<'gcx> StableHashingContext<'gcx> { } impl<'a, 'gcx, 'lcx> StableHashingContextProvider for TyCtxt<'a, 'gcx, 'lcx> { - type ContextType = StableHashingContext<'gcx>; + type ContextType = StableHashingContext<'a>; fn create_stable_hashing_context(&self) -> Self::ContextType { (*self).create_stable_hashing_context() } } -impl<'gcx> StableHashingContextProvider for StableHashingContext<'gcx> { - type ContextType = StableHashingContext<'gcx>; +impl<'a> StableHashingContextProvider for StableHashingContext<'a> { + type ContextType = StableHashingContext<'a>; fn create_stable_hashing_context(&self) -> Self::ContextType { self.clone() } } -impl<'gcx> ::dep_graph::DepGraphSafe for StableHashingContext<'gcx> { +impl<'a> ::dep_graph::DepGraphSafe for StableHashingContext<'a> { } -impl<'gcx> HashStable> for hir::BodyId { +impl<'a> HashStable> for hir::BodyId { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { if hcx.hash_bodies() { hcx.body_resolver.body(*self).hash_stable(hcx, hasher); @@ -230,10 +230,10 @@ impl<'gcx> HashStable> for hir::BodyId { } } -impl<'gcx> HashStable> for hir::HirId { +impl<'a> HashStable> for hir::HirId { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { match hcx.node_id_hashing_mode { NodeIdHashingMode::Ignore => { @@ -252,21 +252,21 @@ impl<'gcx> HashStable> for hir::HirId { } } -impl<'gcx> ToStableHashKey> for hir::HirId { +impl<'a> ToStableHashKey> for hir::HirId { type KeyType = (DefPathHash, hir::ItemLocalId); #[inline] fn to_stable_hash_key(&self, - hcx: &StableHashingContext<'gcx>) + hcx: &StableHashingContext<'a>) -> (DefPathHash, hir::ItemLocalId) { let def_path_hash = hcx.local_def_path_hash(self.owner); (def_path_hash, self.local_id) } } -impl<'gcx> HashStable> for ast::NodeId { +impl<'a> HashStable> for ast::NodeId { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { match hcx.node_id_hashing_mode { NodeIdHashingMode::Ignore => { @@ -279,18 +279,18 @@ impl<'gcx> HashStable> for ast::NodeId { } } -impl<'gcx> ToStableHashKey> for ast::NodeId { +impl<'a> ToStableHashKey> for ast::NodeId { type KeyType = (DefPathHash, hir::ItemLocalId); #[inline] fn to_stable_hash_key(&self, - hcx: &StableHashingContext<'gcx>) + hcx: &StableHashingContext<'a>) -> (DefPathHash, hir::ItemLocalId) { hcx.definitions.node_to_hir_id(*self).to_stable_hash_key(hcx) } } -impl<'gcx> HashStable> for Span { +impl<'a> HashStable> for Span { // Hash a span in a stable way. We can't directly hash the span's BytePos // fields (that would be similar to hashing pointers, since those are just @@ -302,7 +302,7 @@ impl<'gcx> HashStable> for Span { // Also, hashing filenames is expensive so we avoid doing it twice when the // span starts and ends in the same file, which is almost always the case. fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { const TAG_VALID_SPAN: u8 = 0; const TAG_INVALID_SPAN: u8 = 1; @@ -382,8 +382,8 @@ impl<'gcx> HashStable> for Span { } } -pub fn hash_stable_trait_impls<'gcx, W, R>( - hcx: &mut StableHashingContext<'gcx>, +pub fn hash_stable_trait_impls<'a, 'gcx, W, R>( + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher, blanket_impls: &Vec, non_blanket_impls: &HashMap, R>) diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc/ich/impls_hir.rs index cc1b028480e4d..13a4eb9f314d7 100644 --- a/src/librustc/ich/impls_hir.rs +++ b/src/librustc/ich/impls_hir.rs @@ -20,46 +20,46 @@ use rustc_data_structures::stable_hasher::{HashStable, ToStableHashKey, use std::mem; use syntax::ast; -impl<'gcx> HashStable> for DefId { +impl<'a> HashStable> for DefId { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.def_path_hash(*self).hash_stable(hcx, hasher); } } -impl<'gcx> ToStableHashKey> for DefId { +impl<'a> ToStableHashKey> for DefId { type KeyType = DefPathHash; #[inline] - fn to_stable_hash_key(&self, hcx: &StableHashingContext<'gcx>) -> DefPathHash { + fn to_stable_hash_key(&self, hcx: &StableHashingContext<'a>) -> DefPathHash { hcx.def_path_hash(*self) } } -impl<'gcx> HashStable> for LocalDefId { +impl<'a> HashStable> for LocalDefId { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.def_path_hash(self.to_def_id()).hash_stable(hcx, hasher); } } -impl<'gcx> ToStableHashKey> for LocalDefId { +impl<'a> ToStableHashKey> for LocalDefId { type KeyType = DefPathHash; #[inline] - fn to_stable_hash_key(&self, hcx: &StableHashingContext<'gcx>) -> DefPathHash { + fn to_stable_hash_key(&self, hcx: &StableHashingContext<'a>) -> DefPathHash { hcx.def_path_hash(self.to_def_id()) } } -impl<'gcx> HashStable> for CrateNum { +impl<'a> HashStable> for CrateNum { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.def_path_hash(DefId { krate: *self, @@ -68,11 +68,11 @@ impl<'gcx> HashStable> for CrateNum { } } -impl<'gcx> ToStableHashKey> for CrateNum { +impl<'a> ToStableHashKey> for CrateNum { type KeyType = DefPathHash; #[inline] - fn to_stable_hash_key(&self, hcx: &StableHashingContext<'gcx>) -> DefPathHash { + fn to_stable_hash_key(&self, hcx: &StableHashingContext<'a>) -> DefPathHash { let def_id = DefId { krate: *self, index: CRATE_DEF_INDEX }; def_id.to_stable_hash_key(hcx) } @@ -80,13 +80,13 @@ impl<'gcx> ToStableHashKey> for CrateNum { impl_stable_hash_for!(tuple_struct hir::ItemLocalId { index }); -impl<'gcx> ToStableHashKey> +impl<'a> ToStableHashKey> for hir::ItemLocalId { type KeyType = hir::ItemLocalId; #[inline] fn to_stable_hash_key(&self, - _: &StableHashingContext<'gcx>) + _: &StableHashingContext<'a>) -> hir::ItemLocalId { *self } @@ -99,9 +99,9 @@ for hir::ItemLocalId { // want to pick up on a reference changing its target, so we hash the NodeIds // in "DefPath Mode". -impl<'gcx> HashStable> for hir::ItemId { +impl<'a> HashStable> for hir::ItemId { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::ItemId { id @@ -113,9 +113,9 @@ impl<'gcx> HashStable> for hir::ItemId { } } -impl<'gcx> HashStable> for hir::TraitItemId { +impl<'a> HashStable> for hir::TraitItemId { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::TraitItemId { node_id @@ -127,9 +127,9 @@ impl<'gcx> HashStable> for hir::TraitItemId { } } -impl<'gcx> HashStable> for hir::ImplItemId { +impl<'a> HashStable> for hir::ImplItemId { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::ImplItemId { node_id @@ -265,9 +265,9 @@ impl_stable_hash_for!(struct hir::TypeBinding { span }); -impl<'gcx> HashStable> for hir::Ty { +impl<'a> HashStable> for hir::Ty { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.while_hashing_hir_bodies(true, |hcx| { let hir::Ty { @@ -333,9 +333,9 @@ impl_stable_hash_for!(enum hir::FunctionRetTy { Return(t) }); -impl<'gcx> HashStable> for hir::TraitRef { +impl<'a> HashStable> for hir::TraitRef { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::TraitRef { ref path, @@ -370,9 +370,9 @@ impl_stable_hash_for!(struct hir::MacroDef { }); -impl<'gcx> HashStable> for hir::Block { +impl<'a> HashStable> for hir::Block { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::Block { ref stmts, @@ -394,9 +394,9 @@ impl<'gcx> HashStable> for hir::Block { } } -impl<'gcx> HashStable> for hir::Pat { +impl<'a> HashStable> for hir::Pat { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::Pat { id: _, @@ -521,9 +521,9 @@ impl_stable_hash_for!(enum hir::UnsafeSource { UserProvided }); -impl<'gcx> HashStable> for hir::Expr { +impl<'a> HashStable> for hir::Expr { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.while_hashing_hir_bodies(true, |hcx| { let hir::Expr { @@ -585,9 +585,9 @@ impl_stable_hash_for!(enum hir::LoopSource { ForLoop }); -impl<'gcx> HashStable> for hir::MatchSource { +impl<'a> HashStable> for hir::MatchSource { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use hir::MatchSource; @@ -636,9 +636,9 @@ impl_stable_hash_for!(enum hir::ScopeTarget { Loop(loop_id_result) }); -impl<'gcx> HashStable> for ast::Ident { +impl<'a> HashStable> for ast::Ident { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ast::Ident { ref name, @@ -649,9 +649,9 @@ impl<'gcx> HashStable> for ast::Ident { } } -impl<'gcx> HashStable> for hir::TraitItem { +impl<'a> HashStable> for hir::TraitItem { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::TraitItem { id: _, @@ -684,9 +684,9 @@ impl_stable_hash_for!(enum hir::TraitItemKind { Type(bounds, rhs) }); -impl<'gcx> HashStable> for hir::ImplItem { +impl<'a> HashStable> for hir::ImplItem { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::ImplItem { id: _, @@ -718,9 +718,9 @@ impl_stable_hash_for!(enum hir::ImplItemKind { Type(t) }); -impl<'gcx> HashStable> for hir::Visibility { +impl<'a> HashStable> for hir::Visibility { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -739,9 +739,9 @@ impl<'gcx> HashStable> for hir::Visibility { } } -impl<'gcx> HashStable> for hir::Defaultness { +impl<'a> HashStable> for hir::Defaultness { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -760,9 +760,9 @@ impl_stable_hash_for!(enum hir::ImplPolarity { Negative }); -impl<'gcx> HashStable> for hir::Mod { +impl<'a> HashStable> for hir::Mod { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::Mod { inner, @@ -815,9 +815,9 @@ impl_stable_hash_for!(enum hir::VariantData { Unit(id) }); -impl<'gcx> HashStable> for hir::Item { +impl<'a> HashStable> for hir::Item { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::Item { name, @@ -874,10 +874,10 @@ impl_stable_hash_for!(struct hir::ImplItemRef { defaultness }); -impl<'gcx> HashStable> +impl<'a> HashStable> for hir::AssociatedItemKind { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -919,9 +919,9 @@ impl_stable_hash_for!(struct hir::Arg { hir_id }); -impl<'gcx> HashStable> for hir::Body { +impl<'a> HashStable> for hir::Body { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::Body { ref arguments, @@ -937,12 +937,12 @@ impl<'gcx> HashStable> for hir::Body { } } -impl<'gcx> ToStableHashKey> for hir::BodyId { +impl<'a> ToStableHashKey> for hir::BodyId { type KeyType = (DefPathHash, hir::ItemLocalId); #[inline] fn to_stable_hash_key(&self, - hcx: &StableHashingContext<'gcx>) + hcx: &StableHashingContext<'a>) -> (DefPathHash, hir::ItemLocalId) { let hir::BodyId { node_id } = *self; node_id.to_stable_hash_key(hcx) @@ -955,9 +955,9 @@ impl_stable_hash_for!(struct hir::InlineAsmOutput { is_indirect }); -impl<'gcx> HashStable> for hir::GlobalAsm { +impl<'a> HashStable> for hir::GlobalAsm { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::GlobalAsm { asm, @@ -968,9 +968,9 @@ impl<'gcx> HashStable> for hir::GlobalAsm { } } -impl<'gcx> HashStable> for hir::InlineAsm { +impl<'a> HashStable> for hir::InlineAsm { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::InlineAsm { asm, @@ -1051,22 +1051,22 @@ impl_stable_hash_for!(enum hir::Constness { NotConst }); -impl<'gcx> HashStable> +impl<'a> HashStable> for hir::def_id::DefIndex { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.local_def_path_hash(*self).hash_stable(hcx, hasher); } } -impl<'gcx> ToStableHashKey> +impl<'a> ToStableHashKey> for hir::def_id::DefIndex { type KeyType = DefPathHash; #[inline] - fn to_stable_hash_key(&self, hcx: &StableHashingContext<'gcx>) -> DefPathHash { + fn to_stable_hash_key(&self, hcx: &StableHashingContext<'a>) -> DefPathHash { hcx.local_def_path_hash(*self) } } @@ -1079,10 +1079,10 @@ impl_stable_hash_for!(struct hir::def::Export { is_import }); -impl<'gcx> HashStable> +impl<'a> HashStable> for ::middle::lang_items::LangItem { fn hash_stable(&self, - _: &mut StableHashingContext<'gcx>, + _: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { ::std::hash::Hash::hash(self, hasher); } @@ -1093,10 +1093,10 @@ impl_stable_hash_for!(struct ::middle::lang_items::LanguageItems { missing }); -impl<'gcx> HashStable> +impl<'a> HashStable> for hir::TraitCandidate { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { let hir::TraitCandidate { @@ -1110,11 +1110,11 @@ for hir::TraitCandidate { } } -impl<'gcx> ToStableHashKey> for hir::TraitCandidate { +impl<'a> ToStableHashKey> for hir::TraitCandidate { type KeyType = (DefPathHash, Option<(DefPathHash, hir::ItemLocalId)>); fn to_stable_hash_key(&self, - hcx: &StableHashingContext<'gcx>) + hcx: &StableHashingContext<'a>) -> Self::KeyType { let hir::TraitCandidate { def_id, diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs index f46b590d2dc59..e549d1b7530fe 100644 --- a/src/librustc/ich/impls_mir.rs +++ b/src/librustc/ich/impls_mir.rs @@ -36,11 +36,11 @@ impl_stable_hash_for!(struct mir::BasicBlockData<'tcx> { statements, terminator, impl_stable_hash_for!(struct mir::UnsafetyViolation { source_info, description, kind }); impl_stable_hash_for!(struct mir::UnsafetyCheckResult { violations, unsafe_blocks }); -impl<'gcx> HashStable> +impl<'a> HashStable> for mir::UnsafetyViolationKind { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -61,12 +61,12 @@ impl_stable_hash_for!(struct mir::Terminator<'tcx> { source_info }); -impl<'gcx, T> HashStable> for mir::ClearCrossCrate - where T: HashStable> +impl<'a, 'gcx, T> HashStable> for mir::ClearCrossCrate + where T: HashStable> { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -78,61 +78,61 @@ impl<'gcx, T> HashStable> for mir::ClearCrossCrate } } -impl<'gcx> HashStable> for mir::Local { +impl<'a> HashStable> for mir::Local { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use rustc_data_structures::indexed_vec::Idx; self.index().hash_stable(hcx, hasher); } } -impl<'gcx> HashStable> for mir::BasicBlock { +impl<'a> HashStable> for mir::BasicBlock { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use rustc_data_structures::indexed_vec::Idx; self.index().hash_stable(hcx, hasher); } } -impl<'gcx> HashStable> for mir::Field { +impl<'a> HashStable> for mir::Field { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use rustc_data_structures::indexed_vec::Idx; self.index().hash_stable(hcx, hasher); } } -impl<'gcx> HashStable> +impl<'a> HashStable> for mir::VisibilityScope { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use rustc_data_structures::indexed_vec::Idx; self.index().hash_stable(hcx, hasher); } } -impl<'gcx> HashStable> for mir::Promoted { +impl<'a> HashStable> for mir::Promoted { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use rustc_data_structures::indexed_vec::Idx; self.index().hash_stable(hcx, hasher); } } -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for mir::TerminatorKind<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -205,10 +205,10 @@ for mir::TerminatorKind<'gcx> { } } -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for mir::AssertMessage<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -228,10 +228,10 @@ for mir::AssertMessage<'gcx> { impl_stable_hash_for!(struct mir::Statement<'tcx> { source_info, kind }); -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for mir::StatementKind<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -265,12 +265,12 @@ for mir::StatementKind<'gcx> { } } -impl<'gcx, T> HashStable> +impl<'a, 'gcx, T> HashStable> for mir::ValidationOperand<'gcx, T> - where T: HashStable> + where T: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.place.hash_stable(hcx, hasher); @@ -282,9 +282,9 @@ impl<'gcx, T> HashStable> impl_stable_hash_for!(enum mir::ValidationOp { Acquire, Release, Suspend(region_scope) }); -impl<'gcx> HashStable> for mir::Place<'gcx> { +impl<'a, 'gcx> HashStable> for mir::Place<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -301,14 +301,14 @@ impl<'gcx> HashStable> for mir::Place<'gcx> { } } -impl<'gcx, B, V, T> HashStable> +impl<'a, 'gcx, B, V, T> HashStable> for mir::Projection<'gcx, B, V, T> - where B: HashStable>, - V: HashStable>, - T: HashStable> + where B: HashStable>, + V: HashStable>, + T: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let mir::Projection { ref base, @@ -320,13 +320,13 @@ for mir::Projection<'gcx, B, V, T> } } -impl<'gcx, V, T> HashStable> +impl<'a, 'gcx, V, T> HashStable> for mir::ProjectionElem<'gcx, V, T> - where V: HashStable>, - T: HashStable> + where V: HashStable>, + T: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -360,9 +360,9 @@ impl_stable_hash_for!(struct mir::VisibilityScopeInfo { lint_root, safety }); -impl<'gcx> HashStable> for mir::Safety { +impl<'a> HashStable> for mir::Safety { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -377,9 +377,9 @@ impl<'gcx> HashStable> for mir::Safety { } } -impl<'gcx> HashStable> for mir::Operand<'gcx> { +impl<'a, 'gcx> HashStable> for mir::Operand<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -397,9 +397,9 @@ impl<'gcx> HashStable> for mir::Operand<'gcx> { } } -impl<'gcx> HashStable> for mir::Rvalue<'gcx> { +impl<'a, 'gcx> HashStable> for mir::Rvalue<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -457,10 +457,10 @@ impl_stable_hash_for!(enum mir::CastKind { Unsize }); -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for mir::AggregateKind<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -519,9 +519,9 @@ impl_stable_hash_for!(enum mir::NullOp { impl_stable_hash_for!(struct mir::Constant<'tcx> { span, ty, literal }); -impl<'gcx> HashStable> for mir::Literal<'gcx> { +impl<'a, 'gcx> HashStable> for mir::Literal<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -548,9 +548,9 @@ impl_stable_hash_for!(struct mir::ClosureOutlivesRequirement<'tcx> { blame_span }); -impl<'gcx> HashStable> for mir::ClosureOutlivesSubject<'gcx> { +impl<'a, 'gcx> HashStable> for mir::ClosureOutlivesSubject<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -563,3 +563,5 @@ impl<'gcx> HashStable> for mir::ClosureOutlivesSubjec } } } + +impl_stable_hash_for!(struct mir::interpret::GlobalId<'tcx> { instance, promoted }); diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc/ich/impls_syntax.rs index c31a5c9d86d77..03fcf581950af 100644 --- a/src/librustc/ich/impls_syntax.rs +++ b/src/librustc/ich/impls_syntax.rs @@ -28,42 +28,42 @@ use rustc_data_structures::stable_hasher::{HashStable, ToStableHashKey, StableHasher, StableHasherResult}; use rustc_data_structures::accumulate_vec::AccumulateVec; -impl<'gcx> HashStable> for InternedString { +impl<'a> HashStable> for InternedString { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let s: &str = &**self; s.hash_stable(hcx, hasher); } } -impl<'gcx> ToStableHashKey> for InternedString { +impl<'a> ToStableHashKey> for InternedString { type KeyType = InternedString; #[inline] fn to_stable_hash_key(&self, - _: &StableHashingContext<'gcx>) + _: &StableHashingContext<'a>) -> InternedString { self.clone() } } -impl<'gcx> HashStable> for ast::Name { +impl<'a> HashStable> for ast::Name { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.as_str().hash_stable(hcx, hasher); } } -impl<'gcx> ToStableHashKey> for ast::Name { +impl<'a> ToStableHashKey> for ast::Name { type KeyType = InternedString; #[inline] fn to_stable_hash_key(&self, - _: &StableHashingContext<'gcx>) + _: &StableHashingContext<'a>) -> InternedString { self.as_str() } @@ -110,10 +110,10 @@ impl_stable_hash_for!(struct ::syntax::attr::Stability { rustc_const_unstable }); -impl<'gcx> HashStable> +impl<'a> HashStable> for ::syntax::attr::StabilityLevel { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -165,9 +165,9 @@ impl_stable_hash_for!(struct ::syntax::ast::Lifetime { id, span, ident }); impl_stable_hash_for!(enum ::syntax::ast::StrStyle { Cooked, Raw(pounds) }); impl_stable_hash_for!(enum ::syntax::ast::AttrStyle { Outer, Inner }); -impl<'gcx> HashStable> for [ast::Attribute] { +impl<'a> HashStable> for [ast::Attribute] { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { if self.len() == 0 { self.len().hash_stable(hcx, hasher); @@ -190,9 +190,9 @@ impl<'gcx> HashStable> for [ast::Attribute] { } } -impl<'gcx> HashStable> for ast::Attribute { +impl<'a> HashStable> for ast::Attribute { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { // Make sure that these have been filtered out. debug_assert!(self.name().map(|name| !hcx.is_ignored_attr(name)).unwrap_or(true)); @@ -219,10 +219,10 @@ impl<'gcx> HashStable> for ast::Attribute { } } -impl<'gcx> HashStable> +impl<'a> HashStable> for tokenstream::TokenTree { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -241,10 +241,10 @@ for tokenstream::TokenTree { } } -impl<'gcx> HashStable> +impl<'a> HashStable> for tokenstream::TokenStream { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { for sub_tt in self.trees() { sub_tt.hash_stable(hcx, hasher); @@ -252,9 +252,11 @@ for tokenstream::TokenStream { } } -fn hash_token<'gcx, W: StableHasherResult>(token: &token::Token, - hcx: &mut StableHashingContext<'gcx>, - hasher: &mut StableHasher) { +fn hash_token<'a, 'gcx, W: StableHasherResult>( + token: &token::Token, + hcx: &mut StableHashingContext<'a>, + hasher: &mut StableHasher, +) { mem::discriminant(token).hash_stable(hcx, hasher); match *token { token::Token::Eq | @@ -382,9 +384,9 @@ impl_stable_hash_for!(enum ::syntax_pos::FileName { Custom(s) }); -impl<'gcx> HashStable> for FileMap { +impl<'a> HashStable> for FileMap { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let FileMap { name: _, // We hash the smaller name_hash instead of this diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index ea3a1074aa269..082a0428731f8 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -19,31 +19,32 @@ use std::mem; use middle::region; use traits; use ty; +use mir; -impl<'gcx, T> HashStable> +impl<'a, 'gcx, T> HashStable> for &'gcx ty::Slice - where T: HashStable> { + where T: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { (&self[..]).hash_stable(hcx, hasher); } } -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for ty::subst::Kind<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.as_type().hash_stable(hcx, hasher); self.as_region().hash_stable(hcx, hasher); } } -impl<'gcx> HashStable> +impl<'a> HashStable> for ty::RegionKind { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -87,20 +88,20 @@ for ty::RegionKind { } } -impl<'gcx> HashStable> for ty::RegionVid { +impl<'a> HashStable> for ty::RegionVid { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use rustc_data_structures::indexed_vec::Idx; self.index().hash_stable(hcx, hasher); } } -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for ty::adjustment::AutoBorrow<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -115,10 +116,10 @@ for ty::adjustment::AutoBorrow<'gcx> { } } -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for ty::adjustment::Adjust<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -150,10 +151,10 @@ impl_stable_hash_for!(enum ty::BorrowKind { MutBorrow }); -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for ty::UpvarCapture<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -177,11 +178,11 @@ impl_stable_hash_for!(struct ty::FnSig<'tcx> { abi }); -impl<'gcx, T> HashStable> for ty::Binder - where T: HashStable> +impl<'a, 'gcx, T> HashStable> for ty::Binder + where T: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ty::Binder(ref inner) = *self; inner.hash_stable(hcx, hasher); @@ -201,13 +202,13 @@ impl_stable_hash_for!(struct ty::TraitPredicate<'tcx> { trait_ref }); impl_stable_hash_for!(tuple_struct ty::EquatePredicate<'tcx> { t1, t2 }); impl_stable_hash_for!(struct ty::SubtypePredicate<'tcx> { a_is_expected, a, b }); -impl<'gcx, A, B> HashStable> +impl<'a, 'gcx, A, B> HashStable> for ty::OutlivesPredicate - where A: HashStable>, - B: HashStable>, + where A: HashStable>, + B: HashStable>, { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ty::OutlivesPredicate(ref a, ref b) = *self; a.hash_stable(hcx, hasher); @@ -219,9 +220,9 @@ impl_stable_hash_for!(struct ty::ProjectionPredicate<'tcx> { projection_ty, ty } impl_stable_hash_for!(struct ty::ProjectionTy<'tcx> { substs, item_def_id }); -impl<'gcx> HashStable> for ty::Predicate<'gcx> { +impl<'a, 'gcx> HashStable> for ty::Predicate<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -262,9 +263,9 @@ impl<'gcx> HashStable> for ty::Predicate<'gcx> { } } -impl<'gcx> HashStable> for ty::AdtFlags { +impl<'a> HashStable> for ty::AdtFlags { fn hash_stable(&self, - _: &mut StableHashingContext<'gcx>, + _: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { std_hash::Hash::hash(self, hasher); } @@ -289,63 +290,23 @@ impl_stable_hash_for!(struct ty::FieldDef { vis }); -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for ::middle::const_val::ConstVal<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use middle::const_val::ConstVal::*; - use middle::const_val::ConstAggregate::*; mem::discriminant(self).hash_stable(hcx, hasher); match *self { - Integral(ref value) => { - value.hash_stable(hcx, hasher); - } - Float(ref value) => { - value.hash_stable(hcx, hasher); - } - Str(ref value) => { - value.hash_stable(hcx, hasher); - } - ByteStr(ref value) => { - value.hash_stable(hcx, hasher); - } - Bool(value) => { - value.hash_stable(hcx, hasher); - } - Char(value) => { - value.hash_stable(hcx, hasher); - } - Variant(def_id) => { - def_id.hash_stable(hcx, hasher); - } - Function(def_id, substs) => { - def_id.hash_stable(hcx, hasher); - hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { - substs.hash_stable(hcx, hasher); - }); - } - Aggregate(Struct(ref name_values)) => { - let mut values = name_values.to_vec(); - values.sort_unstable_by_key(|&(ref name, _)| name.clone()); - values.hash_stable(hcx, hasher); - } - Aggregate(Tuple(ref value)) => { - value.hash_stable(hcx, hasher); - } - Aggregate(Array(ref value)) => { - value.hash_stable(hcx, hasher); - } - Aggregate(Repeat(ref value, times)) => { - value.hash_stable(hcx, hasher); - times.hash_stable(hcx, hasher); - } Unevaluated(def_id, substs) => { def_id.hash_stable(hcx, hasher); substs.hash_stable(hcx, hasher); } + Value(ref value) => { + value.hash_stable(hcx, hasher); + } } } } @@ -354,6 +315,70 @@ impl_stable_hash_for!(struct ::middle::const_val::ByteArray<'tcx> { data }); +impl_stable_hash_for!(enum mir::interpret::Value { + ByVal(v), + ByValPair(a, b), + ByRef(ptr, align) +}); + +impl_stable_hash_for!(struct mir::interpret::MemoryPointer { + alloc_id, + offset +}); + +impl<'a> HashStable> for mir::interpret::AllocId { + fn hash_stable( + &self, + hcx: &mut StableHashingContext<'a>, + hasher: &mut StableHasher, + ) { + ty::tls::with_opt(|tcx| { + let tcx = tcx.expect("can't hash AllocIds during hir lowering"); + let interner = tcx + .interpret_interner + .borrow(); + if let Some(def_id) = interner.get_corresponding_static_def_id(*self) { + 0.hash_stable(hcx, hasher); + // statics are unique via their DefId + def_id.hash_stable(hcx, hasher); + } else if let Some(alloc) = interner.get_alloc(*self) { + // not a static, can't be recursive, hash the allocation + 1.hash_stable(hcx, hasher); + alloc.hash_stable(hcx, hasher); + } else if let Some(inst) = interner.get_fn(*self) { + 2.hash_stable(hcx, hasher); + inst.hash_stable(hcx, hasher); + } else { + bug!("no allocation for {}", self); + } + }); + } +} + +impl<'a> HashStable> for mir::interpret::Allocation { + fn hash_stable( + &self, + hcx: &mut StableHashingContext<'a>, + hasher: &mut StableHasher, + ) { + self.bytes.hash_stable(hcx, hasher); + for reloc in self.relocations.iter() { + reloc.hash_stable(hcx, hasher); + } + self.undef_mask.hash_stable(hcx, hasher); + self.align.hash_stable(hcx, hasher); + self.mutable.hash_stable(hcx, hasher); + } +} + +impl_stable_hash_for!(struct mir::interpret::Pointer{primval}); + +impl_stable_hash_for!(enum mir::interpret::PrimVal { + Bytes(b), + Ptr(p), + Undef +}); + impl_stable_hash_for!(struct ty::Const<'tcx> { ty, val @@ -364,10 +389,10 @@ impl_stable_hash_for!(struct ::middle::const_val::ConstEvalErr<'tcx> { kind }); -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for ::middle::const_val::ErrKind<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use middle::const_val::ErrKind::*; @@ -404,6 +429,7 @@ for ::middle::const_val::ErrKind<'gcx> { ErroneousReferencedConstant(ref const_val) => { const_val.hash_stable(hcx, hasher); } + Miri(ref err) => err.hash_stable(hcx, hasher), } } } @@ -417,6 +443,175 @@ impl_stable_hash_for!(struct ty::GenericPredicates<'tcx> { predicates }); +impl<'a, 'gcx> HashStable> +for ::mir::interpret::EvalError<'gcx> { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'a>, + hasher: &mut StableHasher) { + use mir::interpret::EvalErrorKind::*; + + mem::discriminant(&self.kind).hash_stable(hcx, hasher); + + match self.kind { + DanglingPointerDeref | + DoubleFree | + InvalidMemoryAccess | + InvalidFunctionPointer | + InvalidBool | + InvalidDiscriminant | + InvalidNullPointerUsage | + ReadPointerAsBytes | + ReadBytesAsPointer | + InvalidPointerMath | + ReadUndefBytes | + DeadLocal | + ExecutionTimeLimitReached | + StackFrameLimitReached | + OutOfTls | + TlsOutOfBounds | + CalledClosureAsFunction | + VtableForArgumentlessMethod | + ModifiedConstantMemory | + AssumptionNotHeld | + InlineAsm | + ReallocateNonBasePtr | + DeallocateNonBasePtr | + HeapAllocZeroBytes | + Unreachable | + Panic | + ReadFromReturnPointer | + UnimplementedTraitSelection | + TypeckError | + DerefFunctionPointer | + ExecuteMemory | + OverflowingMath => {} + MachineError(ref err) => err.hash_stable(hcx, hasher), + FunctionPointerTyMismatch(a, b) => { + a.hash_stable(hcx, hasher); + b.hash_stable(hcx, hasher) + }, + NoMirFor(ref s) => s.hash_stable(hcx, hasher), + UnterminatedCString(ptr) => ptr.hash_stable(hcx, hasher), + PointerOutOfBounds { + ptr, + access, + allocation_size, + } => { + ptr.hash_stable(hcx, hasher); + access.hash_stable(hcx, hasher); + allocation_size.hash_stable(hcx, hasher) + }, + InvalidBoolOp(bop) => bop.hash_stable(hcx, hasher), + Unimplemented(ref s) => s.hash_stable(hcx, hasher), + ArrayIndexOutOfBounds(sp, a, b) => { + sp.hash_stable(hcx, hasher); + a.hash_stable(hcx, hasher); + b.hash_stable(hcx, hasher) + }, + Math(sp, ref err) => { + sp.hash_stable(hcx, hasher); + err.hash_stable(hcx, hasher) + }, + Intrinsic(ref s) => s.hash_stable(hcx, hasher), + InvalidChar(c) => c.hash_stable(hcx, hasher), + OutOfMemory { + allocation_size, + memory_size, + memory_usage, + } => { + allocation_size.hash_stable(hcx, hasher); + memory_size.hash_stable(hcx, hasher); + memory_usage.hash_stable(hcx, hasher) + }, + AbiViolation(ref s) => s.hash_stable(hcx, hasher), + AlignmentCheckFailed { + required, + has, + } => { + required.hash_stable(hcx, hasher); + has.hash_stable(hcx, hasher) + }, + MemoryLockViolation { + ptr, + len, + frame, + access, + ref lock, + } => { + ptr.hash_stable(hcx, hasher); + len.hash_stable(hcx, hasher); + frame.hash_stable(hcx, hasher); + access.hash_stable(hcx, hasher); + lock.hash_stable(hcx, hasher) + }, + MemoryAcquireConflict { + ptr, + len, + kind, + ref lock, + } => { + ptr.hash_stable(hcx, hasher); + len.hash_stable(hcx, hasher); + kind.hash_stable(hcx, hasher); + lock.hash_stable(hcx, hasher) + }, + InvalidMemoryLockRelease { + ptr, + len, + frame, + ref lock, + } => { + ptr.hash_stable(hcx, hasher); + len.hash_stable(hcx, hasher); + frame.hash_stable(hcx, hasher); + lock.hash_stable(hcx, hasher) + }, + DeallocatedLockedMemory { + ptr, + ref lock, + } => { + ptr.hash_stable(hcx, hasher); + lock.hash_stable(hcx, hasher) + }, + ValidationFailure(ref s) => s.hash_stable(hcx, hasher), + TypeNotPrimitive(ty) => ty.hash_stable(hcx, hasher), + ReallocatedWrongMemoryKind(ref a, ref b) => { + a.hash_stable(hcx, hasher); + b.hash_stable(hcx, hasher) + }, + DeallocatedWrongMemoryKind(ref a, ref b) => { + a.hash_stable(hcx, hasher); + b.hash_stable(hcx, hasher) + }, + IncorrectAllocationInformation(a, b, c, d) => { + a.hash_stable(hcx, hasher); + b.hash_stable(hcx, hasher); + c.hash_stable(hcx, hasher); + d.hash_stable(hcx, hasher) + }, + Layout(lay) => lay.hash_stable(hcx, hasher), + HeapAllocNonPowerOfTwoAlignment(n) => n.hash_stable(hcx, hasher), + PathNotFound(ref v) => v.hash_stable(hcx, hasher), + } + } +} + +impl_stable_hash_for!(enum mir::interpret::Lock { + NoLock, + WriteLock(dl), + ReadLock(v) +}); + +impl_stable_hash_for!(struct mir::interpret::DynamicLifetime { + frame, + region +}); + +impl_stable_hash_for!(enum mir::interpret::AccessKind { + Read, + Write +}); + impl_stable_hash_for!(enum ty::Variance { Covariant, Invariant, @@ -428,9 +623,9 @@ impl_stable_hash_for!(enum ty::adjustment::CustomCoerceUnsized { Struct(index) }); -impl<'gcx> HashStable> for ty::Generics { +impl<'a> HashStable> for ty::Generics { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ty::Generics { parent, @@ -456,10 +651,10 @@ impl<'gcx> HashStable> for ty::Generics { } } -impl<'gcx> HashStable> +impl<'a> HashStable> for ty::RegionParameterDef { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ty::RegionParameterDef { name, @@ -485,12 +680,12 @@ impl_stable_hash_for!(struct ty::TypeParameterDef { synthetic }); -impl<'gcx, T> HashStable> +impl<'a, 'gcx, T> HashStable> for ::middle::resolve_lifetime::Set1 - where T: HashStable> + where T: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use middle::resolve_lifetime::Set1; @@ -541,11 +736,11 @@ impl_stable_hash_for!(enum ty::cast::CastKind { impl_stable_hash_for!(tuple_struct ::middle::region::FirstStatementIndex { idx }); impl_stable_hash_for!(struct ::middle::region::Scope { id, code }); -impl<'gcx> ToStableHashKey> for region::Scope { +impl<'a> ToStableHashKey> for region::Scope { type KeyType = region::Scope; #[inline] - fn to_stable_hash_key(&self, _: &StableHashingContext<'gcx>) -> region::Scope { + fn to_stable_hash_key(&self, _: &StableHashingContext<'a>) -> region::Scope { *self } } @@ -571,11 +766,11 @@ impl_stable_hash_for!(enum ty::BoundRegion { BrEnv }); -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for ty::TypeVariants<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use ty::TypeVariants::*; @@ -669,11 +864,11 @@ impl_stable_hash_for!(struct ty::TypeAndMut<'tcx> { mutbl }); -impl<'gcx> HashStable> +impl<'a, 'gcx> HashStable> for ty::ExistentialPredicate<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -706,9 +901,9 @@ impl_stable_hash_for!(struct ty::Instance<'tcx> { substs }); -impl<'gcx> HashStable> for ty::InstanceDef<'gcx> { +impl<'a, 'gcx> HashStable> for ty::InstanceDef<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); @@ -730,21 +925,21 @@ impl<'gcx> HashStable> for ty::InstanceDef<'gcx> { ty::InstanceDef::ClosureOnceShim { call_once } => { call_once.hash_stable(hcx, hasher); } - ty::InstanceDef::DropGlue(def_id, t) => { + ty::InstanceDef::DropGlue(def_id, ty) => { def_id.hash_stable(hcx, hasher); - t.hash_stable(hcx, hasher); + ty.hash_stable(hcx, hasher); } - ty::InstanceDef::CloneShim(def_id, t) => { + ty::InstanceDef::CloneShim(def_id, ty) => { def_id.hash_stable(hcx, hasher); - t.hash_stable(hcx, hasher); + ty.hash_stable(hcx, hasher); } } } } -impl<'gcx> HashStable> for ty::TraitDef { +impl<'a> HashStable> for ty::TraitDef { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ty::TraitDef { // We already have the def_path_hash below, no need to hash it twice @@ -772,9 +967,9 @@ impl_stable_hash_for!(struct ty::DtorckConstraint<'tcx> { }); -impl<'gcx> HashStable> for ty::CrateVariancesMap { +impl<'a> HashStable> for ty::CrateVariancesMap { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ty::CrateVariancesMap { ref variances, @@ -808,12 +1003,12 @@ impl_stable_hash_for!(enum ty::AssociatedItemContainer { }); -impl<'gcx, T> HashStable> +impl<'a, 'gcx, T> HashStable> for ty::steal::Steal - where T: HashStable> + where T: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.borrow().hash_stable(hcx, hasher); } @@ -835,10 +1030,10 @@ impl_stable_hash_for!(enum ::middle::privacy::AccessLevel { Public }); -impl<'gcx> HashStable> +impl<'a> HashStable> for ::middle::privacy::AccessLevels { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { let ::middle::privacy::AccessLevels { @@ -865,10 +1060,10 @@ impl_stable_hash_for!(tuple_struct ::middle::reachable::ReachableSet { reachable_set }); -impl<'gcx, N> HashStable> -for traits::Vtable<'gcx, N> where N: HashStable> { +impl<'a, 'gcx, N> HashStable> +for traits::Vtable<'gcx, N> where N: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use traits::Vtable::*; @@ -887,10 +1082,10 @@ for traits::Vtable<'gcx, N> where N: HashStable> { } } -impl<'gcx, N> HashStable> -for traits::VtableImplData<'gcx, N> where N: HashStable> { +impl<'a, 'gcx, N> HashStable> +for traits::VtableImplData<'gcx, N> where N: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let traits::VtableImplData { impl_def_id, @@ -903,10 +1098,10 @@ for traits::VtableImplData<'gcx, N> where N: HashStable HashStable> -for traits::VtableAutoImplData where N: HashStable> { +impl<'a, 'gcx, N> HashStable> +for traits::VtableAutoImplData where N: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let traits::VtableAutoImplData { trait_def_id, @@ -917,10 +1112,10 @@ for traits::VtableAutoImplData where N: HashStable } } -impl<'gcx, N> HashStable> -for traits::VtableObjectData<'gcx, N> where N: HashStable> { +impl<'a, 'gcx, N> HashStable> +for traits::VtableObjectData<'gcx, N> where N: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let traits::VtableObjectData { upcast_trait_ref, @@ -933,10 +1128,10 @@ for traits::VtableObjectData<'gcx, N> where N: HashStable HashStable> -for traits::VtableBuiltinData where N: HashStable> { +impl<'a, 'gcx, N> HashStable> +for traits::VtableBuiltinData where N: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let traits::VtableBuiltinData { ref nested, @@ -945,10 +1140,10 @@ for traits::VtableBuiltinData where N: HashStable> } } -impl<'gcx, N> HashStable> -for traits::VtableClosureData<'gcx, N> where N: HashStable> { +impl<'a, 'gcx, N> HashStable> +for traits::VtableClosureData<'gcx, N> where N: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let traits::VtableClosureData { closure_def_id, @@ -961,10 +1156,10 @@ for traits::VtableClosureData<'gcx, N> where N: HashStable HashStable> -for traits::VtableFnPointerData<'gcx, N> where N: HashStable> { +impl<'a, 'gcx, N> HashStable> +for traits::VtableFnPointerData<'gcx, N> where N: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let traits::VtableFnPointerData { fn_ty, @@ -975,10 +1170,10 @@ for traits::VtableFnPointerData<'gcx, N> where N: HashStable HashStable> -for traits::VtableGeneratorData<'gcx, N> where N: HashStable> { +impl<'a, 'gcx, N> HashStable> +for traits::VtableGeneratorData<'gcx, N> where N: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let traits::VtableGeneratorData { closure_def_id, diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 075ee0b8c7c28..fb0d3dc41e5e3 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -47,6 +47,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 143d2c2ea28bb..14646f886fbd2 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -16,6 +16,12 @@ use lint::{LintPass, LateLintPass, LintArray}; +declare_lint! { + pub EXCEEDING_BITSHIFTS, + Deny, + "shift exceeds the type's number of bits" +} + declare_lint! { pub CONST_ERR, Warn, @@ -246,6 +252,12 @@ declare_lint! { "raw pointer to an inference variable" } +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)] @@ -254,6 +266,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 4bc37747f2a76..0a9e71b5655aa 100644 --- a/src/librustc/lint/levels.rs +++ b/src/librustc/lint/levels.rs @@ -391,10 +391,10 @@ impl LintLevelMap { } } -impl<'gcx> HashStable> for LintLevelMap { +impl<'a> HashStable> for LintLevelMap { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let LintLevelMap { ref sets, diff --git a/src/librustc/macros.rs b/src/librustc/macros.rs index f0285d6a93782..9a394e524817b 100644 --- a/src/librustc/macros.rs +++ b/src/librustc/macros.rs @@ -73,10 +73,10 @@ macro_rules! __impl_stable_hash_field { #[macro_export] macro_rules! impl_stable_hash_for { (enum $enum_name:path { $( $variant:ident $( ( $($arg:ident),* ) )* ),* }) => { - impl<'tcx> ::rustc_data_structures::stable_hasher::HashStable<$crate::ich::StableHashingContext<'tcx>> for $enum_name { + impl<'a, 'tcx> ::rustc_data_structures::stable_hasher::HashStable<$crate::ich::StableHashingContext<'a>> for $enum_name { #[inline] fn hash_stable(&self, - __ctx: &mut $crate::ich::StableHashingContext<'tcx>, + __ctx: &mut $crate::ich::StableHashingContext<'a>, __hasher: &mut ::rustc_data_structures::stable_hasher::StableHasher) { use $enum_name::*; ::std::mem::discriminant(self).hash_stable(__ctx, __hasher); @@ -92,10 +92,10 @@ macro_rules! impl_stable_hash_for { } }; (struct $struct_name:path { $($field:ident),* }) => { - impl<'tcx> ::rustc_data_structures::stable_hasher::HashStable<$crate::ich::StableHashingContext<'tcx>> for $struct_name { + impl<'a, 'tcx> ::rustc_data_structures::stable_hasher::HashStable<$crate::ich::StableHashingContext<'a>> for $struct_name { #[inline] fn hash_stable(&self, - __ctx: &mut $crate::ich::StableHashingContext<'tcx>, + __ctx: &mut $crate::ich::StableHashingContext<'a>, __hasher: &mut ::rustc_data_structures::stable_hasher::StableHasher) { let $struct_name { $(ref $field),* @@ -106,10 +106,10 @@ macro_rules! impl_stable_hash_for { } }; (tuple_struct $struct_name:path { $($field:ident),* }) => { - impl<'tcx> ::rustc_data_structures::stable_hasher::HashStable<$crate::ich::StableHashingContext<'tcx>> for $struct_name { + impl<'a, 'tcx> ::rustc_data_structures::stable_hasher::HashStable<$crate::ich::StableHashingContext<'a>> for $struct_name { #[inline] fn hash_stable(&self, - __ctx: &mut $crate::ich::StableHashingContext<'tcx>, + __ctx: &mut $crate::ich::StableHashingContext<'a>, __hasher: &mut ::rustc_data_structures::stable_hasher::StableHasher) { let $struct_name ( $(ref $field),* @@ -125,11 +125,11 @@ macro_rules! impl_stable_hash_for { macro_rules! impl_stable_hash_for_spanned { ($T:path) => ( - impl<'tcx> HashStable> for ::syntax::codemap::Spanned<$T> + impl<'a, 'tcx> HashStable> for ::syntax::codemap::Spanned<$T> { #[inline] fn hash_stable(&self, - hcx: &mut StableHashingContext<'tcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.node.hash_stable(hcx, hasher); self.span.hash_stable(hcx, hasher); diff --git a/src/librustc/middle/borrowck.rs b/src/librustc/middle/borrowck.rs index 380f79361e27f..6f5791ed5d71b 100644 --- a/src/librustc/middle/borrowck.rs +++ b/src/librustc/middle/borrowck.rs @@ -20,9 +20,9 @@ pub struct BorrowCheckResult { pub used_mut_nodes: FxHashSet, } -impl<'gcx> HashStable> for BorrowCheckResult { +impl<'a> HashStable> for BorrowCheckResult { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let BorrowCheckResult { ref used_mut_nodes, diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index 440af39a0d469..35c51c0c206b0 100644 --- a/src/librustc/middle/const_val.rs +++ b/src/librustc/middle/const_val.rs @@ -14,12 +14,11 @@ 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 serialize; use syntax_pos::Span; use std::borrow::Cow; @@ -28,16 +27,8 @@ 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>), + Value(Value), } #[derive(Copy, Clone, Debug, Hash, RustcEncodable, Eq, PartialEq)] @@ -47,33 +38,34 @@ pub struct ByteArray<'tcx> { impl<'tcx> serialize::UseSpecializedDecodable for ByteArray<'tcx> {} -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] -pub enum ConstAggregate<'tcx> { - Struct(&'tcx [(ast::Name, &'tcx ty::Const<'tcx>)]), - Tuple(&'tcx [&'tcx ty::Const<'tcx>]), - Array(&'tcx [&'tcx ty::Const<'tcx>]), - Repeat(&'tcx ty::Const<'tcx>, u64), -} - -impl<'tcx> Encodable for ConstAggregate<'tcx> { - fn encode(&self, _: &mut S) -> Result<(), S::Error> { - bug!("should never encode ConstAggregate::{:?}", self) +impl<'tcx> ConstVal<'tcx> { + pub fn to_u128(&self) -> Option { + match *self { + ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))) => { + Some(b) + }, + _ => None, + } } -} - -impl<'tcx> Decodable for ConstAggregate<'tcx> { - fn decode(_: &mut D) -> Result { - bug!("should never decode ConstAggregate") + pub fn unwrap_u64(&self) -> u64 { + match self.to_u128() { + Some(val) => { + assert_eq!(val as u64 as u128, val); + val as u64 + }, + None => bug!("expected constant u64, got {:#?}", self), + } } -} - -impl<'tcx> ConstVal<'tcx> { - pub fn to_const_int(&self) -> Option { + pub fn unwrap_usize<'a, 'gcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> ConstUsize { 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))) => { + assert_eq!(b as u64 as u128, b); + match ConstUsize::new(b as u64, tcx.sess.target.usize_ty) { + Ok(val) => val, + Err(e) => bug!("{:#?} is not a usize {:?}", self, e), + } + }, + _ => bug!("expected constant u64, got {:#?}", self), } } } @@ -108,6 +100,13 @@ pub enum ErrKind<'tcx> { TypeckError, CheckMatchError, + Miri(::mir::interpret::EvalError<'tcx>), +} + +impl<'tcx> From<::mir::interpret::EvalError<'tcx>> for ErrKind<'tcx> { + fn from(err: ::mir::interpret::EvalError<'tcx>) -> ErrKind<'tcx> { + ErrKind::Miri(err) + } } impl<'tcx> From for ErrKind<'tcx> { @@ -170,6 +169,8 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { TypeckError => simple!("type-checking failed"), CheckMatchError => simple!("match-checking failed"), + // FIXME: report a full backtrace + Miri(ref err) => simple!("miri failed: {}", err), } } @@ -186,7 +187,7 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { err = i_err; } - let mut diag = struct_span_err!(tcx.sess, err.span, E0080, "constant evaluation error"); + let mut diag = struct_error(tcx, err.span, "constant evaluation error"); err.note(tcx, primary_span, primary_kind, &mut diag); diag } @@ -221,3 +222,11 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { self.struct_error(tcx, primary_span, primary_kind).emit(); } } + +pub fn struct_error<'a, 'gcx, 'tcx>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, + span: Span, + msg: &str, +) -> DiagnosticBuilder<'gcx> { + struct_span_err!(tcx.sess, span, E0080, "{}", msg) +} diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index dca676130b9a7..04ed87b072d7e 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -278,6 +278,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 a8955723e3ae0..2cb853d0bb101 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -912,8 +912,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { // Always promote `[T; 0]` (even when e.g. borrowed mutably). let promotable = match expr_ty.sty { - ty::TyArray(_, len) if - len.val.to_const_int().and_then(|i| i.to_u64()) == Some(0) => true, + ty::TyArray(_, len) if len.val.to_u128() == Some(0) => true, _ => promotable, }; diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 049bf4470cbc6..ac52ce93fdd28 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -1403,9 +1403,9 @@ pub fn provide(providers: &mut Providers) { }; } -impl<'gcx> HashStable> for ScopeTree { +impl<'a> HashStable> for ScopeTree { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ScopeTree { root_body, diff --git a/src/librustc/mir/cache.rs b/src/librustc/mir/cache.rs index efc2f647cfdf5..45cb70d007068 100644 --- a/src/librustc/mir/cache.rs +++ b/src/librustc/mir/cache.rs @@ -35,9 +35,9 @@ impl serialize::Decodable for Cache { } } -impl<'gcx> HashStable> for Cache { +impl<'a> HashStable> for Cache { fn hash_stable(&self, - _: &mut StableHashingContext<'gcx>, + _: &mut StableHashingContext<'a>, _: &mut StableHasher) { // do nothing } diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs index 9ebfe25c107a9..977e617968aa4 100644 --- a/src/librustc/mir/interpret/error.rs +++ b/src/librustc/mir/interpret/error.rs @@ -12,7 +12,7 @@ use rustc_const_math::ConstMathErr; use syntax::codemap::Span; use backtrace::Backtrace; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct EvalError<'tcx> { pub kind: EvalErrorKind<'tcx>, pub backtrace: Option, @@ -31,11 +31,11 @@ impl<'tcx> From> for EvalError<'tcx> { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum EvalErrorKind<'tcx> { /// This variant is used by machines to signal their own errors that do not /// match an existing variant - MachineError(Box), + MachineError(String), FunctionPointerTyMismatch(FnSig<'tcx>, FnSig<'tcx>), NoMirFor(String), UnterminatedCString(MemoryPointer), @@ -132,7 +132,7 @@ impl<'tcx> Error for EvalError<'tcx> { fn description(&self) -> &str { use self::EvalErrorKind::*; match self.kind { - MachineError(ref inner) => inner.description(), + MachineError(ref inner) => inner, FunctionPointerTyMismatch(..) => "tried to call a function through a function pointer of a different type", InvalidMemoryAccess => @@ -247,14 +247,6 @@ impl<'tcx> Error for EvalError<'tcx> { "encountered constants with type errors, stopping evaluation", } } - - fn cause(&self) -> Option<&Error> { - use self::EvalErrorKind::*; - match self.kind { - MachineError(ref inner) => Some(&**inner), - _ => None, - } - } } impl<'tcx> fmt::Display for EvalError<'tcx> { @@ -294,8 +286,8 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { write!(f, "tried to reallocate memory from {} to {}", old, new), DeallocatedWrongMemoryKind(ref old, ref new) => write!(f, "tried to deallocate {} memory but gave {} as the kind", old, new), - Math(span, ref err) => - write!(f, "{:?} at {:?}", err, span), + Math(_, ref err) => + write!(f, "{}", err.description()), Intrinsic(ref err) => write!(f, "{}", err), InvalidChar(c) => diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs index 8ffea62f6be51..a41ad94f00a0b 100644 --- a/src/librustc/mir/interpret/mod.rs +++ b/src/librustc/mir/interpret/mod.rs @@ -41,7 +41,7 @@ pub enum AccessKind { } /// Uniquely identifies a specific constant or static. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, RustcEncodable, RustcDecodable)] pub struct GlobalId<'tcx> { /// For a constant or static, the `Instance` of the item itself. /// For a promoted global, the `Instance` of the function they belong to. @@ -101,7 +101,7 @@ pub trait PointerArithmetic: layout::HasDataLayout { impl PointerArithmetic for T {} -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash)] pub struct MemoryPointer { pub alloc_id: AllocId, pub offset: u64, @@ -148,13 +148,16 @@ impl<'tcx> MemoryPointer { #[derive(Copy, Clone, Default, Eq, Hash, Ord, PartialEq, PartialOrd, Debug)] pub struct AllocId(pub u64); +impl ::rustc_serialize::UseSpecializedEncodable for AllocId {} +impl ::rustc_serialize::UseSpecializedDecodable for AllocId {} + impl fmt::Display for AllocId { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) } } -#[derive(Debug, Eq, PartialEq, Hash)] +#[derive(Debug, Eq, PartialEq, Hash, RustcEncodable, RustcDecodable)] pub struct Allocation { /// The actual bytes of the allocation. /// Note that the bytes of a pointer represent the offset of the pointer @@ -166,6 +169,8 @@ pub struct Allocation { pub undef_mask: UndefMask, /// The alignment of the allocation to detect unaligned reads. pub align: Align, + /// Whether the allocation should be put into mutable memory when translating via llvm + pub mutable: bool, } impl Allocation { @@ -177,6 +182,7 @@ impl Allocation { relocations: BTreeMap::new(), undef_mask, align: Align::from_bytes(1, 1).unwrap(), + mutable: false, } } } @@ -188,12 +194,14 @@ impl Allocation { type Block = u64; const BLOCK_SIZE: u64 = 64; -#[derive(Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Clone, Debug, Eq, PartialEq, Hash, RustcEncodable, RustcDecodable)] pub struct UndefMask { blocks: Vec, len: u64, } +impl_stable_hash_for!(struct mir::interpret::UndefMask{blocks, len}); + impl UndefMask { pub fn new(size: u64) -> Self { let mut m = UndefMask { diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc/mir/interpret/value.rs index 0bfff2a80e678..c00956c0a8570 100644 --- a/src/librustc/mir/interpret/value.rs +++ b/src/librustc/mir/interpret/value.rs @@ -1,6 +1,7 @@ #![allow(unknown_lints)] use ty::layout::{Align, HasDataLayout}; +use ty; use super::{EvalResult, MemoryPointer, PointerArithmetic}; use syntax::ast::FloatTy; @@ -29,13 +30,22 @@ pub fn bytes_to_f64(bits: u128) -> ConstFloat { /// For optimization of a few very common cases, there is also a representation for a pair of /// primitive values (`ByValPair`). It allows Miri to avoid making allocations for checked binary /// operations and fat pointers. This idea was taken from rustc's trans. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash)] pub enum Value { ByRef(Pointer, Align), ByVal(PrimVal), ByValPair(PrimVal, PrimVal), } +impl<'tcx> ty::TypeFoldable<'tcx> for Value { + fn super_fold_with<'gcx: 'tcx, F: ty::fold::TypeFolder<'gcx, 'tcx>>(&self, _: &mut F) -> Self { + *self + } + fn super_visit_with>(&self, _: &mut V) -> bool { + false + } +} + /// A wrapper type around `PrimVal` that cannot be turned back into a `PrimVal` accidentally. /// This type clears up a few APIs where having a `PrimVal` argument for something that is /// potentially an integer pointer or a pointer to an allocation was unclear. @@ -43,9 +53,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 +148,7 @@ impl ::std::convert::From for Pointer { /// `memory::Allocation`. It is in many ways like a small chunk of a `Allocation`, up to 8 bytes in /// size. Like a range of bytes in an `Allocation`, a `PrimVal` can either represent the raw bytes /// of a simple value, a pointer into another `Allocation`, or be undefined. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash)] pub enum PrimVal { /// The raw bytes of a simple value. Bytes(u128), diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 3aa94b3469942..61661c0c58025 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -15,7 +15,7 @@ use graphviz::IntoCow; use middle::const_val::ConstVal; use middle::region; -use rustc_const_math::{ConstUsize, ConstInt, ConstMathErr}; +use rustc_const_math::{ConstUsize, ConstMathErr}; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; use rustc_data_structures::control_flow_graph::dominators::{Dominators, dominators}; use rustc_data_structures::control_flow_graph::{GraphPredecessors, GraphSuccessors}; @@ -24,13 +24,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}; @@ -694,7 +695,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 @@ -826,7 +827,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, @@ -1055,12 +1056,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() @@ -1434,7 +1439,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 }) }, @@ -1754,7 +1760,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) @@ -1764,25 +1770,48 @@ impl<'tcx> Debug for Literal<'tcx> { } /// Write a `ConstVal` in a way closer to the original source code than the `Debug` output. -fn fmt_const_val(fmt: &mut W, const_val: &ConstVal) -> fmt::Result { +fn fmt_const_val(fmt: &mut W, const_val: &ty::Const) -> fmt::Result { use middle::const_val::ConstVal::*; - match *const_val { - Float(f) => write!(fmt, "{:?}", f), - Integral(n) => write!(fmt, "{}", n), - Str(s) => write!(fmt, "{:?}", s), - ByteStr(bytes) => { - let escaped: String = bytes.data - .iter() - .flat_map(|&ch| ascii::escape_default(ch).map(|c| c as char)) - .collect(); - write!(fmt, "b\"{}\"", escaped) - } - Bool(b) => write!(fmt, "{:?}", b), - Char(c) => write!(fmt, "{:?}", c), - Variant(def_id) | - Function(def_id, _) => write!(fmt, "{}", item_path_str(def_id)), - Aggregate(_) => bug!("`ConstVal::{:?}` should not be in MIR", const_val), - Unevaluated(..) => write!(fmt, "{:?}", const_val) + match const_val.val { + Unevaluated(..) => write!(fmt, "{:?}", const_val), + Value(val) => print_miri_value(val, const_val.ty, fmt), + } +} + +pub fn print_miri_value(value: Value, ty: Ty, f: &mut W) -> fmt::Result { + use ty::TypeVariants::*; + use rustc_const_math::ConstFloat; + match (value, &ty.sty) { + (Value::ByVal(PrimVal::Bytes(0)), &TyBool) => write!(f, "false"), + (Value::ByVal(PrimVal::Bytes(1)), &TyBool) => write!(f, "true"), + (Value::ByVal(PrimVal::Bytes(bits)), &TyFloat(fty)) => + write!(f, "{}", ConstFloat { bits, ty: fty }), + (Value::ByVal(PrimVal::Bytes(n)), &TyUint(ui)) => write!(f, "{:?}{}", n, ui), + (Value::ByVal(PrimVal::Bytes(n)), &TyInt(i)) => write!(f, "{:?}{}", n as i128, i), + (Value::ByVal(PrimVal::Bytes(n)), &TyChar) => + write!(f, "{:?}", ::std::char::from_u32(n as u32).unwrap()), + (Value::ByVal(PrimVal::Undef), &TyFnDef(did, _)) => + write!(f, "{}", item_path_str(did)), + (Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(len)), &TyRef(_, TypeAndMut { + ty: &ty::TyS { sty: TyStr, .. }, .. + })) => { + ty::tls::with(|tcx| { + let alloc = tcx + .interpret_interner + .borrow() + .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), } } @@ -2366,6 +2395,15 @@ impl<'tcx, B, V, T> TypeFoldable<'tcx> for Projection<'tcx, B, V, T> } } +impl<'tcx> TypeFoldable<'tcx> for Field { + fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, _: &mut F) -> Self { + *self + } + fn super_visit_with>(&self, _: &mut V) -> bool { + false + } +} + impl<'tcx> TypeFoldable<'tcx> for Constant<'tcx> { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { Constant { diff --git a/src/librustc/mir/mono.rs b/src/librustc/mir/mono.rs index efdf4066815f4..7c75597e477e8 100644 --- a/src/librustc/mir/mono.rs +++ b/src/librustc/mir/mono.rs @@ -25,9 +25,9 @@ pub enum MonoItem<'tcx> { GlobalAsm(NodeId), } -impl<'tcx> HashStable> for MonoItem<'tcx> { +impl<'a, 'tcx> HashStable> for MonoItem<'tcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'tcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { ::std::mem::discriminant(self).hash_stable(hcx, hasher); @@ -133,9 +133,9 @@ impl<'tcx> CodegenUnit<'tcx> { } } -impl<'tcx> HashStable> for CodegenUnit<'tcx> { +impl<'a, 'tcx> HashStable> for CodegenUnit<'tcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'tcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let CodegenUnit { ref items, diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index 23f360d5c3922..e57de8c671c36 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -70,7 +70,7 @@ impl<'a, 'gcx, 'tcx> PlaceTy<'tcx> { PlaceTy::Ty { ty: match ty.sty { ty::TyArray(inner, size) => { - let size = size.val.to_const_int().unwrap().to_u64().unwrap(); + let size = size.val.unwrap_u64(); let len = size - (from as u64) - (to as u64); tcx.mk_array(inner, len) } diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 58067931d562e..dc09bb31156de 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -243,12 +243,6 @@ macro_rules! make_mir_visitor { self.super_generator_interior(interior); } - fn visit_const_int(&mut self, - const_int: &ConstInt, - _: Location) { - self.super_const_int(const_int); - } - fn visit_const_usize(&mut self, const_usize: & $($mutability)* ConstUsize, _: Location) { @@ -419,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); } @@ -785,9 +776,6 @@ macro_rules! make_mir_visitor { _substs: & $($mutability)* ClosureSubsts<'tcx>) { } - fn super_const_int(&mut self, _const_int: &ConstInt) { - } - fn super_const_usize(&mut self, _const_usize: & $($mutability)* ConstUsize) { } diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 69b86416afab0..5a24fc9723b49 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -103,10 +103,10 @@ impl_stable_hash_for!(enum self::OutputType { DepInfo }); -impl<'tcx> ToStableHashKey> for OutputType { +impl<'a, 'tcx> ToStableHashKey> for OutputType { type KeyType = OutputType; #[inline] - fn to_stable_hash_key(&self, _: &StableHashingContext<'tcx>) -> Self::KeyType { + fn to_stable_hash_key(&self, _: &StableHashingContext<'a>) -> Self::KeyType { *self } } diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 1aca687af2bf3..968efe90a7822 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; @@ -814,7 +813,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 93e33836818ce..680c6f4d55d77 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, InferOk}; +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; @@ -525,17 +527,34 @@ fn process_predicate<'a, 'gcx, 'tcx>( } Some(param_env) => { match selcx.tcx().lift_to_global(&substs) { + Some(substs) => { + let instance = ty::Instance::resolve( + selcx.tcx().global_tcx(), + param_env, + def_id, + substs, + ); + if let Some(instance) = instance { + let cid = GlobalId { + instance, + promoted: None, + }; + match selcx.tcx().at(obligation.cause.span) + .const_eval(param_env.and(cid)) { + Ok(_) => Ok(Some(vec![])), + Err(err) => Err(CodeSelectionError(ConstEvalFailure(err))) + } + } else { + Err(CodeSelectionError(ConstEvalFailure(ConstEvalErr { + span: obligation.cause.span, + kind: ErrKind::UnimplementedConstVal("could not resolve"), + }))) + } + }, 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 fd47e09aad7f9..d01805c2dea3f 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -18,8 +18,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 3342d13dd6e5f..c1e0790ca32e8 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -28,6 +28,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}; @@ -335,12 +336,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); @@ -348,18 +354,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 51d2bc8701a4e..b681512ec834f 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -40,6 +40,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 rustc_data_structures::snapshot_vec::{SnapshotVecDelegate, SnapshotVec}; @@ -740,11 +741,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 834389e5d009c..ec915be04d38d 100644 --- a/src/librustc/traits/specialize/specialization_graph.rs +++ b/src/librustc/traits/specialize/specialization_graph.rs @@ -394,9 +394,9 @@ pub fn ancestors(tcx: TyCtxt, } } -impl<'gcx> HashStable> for Children { +impl<'a> HashStable> for Children { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let Children { ref nonblanket_impls, diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 87742fe91627e..a1937f78cd4f0 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,7 @@ use rustc_data_structures::stable_hasher::{HashStable, hash_stable_hashmap, StableHasher, StableHasherResult, StableVec}; use arena::{TypedArena, DroplessArena}; -use rustc_const_math::{ConstInt, ConstUsize}; +use rustc_const_math::ConstUsize; use rustc_data_structures::indexed_vec::IndexVec; use std::any::Any; use std::borrow::Borrow; @@ -674,9 +675,9 @@ impl<'tcx> TypeckTables<'tcx> { } } -impl<'gcx> HashStable> for TypeckTables<'gcx> { +impl<'a, 'gcx> HashStable> for TypeckTables<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ty::TypeckTables { local_id_root, @@ -904,12 +905,18 @@ pub struct InterpretInterner<'tcx> { /// Allows obtaining const allocs via a unique identifier alloc_by_id: FxHashMap, + /// Reverse map of `alloc_cache` + global_cache: FxHashMap, + /// The AllocId to assign to the next new regular allocation. /// Always incremented, never gets smaller. next_id: interpret::AllocId, - /// Allows checking whether a constant already has an allocation - alloc_cache: FxHashMap, interpret::AllocId>, + /// Allows checking whether a static already has an allocation + /// + /// This is only important for detecting statics referring to themselves + // FIXME(oli-obk) move it to the EvalContext? + alloc_cache: FxHashMap, /// A cache for basic byte allocations keyed by their contents. This is used to deduplicate /// allocations for string and bytestring literals. @@ -944,21 +951,29 @@ impl<'tcx> InterpretInterner<'tcx> { pub fn get_cached( &self, - global_id: interpret::GlobalId<'tcx>, + static_id: DefId, ) -> Option { - self.alloc_cache.get(&global_id).cloned() + self.alloc_cache.get(&static_id).cloned() } pub fn cache( &mut self, - global_id: interpret::GlobalId<'tcx>, - ptr: interpret::AllocId, + 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); + self.global_cache.insert(alloc_id, static_id); + if let Some(old) = self.alloc_cache.insert(static_id, alloc_id) { + bug!("tried to cache {:?}, but was already existing as {:#?}", static_id, old); } } + pub fn get_corresponding_static_def_id( + &self, + ptr: interpret::AllocId, + ) -> Option { + self.global_cache.get(&ptr).cloned() + } + pub fn intern_at_reserved( &mut self, id: interpret::AllocId, @@ -1247,6 +1262,36 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self.get_lang_items(LOCAL_CRATE) } + pub fn is_binop_lang_item(&self, def_id: DefId) -> Option<(mir::BinOp, bool)> { + let items = self.lang_items(); + let def_id = Some(def_id); + if items.i128_add_fn() == def_id { Some((mir::BinOp::Add, false)) } + else if items.u128_add_fn() == def_id { Some((mir::BinOp::Add, false)) } + else if items.i128_sub_fn() == def_id { Some((mir::BinOp::Sub, false)) } + else if items.u128_sub_fn() == def_id { Some((mir::BinOp::Sub, false)) } + else if items.i128_mul_fn() == def_id { Some((mir::BinOp::Mul, false)) } + else if items.u128_mul_fn() == def_id { Some((mir::BinOp::Mul, false)) } + else if items.i128_div_fn() == def_id { Some((mir::BinOp::Div, false)) } + else if items.u128_div_fn() == def_id { Some((mir::BinOp::Div, false)) } + else if items.i128_rem_fn() == def_id { Some((mir::BinOp::Rem, false)) } + else if items.u128_rem_fn() == def_id { Some((mir::BinOp::Rem, false)) } + else if items.i128_shl_fn() == def_id { Some((mir::BinOp::Shl, false)) } + else if items.u128_shl_fn() == def_id { Some((mir::BinOp::Shl, false)) } + else if items.i128_shr_fn() == def_id { Some((mir::BinOp::Shr, false)) } + else if items.u128_shr_fn() == def_id { Some((mir::BinOp::Shr, false)) } + else if items.i128_addo_fn() == def_id { Some((mir::BinOp::Add, true)) } + else if items.u128_addo_fn() == def_id { Some((mir::BinOp::Add, true)) } + else if items.i128_subo_fn() == def_id { Some((mir::BinOp::Sub, true)) } + else if items.u128_subo_fn() == def_id { Some((mir::BinOp::Sub, true)) } + else if items.i128_mulo_fn() == def_id { Some((mir::BinOp::Mul, true)) } + else if items.u128_mulo_fn() == def_id { Some((mir::BinOp::Mul, true)) } + else if items.i128_shlo_fn() == def_id { Some((mir::BinOp::Shl, true)) } + else if items.u128_shlo_fn() == def_id { Some((mir::BinOp::Shl, true)) } + else if items.i128_shro_fn() == def_id { Some((mir::BinOp::Shr, true)) } + else if items.u128_shro_fn() == def_id { Some((mir::BinOp::Shr, true)) } + else { None } + } + pub fn stability(self) -> Rc> { self.stability_index(LOCAL_CRATE) } @@ -1316,7 +1361,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, @@ -1996,7 +2041,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { pub fn mk_array_const_usize(self, ty: Ty<'tcx>, n: ConstUsize) -> Ty<'tcx> { self.mk_ty(TyArray(ty, self.mk_const(ty::Const { - val: ConstVal::Integral(ConstInt::Usize(n)), + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(n.as_u64().into()))), ty: self.types.usize }))) } diff --git a/src/librustc/ty/error.rs b/src/librustc/ty/error.rs index d2152024cff07..7a8665fae389b 100644 --- a/src/librustc/ty/error.rs +++ b/src/librustc/ty/error.rs @@ -12,6 +12,7 @@ use hir::def_id::DefId; use infer::type_variable; use middle::const_val::ConstVal; use ty::{self, BoundRegion, DefIdTree, Region, Ty, TyCtxt}; +use mir::interpret::{Value, PrimVal}; use std::fmt; use syntax::abi; @@ -19,8 +20,6 @@ use syntax::ast; use errors::DiagnosticBuilder; use syntax_pos::Span; -use rustc_const_math::ConstInt; - use hir; #[derive(Clone, Copy, Debug)] @@ -193,10 +192,10 @@ impl<'a, 'gcx, 'lcx, 'tcx> ty::TyS<'tcx> { ty::TyAdt(def, _) => format!("{} `{}`", def.descr(), tcx.item_path_str(def.did)), ty::TyForeign(def_id) => format!("extern type `{}`", tcx.item_path_str(def_id)), ty::TyArray(_, n) => { - if let ConstVal::Integral(ConstInt::Usize(n)) = n.val { - format!("array of {} elements", n) - } else { - "array".to_string() + match n.val { + ConstVal::Value(Value::ByVal(PrimVal::Bytes(n))) => + format!("array of {} elements", n), + _ => "array".to_string(), } } ty::TySlice(_) => "slice".to_string(), diff --git a/src/librustc/ty/fast_reject.rs b/src/librustc/ty/fast_reject.rs index 138f6af77c658..c4ee29b04f368 100644 --- a/src/librustc/ty/fast_reject.rs +++ b/src/librustc/ty/fast_reject.rs @@ -149,12 +149,12 @@ impl SimplifiedTypeGen { } } -impl<'gcx, D> HashStable> for SimplifiedTypeGen +impl<'a, 'gcx, D> HashStable> for SimplifiedTypeGen where D: Copy + Debug + Ord + Eq + Hash + - HashStable>, + HashStable>, { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { diff --git a/src/librustc/ty/flags.rs b/src/librustc/ty/flags.rs index 63c646dbd2310..bc27ba8a357a2 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}; @@ -212,30 +212,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 0072512464a0e..6936e93994829 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) => { - if len.val.to_const_int().and_then(|i| i.to_u64()) == Some(0) { + if len.val.to_u128() == Some(0) { DefIdForest::empty() } else { ty.uninhabited_from(visited, tcx) diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs index 63bf52a9bdf78..37041a975b437 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 50efb73003731..9c7b41b92ff96 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -342,7 +342,7 @@ impl AddAssign for Size { /// Each field is a power of two, giving the alignment a maximum /// value of 2(28 - 1), which is limited by LLVM to a i32, with /// a maximum capacity of 231 - 1 or 2147483647. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] pub struct Align { abi: u8, pref: u8, @@ -946,7 +946,7 @@ impl<'a, 'tcx> LayoutDetails { enum StructKind { /// A tuple, closure, or univariant which cannot be coerced to unsized. AlwaysSized, - /// A univariant, the last field of which may be coerced to unsized. + /// A univariant, the last field of which fn compute_uncachedmay be coerced to unsized. MaybeUnsized, /// A univariant, but with a prefix of an arbitrary size & alignment (e.g. enum tag). Prefixed(Size, Align), @@ -1231,7 +1231,7 @@ impl<'a, 'tcx> LayoutDetails { } let element = cx.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))?; @@ -2341,9 +2341,9 @@ impl<'a, 'tcx> TyLayout<'tcx> { } } -impl<'gcx> HashStable> for Variants { +impl<'a> HashStable> for Variants { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use ty::layout::Variants::*; mem::discriminant(self).hash_stable(hcx, hasher); @@ -2377,9 +2377,9 @@ impl<'gcx> HashStable> for Variants { } } -impl<'gcx> HashStable> for FieldPlacement { +impl<'a> HashStable> for FieldPlacement { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use ty::layout::FieldPlacement::*; mem::discriminant(self).hash_stable(hcx, hasher); @@ -2400,9 +2400,9 @@ impl<'gcx> HashStable> for FieldPlacement { } } -impl<'gcx> HashStable> for Abi { +impl<'a> HashStable> for Abi { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use ty::layout::Abi::*; mem::discriminant(self).hash_stable(hcx, hasher); @@ -2427,9 +2427,9 @@ impl<'gcx> HashStable> for Abi { } } -impl<'gcx> HashStable> for Scalar { +impl<'a> HashStable> for Scalar { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let Scalar { value, valid_range: RangeInclusive { start, end } } = *self; value.hash_stable(hcx, hasher); @@ -2470,10 +2470,10 @@ impl_stable_hash_for!(struct ::ty::layout::Size { raw }); -impl<'gcx> HashStable> for LayoutError<'gcx> +impl<'a, 'gcx> HashStable> for LayoutError<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use ty::layout::LayoutError::*; mem::discriminant(self).hash_stable(hcx, hasher); diff --git a/src/librustc/ty/maps/config.rs b/src/librustc/ty/maps/config.rs index 8dedcb24c2fb6..8876867f58908 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 b7b64c9761a8e..8fb1ad0da823b 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 e7e92b8a4288f..22f4d7073a02e 100644 --- a/src/librustc/ty/maps/mod.rs +++ b/src/librustc/ty/maps/mod.rs @@ -16,7 +16,6 @@ use hir::{self, TraitCandidate, ItemLocalId}; 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; +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; @@ -204,8 +205,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>, @@ -416,7 +417,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 4e2421dad2161..327296d613612 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::indexed_vec::{IndexVec, Idx}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder, opaque, @@ -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, }; @@ -360,6 +361,7 @@ impl<'sess> OnDiskCache<'sess> { file_index_to_file: &mut file_index_to_file, file_index_to_stable_id: &self.file_index_to_stable_id, synthetic_expansion_infos: &mut synthetic_expansion_infos, + interpret_alloc_cache: FxHashMap::default(), }; match decode_tagged(&mut decoder, dep_node_index) { @@ -421,6 +423,7 @@ struct CacheDecoder<'a, 'tcx: 'a, 'x> { synthetic_expansion_infos: &'x mut FxHashMap, file_index_to_file: &'x mut FxHashMap>, file_index_to_stable_id: &'x FxHashMap, + interpret_alloc_cache: FxHashMap, } impl<'a, 'tcx, 'x> CacheDecoder<'a, 'tcx, 'x> { @@ -540,6 +543,51 @@ impl<'a, 'tcx: 'a, 'x> ty_codec::TyDecoder<'a, 'tcx> for CacheDecoder<'a, 'tcx, implement_ty_decoder!( CacheDecoder<'a, 'tcx, 'x> ); +impl<'a, 'tcx, 'x> SpecializedDecoder for CacheDecoder<'a, 'tcx, 'x> { + fn specialized_decode(&mut self) -> Result { + const MAX1: usize = usize::max_value() - 1; + let tcx = self.tcx; + let interpret_interner = || tcx.interpret_interner.borrow_mut(); + let pos = TyDecoder::position(self); + match usize::decode(self)? { + ::std::usize::MAX => { + let alloc_id = interpret_interner().reserve(); + trace!("creating alloc id {:?} at {}", alloc_id, pos); + // insert early to allow recursive allocs + self.interpret_alloc_cache.insert(pos, alloc_id); + + let allocation = interpret::Allocation::decode(self)?; + trace!("decoded alloc {:?} {:#?}", alloc_id, allocation); + let allocation = self.tcx.intern_const_alloc(allocation); + interpret_interner().intern_at_reserved(alloc_id, allocation); + + if let Some(glob) = Option::::decode(self)? { + interpret_interner().cache(glob, alloc_id); + } + + Ok(alloc_id) + }, + MAX1 => { + trace!("creating fn alloc id at {}", pos); + let instance = ty::Instance::decode(self)?; + trace!("decoded fn alloc instance: {:?}", instance); + let id = interpret_interner().create_fn_alloc(instance); + trace!("created fn alloc id: {:?}", id); + self.interpret_alloc_cache.insert(pos, id); + Ok(id) + }, + shorthand => { + trace!("loading shorthand {}", shorthand); + if let Some(&alloc_id) = self.interpret_alloc_cache.get(&shorthand) { + return Ok(alloc_id); + } + trace!("shorthand {} not cached, loading entire allocation", shorthand); + // need to load allocation + self.with_position(shorthand, |this| interpret::AllocId::decode(this)) + }, + } + } +} impl<'a, 'tcx, 'x> SpecializedDecoder for CacheDecoder<'a, 'tcx, 'x> { fn specialized_decode(&mut self) -> Result { let tag: u8 = Decodable::decode(self)?; @@ -696,6 +744,7 @@ struct CacheEncoder<'enc, 'a, 'tcx, E> type_shorthands: FxHashMap, usize>, predicate_shorthands: FxHashMap, usize>, expn_info_shorthands: FxHashMap, + interpret_alloc_shorthands: FxHashMap, codemap: CachingCodemapView<'tcx>, file_to_file_index: FxHashMap<*const FileMap, FileMapIndex>, } @@ -728,6 +777,38 @@ impl<'enc, 'a, 'tcx, E> CacheEncoder<'enc, 'a, 'tcx, E> } } +impl<'enc, 'a, 'tcx, E> SpecializedEncoder for CacheEncoder<'enc, 'a, 'tcx, E> + where E: 'enc + ty_codec::TyEncoder +{ + fn specialized_encode(&mut self, alloc_id: &interpret::AllocId) -> Result<(), Self::Error> { + trace!("encoding {:?} at {}", alloc_id, self.position()); + if let Some(shorthand) = self.interpret_alloc_shorthands.get(alloc_id).cloned() { + trace!("encoding {:?} as shorthand to {}", alloc_id, shorthand); + return shorthand.encode(self); + } + let start = self.position(); + // cache the allocation shorthand now, because the allocation itself might recursively + // point to itself. + self.interpret_alloc_shorthands.insert(*alloc_id, start); + let interpret_interner = self.tcx.interpret_interner.borrow(); + if let Some(alloc) = interpret_interner.get_alloc(*alloc_id) { + trace!("encoding {:?} with {:#?}", alloc_id, alloc); + usize::max_value().encode(self)?; + alloc.encode(self)?; + interpret_interner + .get_corresponding_static_def_id(*alloc_id) + .encode(self)?; + } else if let Some(fn_instance) = interpret_interner.get_fn(*alloc_id) { + trace!("encoding {:?} with {:#?}", alloc_id, fn_instance); + (usize::max_value() - 1).encode(self)?; + fn_instance.encode(self)?; + } else { + bug!("alloc id without corresponding allocation: {}", alloc_id); + } + Ok(()) + } +} + impl<'enc, 'a, 'tcx, E> SpecializedEncoder for CacheEncoder<'enc, 'a, 'tcx, E> where E: 'enc + ty_codec::TyEncoder { diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 2b4d2c80c6f9e..fd82a3c53cbe5 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -26,6 +26,7 @@ use middle::lang_items::{FnTraitLangItem, FnMutTraitLangItem, FnOnceTraitLangIte use middle::privacy::AccessLevels; use middle::resolve_lifetime::ObjectLifetimeDefault; use mir::Mir; +use mir::interpret::{GlobalId, Value, PrimVal}; use mir::GeneratorLayout; use session::CrateDisambiguator; use traits; @@ -79,7 +80,7 @@ pub use self::binding::BindingMode; pub use self::binding::BindingMode::*; pub use self::context::{TyCtxt, GlobalArenas, AllArenas, tls, keep_local}; -pub use self::context::{Lift, TypeckTables}; +pub use self::context::{Lift, TypeckTables, InterpretInterner}; pub use self::instance::{Instance, InstanceDef}; @@ -528,9 +529,9 @@ impl<'tcx> TyS<'tcx> { } } -impl<'gcx> HashStable> for ty::TyS<'gcx> { +impl<'a, 'gcx> HashStable> for ty::TyS<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ty::TyS { ref sty, @@ -1367,11 +1368,11 @@ impl<'tcx, T> ParamEnvAnd<'tcx, T> { } } -impl<'gcx, T> HashStable> for ParamEnvAnd<'gcx, T> - where T: HashStable> +impl<'a, 'gcx, T> HashStable> for ParamEnvAnd<'gcx, T> + where T: HashStable> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ParamEnvAnd { ref param_env, @@ -1472,9 +1473,9 @@ impl<'tcx> serialize::UseSpecializedEncodable for &'tcx AdtDef { impl<'tcx> serialize::UseSpecializedDecodable for &'tcx AdtDef {} -impl<'gcx> HashStable> for AdtDef { +impl<'a> HashStable> for AdtDef { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ty::AdtDef { did, @@ -1743,15 +1744,30 @@ impl<'a, 'gcx, 'tcx> AdtDef { let mut discr = prev_discr.map_or(initial, |d| d.wrap_incr()); if let VariantDiscr::Explicit(expr_did) = v.discr { let substs = Substs::identity_for_item(tcx.global_tcx(), expr_did); - match tcx.const_eval(param_env.and((expr_did, substs))) { - Ok(&ty::Const { val: ConstVal::Integral(v), .. }) => { - discr = v; + 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); + use syntax::attr::IntType; + discr = match repr_type { + IntType::SignedInt(int_type) => ConstInt::new_signed( + b as i128, int_type, tcx.sess.target.isize_ty).unwrap(), + IntType::UnsignedInt(uint_type) => ConstInt::new_unsigned( + b, uint_type, tcx.sess.target.usize_ty).unwrap(), + }; } - err => { + _ => { if !expr_did.is_local() { span_bug!(tcx.def_span(expr_did), "variant discriminant evaluation succeeded \ - in its crate but failed locally: {:?}", err); + in its crate but failed locally"); } } } @@ -1783,16 +1799,31 @@ impl<'a, 'gcx, 'tcx> AdtDef { } ty::VariantDiscr::Explicit(expr_did) => { let substs = Substs::identity_for_item(tcx.global_tcx(), expr_did); - match tcx.const_eval(param_env.and((expr_did, substs))) { - Ok(&ty::Const { val: ConstVal::Integral(v), .. }) => { - explicit_value = v; + 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); + use syntax::attr::IntType; + explicit_value = match repr_type { + IntType::SignedInt(int_type) => ConstInt::new_signed( + b as i128, int_type, tcx.sess.target.isize_ty).unwrap(), + IntType::UnsignedInt(uint_type) => ConstInt::new_unsigned( + b, uint_type, tcx.sess.target.usize_ty).unwrap(), + }; break; } - err => { + _ => { if !expr_did.is_local() { span_bug!(tcx.def_span(expr_did), "variant discriminant evaluation succeeded \ - in its crate but failed locally: {:?}", err); + in its crate but failed locally"); } if explicit_index == 0 { break; diff --git a/src/librustc/ty/relate.rs b/src/librustc/ty/relate.rs index 376cdc462e82f..2d230051cb714 100644 --- a/src/librustc/ty/relate.rs +++ b/src/librustc/ty/relate.rs @@ -19,6 +19,7 @@ use traits::Reveal; use ty::subst::{Kind, Substs}; use ty::{self, Ty, TyCtxt, TypeFoldable}; use ty::error::{ExpectedFound, TypeError}; +use mir::interpret::{GlobalId, Value, PrimVal}; use util::common::ErrorReported; use std::rc::Rc; use std::iter; @@ -441,19 +442,35 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R, assert_eq!(sz_b.ty, tcx.types.usize); let to_u64 = |x: &'tcx ty::Const<'tcx>| -> Result { match x.val { - ConstVal::Integral(x) => Ok(x.to_u64().unwrap()), + ConstVal::Value(Value::ByVal(prim)) => Ok(prim.to_u64().unwrap()), ConstVal::Unevaluated(def_id, substs) => { // FIXME(eddyb) get the right param_env. let param_env = ty::ParamEnv::empty(Reveal::UserFacing); match tcx.lift_to_global(&substs) { Some(substs) => { - match tcx.const_eval(param_env.and((def_id, substs))) { - Ok(&ty::Const { val: ConstVal::Integral(x), .. }) => { - return Ok(x.to_u64().unwrap()); + let instance = ty::Instance::resolve( + tcx.global_tcx(), + param_env, + def_id, + substs, + ); + if let Some(instance) = instance { + let cid = GlobalId { + instance, + promoted: None + }; + match tcx.const_eval(param_env.and(cid)) { + Ok(&ty::Const { + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))), + .. + }) => { + assert_eq!(b as u64 as u128, b); + return Ok(b as u64); + } + _ => {} } - _ => {} } - } + }, None => {} } tcx.sess.delay_span_bug(tcx.def_span(def_id), diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 438511281ba47..fab4beb1c28c8 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -14,11 +14,12 @@ //! `BraceStructLiftImpl!`) to help with the tedium. use infer::type_variable; -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; @@ -57,6 +58,7 @@ CopyImpls! { ::syntax::abi::Abi, ::hir::def_id::DefId, ::mir::Local, + ::mir::Promoted, ::traits::Reveal, ::syntax_pos::Span, } @@ -606,6 +608,116 @@ impl<'a, 'tcx> Lift<'tcx> for ConstEvalErr<'a> { } } +impl<'a, 'tcx> Lift<'tcx> for interpret::EvalError<'a> { + type Lifted = interpret::EvalError<'tcx>; + fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option { + use ::mir::interpret::EvalErrorKind::*; + let kind = match self.kind { + MachineError(ref err) => MachineError(err.clone()), + FunctionPointerTyMismatch(a, b) => FunctionPointerTyMismatch( + tcx.lift(&a)?, + tcx.lift(&b)?, + ), + NoMirFor(ref s) => NoMirFor(s.clone()), + UnterminatedCString(ptr) => UnterminatedCString(ptr), + DanglingPointerDeref => DanglingPointerDeref, + DoubleFree => DoubleFree, + InvalidMemoryAccess => InvalidMemoryAccess, + InvalidFunctionPointer => InvalidFunctionPointer, + InvalidBool => InvalidBool, + InvalidDiscriminant => InvalidDiscriminant, + PointerOutOfBounds { + ptr, + access, + allocation_size, + } => PointerOutOfBounds { ptr, access, allocation_size }, + InvalidNullPointerUsage => InvalidNullPointerUsage, + ReadPointerAsBytes => ReadPointerAsBytes, + ReadBytesAsPointer => ReadBytesAsPointer, + InvalidPointerMath => InvalidPointerMath, + ReadUndefBytes => ReadUndefBytes, + DeadLocal => DeadLocal, + InvalidBoolOp(bop) => InvalidBoolOp(bop), + Unimplemented(ref s) => Unimplemented(s.clone()), + DerefFunctionPointer => DerefFunctionPointer, + ExecuteMemory => ExecuteMemory, + ArrayIndexOutOfBounds(sp, a, b) => ArrayIndexOutOfBounds(sp, a, b), + Math(sp, ref err) => Math(sp, err.clone()), + Intrinsic(ref s) => Intrinsic(s.clone()), + OverflowingMath => OverflowingMath, + InvalidChar(c) => InvalidChar(c), + OutOfMemory { + allocation_size, + memory_size, + memory_usage, + } => OutOfMemory { allocation_size, memory_size, memory_usage }, + ExecutionTimeLimitReached => ExecutionTimeLimitReached, + StackFrameLimitReached => StackFrameLimitReached, + OutOfTls => OutOfTls, + TlsOutOfBounds => TlsOutOfBounds, + AbiViolation(ref s) => AbiViolation(s.clone()), + AlignmentCheckFailed { + required, + has, + } => AlignmentCheckFailed { required, has }, + MemoryLockViolation { + ptr, + len, + frame, + access, + ref lock, + } => MemoryLockViolation { ptr, len, frame, access, lock: lock.clone() }, + MemoryAcquireConflict { + ptr, + len, + kind, + ref lock, + } => MemoryAcquireConflict { ptr, len, kind, lock: lock.clone() }, + InvalidMemoryLockRelease { + ptr, + len, + frame, + ref lock, + } => InvalidMemoryLockRelease { ptr, len, frame, lock: lock.clone() }, + DeallocatedLockedMemory { + ptr, + ref lock, + } => DeallocatedLockedMemory { ptr, lock: lock.clone() }, + ValidationFailure(ref s) => ValidationFailure(s.clone()), + CalledClosureAsFunction => CalledClosureAsFunction, + VtableForArgumentlessMethod => VtableForArgumentlessMethod, + ModifiedConstantMemory => ModifiedConstantMemory, + AssumptionNotHeld => AssumptionNotHeld, + InlineAsm => InlineAsm, + TypeNotPrimitive(ty) => TypeNotPrimitive(tcx.lift(&ty)?), + ReallocatedWrongMemoryKind(ref a, ref b) => { + ReallocatedWrongMemoryKind(a.clone(), b.clone()) + }, + DeallocatedWrongMemoryKind(ref a, ref b) => { + DeallocatedWrongMemoryKind(a.clone(), b.clone()) + }, + ReallocateNonBasePtr => ReallocateNonBasePtr, + DeallocateNonBasePtr => DeallocateNonBasePtr, + IncorrectAllocationInformation(a, b, c, d) => { + IncorrectAllocationInformation(a, b, c, d) + }, + Layout(lay) => Layout(tcx.lift(&lay)?), + HeapAllocZeroBytes => HeapAllocZeroBytes, + HeapAllocNonPowerOfTwoAlignment(n) => HeapAllocNonPowerOfTwoAlignment(n), + Unreachable => Unreachable, + Panic => Panic, + ReadFromReturnPointer => ReadFromReturnPointer, + PathNotFound(ref v) => PathNotFound(v.clone()), + UnimplementedTraitSelection => UnimplementedTraitSelection, + TypeckError => TypeckError, + }; + Some(interpret::EvalError { + kind, + backtrace: self.backtrace.clone(), + }) + } +} + impl<'a, 'tcx> Lift<'tcx> for const_val::ErrKind<'a> { type Lifted = const_val::ErrKind<'tcx>; fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option { @@ -635,6 +747,7 @@ impl<'a, 'tcx> Lift<'tcx> for const_val::ErrKind<'a> { TypeckError => TypeckError, CheckMatchError => CheckMatchError, + Miri(ref e) => return tcx.lift(e).map(Miri), }) } } @@ -653,6 +766,42 @@ impl<'a, 'tcx> Lift<'tcx> for ty::layout::LayoutError<'a> { } } +impl<'a, 'tcx> Lift<'tcx> for ty::InstanceDef<'a> { + type Lifted = ty::InstanceDef<'tcx>; + fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option { + match *self { + ty::InstanceDef::Item(def_id) => + Some(ty::InstanceDef::Item(def_id)), + ty::InstanceDef::Intrinsic(def_id) => + Some(ty::InstanceDef::Intrinsic(def_id)), + ty::InstanceDef::FnPtrShim(def_id, ref ty) => + Some(ty::InstanceDef::FnPtrShim(def_id, tcx.lift(ty)?)), + ty::InstanceDef::Virtual(def_id, n) => + Some(ty::InstanceDef::Virtual(def_id, n)), + ty::InstanceDef::ClosureOnceShim { call_once } => + Some(ty::InstanceDef::ClosureOnceShim { call_once }), + ty::InstanceDef::DropGlue(def_id, ref ty) => + Some(ty::InstanceDef::DropGlue(def_id, tcx.lift(ty)?)), + ty::InstanceDef::CloneShim(def_id, ref ty) => + Some(ty::InstanceDef::CloneShim(def_id, tcx.lift(ty)?)), + } + } +} + +BraceStructLiftImpl! { + impl<'a, 'tcx> Lift<'tcx> for ty::Instance<'a> { + type Lifted = ty::Instance<'tcx>; + def, substs + } +} + +BraceStructLiftImpl! { + impl<'a, 'tcx> Lift<'tcx> for interpret::GlobalId<'a> { + type Lifted = interpret::GlobalId<'tcx>; + instance, promoted + } +} + /////////////////////////////////////////////////////////////////////////// // TypeFoldable implementations. // @@ -778,6 +927,74 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::Slice> { } } +impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> { + fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { + use ty::InstanceDef::*; + Self { + substs: self.substs.fold_with(folder), + def: match self.def { + Item(did) => Item(did.fold_with(folder)), + Intrinsic(did) => Intrinsic(did.fold_with(folder)), + FnPtrShim(did, ty) => FnPtrShim( + did.fold_with(folder), + ty.fold_with(folder), + ), + Virtual(did, i) => Virtual( + did.fold_with(folder), + i, + ), + ClosureOnceShim { call_once } => ClosureOnceShim { + call_once: call_once.fold_with(folder), + }, + DropGlue(did, ty) => DropGlue( + did.fold_with(folder), + ty.fold_with(folder), + ), + CloneShim(did, ty) => CloneShim( + did.fold_with(folder), + ty.fold_with(folder), + ), + }, + } + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + use ty::InstanceDef::*; + self.substs.visit_with(visitor) || + match self.def { + Item(did) => did.visit_with(visitor), + Intrinsic(did) => did.visit_with(visitor), + FnPtrShim(did, ty) => { + did.visit_with(visitor) || + ty.visit_with(visitor) + }, + Virtual(did, _) => did.visit_with(visitor), + ClosureOnceShim { call_once } => call_once.visit_with(visitor), + DropGlue(did, ty) => { + did.visit_with(visitor) || + ty.visit_with(visitor) + }, + CloneShim(did, ty) => { + did.visit_with(visitor) || + ty.visit_with(visitor) + }, + } + } +} + +impl<'tcx> TypeFoldable<'tcx> for interpret::GlobalId<'tcx> { + fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { + Self { + instance: self.instance.fold_with(folder), + promoted: self.promoted + } + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.instance.visit_with(visitor) + } +} + impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { let sty = match self.sty { @@ -1267,53 +1484,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)) } @@ -1322,24 +1493,7 @@ impl<'tcx> TypeFoldable<'tcx> for ConstVal<'tcx> { fn super_visit_with>(&self, visitor: &mut V) -> bool { match *self { - ConstVal::Integral(_) | - ConstVal::Float(_) | - ConstVal::Str(_) | - ConstVal::ByteStr(_) | - ConstVal::Bool(_) | - ConstVal::Char(_) | - ConstVal::Variant(_) => false, - ConstVal::Function(_, substs) => substs.visit_with(visitor), - ConstVal::Aggregate(ConstAggregate::Struct(fields)) => { - fields.iter().any(|&(_, v)| v.visit_with(visitor)) - } - ConstVal::Aggregate(ConstAggregate::Tuple(fields)) | - ConstVal::Aggregate(ConstAggregate::Array(fields)) => { - fields.iter().any(|v| v.visit_with(visitor)) - } - ConstVal::Aggregate(ConstAggregate::Repeat(v, _)) => { - v.visit_with(visitor) - } + ConstVal::Value(_) => false, ConstVal::Unevaluated(_, substs) => substs.visit_with(visitor), } } diff --git a/src/librustc/ty/trait_def.rs b/src/librustc/ty/trait_def.rs index 0fbf9f1bd587b..9c9ce17d8ad0e 100644 --- a/src/librustc/ty/trait_def.rs +++ b/src/librustc/ty/trait_def.rs @@ -186,9 +186,9 @@ pub(super) fn trait_impls_of_provider<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, }) } -impl<'gcx> HashStable> for TraitImpls { +impl<'a> HashStable> for TraitImpls { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let TraitImpls { ref blanket_impls, diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index de96e9dc8ff2d..9ad0f3b0c385c 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -23,6 +23,7 @@ use ty::subst::{Subst, Kind}; use ty::TypeVariants::*; use util::common::ErrorReported; use middle::lang_items; +use mir::interpret::{Value, PrimVal}; use rustc_const_math::{ConstInt, ConstIsize, ConstUsize}; use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult, @@ -682,22 +683,32 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } - /// Check if the node pointed to by def_id is a mutable static item - pub fn is_static_mut(&self, def_id: DefId) -> bool { + /// Return whether the node pointed to by def_id is a static item, and its mutability + pub fn is_static(&self, def_id: DefId) -> Option { if let Some(node) = self.hir.get_if_local(def_id) { match node { Node::NodeItem(&hir::Item { - node: hir::ItemStatic(_, hir::MutMutable, _), .. - }) => true, + node: hir::ItemStatic(_, mutbl, _), .. + }) => Some(mutbl), Node::NodeForeignItem(&hir::ForeignItem { - node: hir::ForeignItemStatic(_, mutbl), .. - }) => mutbl, - _ => false + node: hir::ForeignItemStatic(_, is_mutbl), .. + }) => + Some(if is_mutbl { + hir::Mutability::MutMutable + } else { + hir::Mutability::MutImmutable + }), + _ => None } } else { match self.describe_def(def_id) { - Some(Def::Static(_, mutbl)) => mutbl, - _ => false + Some(Def::Static(_, is_mutbl)) => + Some(if is_mutbl { + hir::Mutability::MutMutable + } else { + hir::Mutability::MutImmutable + }), + _ => None } } } @@ -756,7 +767,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 448ad4cf675c7..8ede5587f93fc 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; @@ -137,30 +137,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 a851ccc34bfd6..6930b376d83ec 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; @@ -221,28 +221,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 51841836698eb..061591eef716a 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -21,12 +21,12 @@ use ty::{TyClosure, TyGenerator, TyForeign, TyProjection, TyAnon}; 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; @@ -1137,7 +1137,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 53b8402ab2ad5..0000000000000 --- a/src/librustc_const_eval/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_const_eval" -version = "0.0.0" - -[lib] -name = "rustc_const_eval" -path = "lib.rs" -crate-type = ["dylib"] - -[dependencies] -arena = { path = "../libarena" } -log = "0.4" -rustc = { path = "../librustc" } -rustc_const_math = { path = "../librustc_const_math" } -rustc_data_structures = { path = "../librustc_data_structures" } -rustc_errors = { path = "../librustc_errors" } -syntax = { path = "../libsyntax" } -syntax_pos = { path = "../libsyntax_pos" } diff --git a/src/librustc_const_eval/diagnostics.rs b/src/librustc_const_eval/diagnostics.rs deleted file mode 100644 index d01b3c45f7fd1..0000000000000 --- a/src/librustc_const_eval/diagnostics.rs +++ /dev/null @@ -1,571 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(non_snake_case)] - -// Error messages for EXXXX errors. -// Each message should start and end with a new line, and be wrapped to 80 characters. -// In vim you can `:set tw=80` and use `gq` to wrap paragraphs. Use `:set tw=0` to disable. -register_long_diagnostics! { - -E0001: r##" -#### Note: this error code is no longer emitted by the compiler. - -This error suggests that the expression arm corresponding to the noted pattern -will never be reached as for all possible values of the expression being -matched, one of the preceding patterns will match. - -This means that perhaps some of the preceding patterns are too general, this -one is too specific or the ordering is incorrect. - -For example, the following `match` block has too many arms: - -``` -match Some(0) { - Some(bar) => {/* ... */} - x => {/* ... */} // This handles the `None` case - _ => {/* ... */} // All possible cases have already been handled -} -``` - -`match` blocks have their patterns matched in order, so, for example, putting -a wildcard arm above a more specific arm will make the latter arm irrelevant. - -Ensure the ordering of the match arm is correct and remove any superfluous -arms. -"##, - -E0002: r##" -#### Note: this error code is no longer emitted by the compiler. - -This error indicates that an empty match expression is invalid because the type -it is matching on is non-empty (there exist values of this type). In safe code -it is impossible to create an instance of an empty type, so empty match -expressions are almost never desired. This error is typically fixed by adding -one or more cases to the match expression. - -An example of an empty type is `enum Empty { }`. So, the following will work: - -``` -enum Empty {} - -fn foo(x: Empty) { - match x { - // empty - } -} -``` - -However, this won't: - -```compile_fail -fn foo(x: Option) { - match x { - // empty - } -} -``` -"##, - -E0003: r##" -#### Note: this error code is no longer emitted by the compiler. - -Not-a-Number (NaN) values cannot be compared for equality and hence can never -match the input to a match expression. So, the following will not compile: - -```compile_fail -const NAN: f32 = 0.0 / 0.0; - -let number = 0.1f32; - -match number { - NAN => { /* ... */ }, - _ => {} -} -``` - -To match against NaN values, you should instead use the `is_nan()` method in a -guard, like so: - -``` -let number = 0.1f32; - -match number { - x if x.is_nan() => { /* ... */ } - _ => {} -} -``` -"##, - -E0004: r##" -This error indicates that the compiler cannot guarantee a matching pattern for -one or more possible inputs to a match expression. Guaranteed matches are -required in order to assign values to match expressions, or alternatively, -determine the flow of execution. Erroneous code example: - -```compile_fail,E0004 -enum Terminator { - HastaLaVistaBaby, - TalkToMyHand, -} - -let x = Terminator::HastaLaVistaBaby; - -match x { // error: non-exhaustive patterns: `HastaLaVistaBaby` not covered - Terminator::TalkToMyHand => {} -} -``` - -If you encounter this error you must alter your patterns so that every possible -value of the input type is matched. For types with a small number of variants -(like enums) you should probably cover all cases explicitly. Alternatively, the -underscore `_` wildcard pattern can be added after all other patterns to match -"anything else". Example: - -``` -enum Terminator { - HastaLaVistaBaby, - TalkToMyHand, -} - -let x = Terminator::HastaLaVistaBaby; - -match x { - Terminator::TalkToMyHand => {} - Terminator::HastaLaVistaBaby => {} -} - -// or: - -match x { - Terminator::TalkToMyHand => {} - _ => {} -} -``` -"##, - -E0005: r##" -Patterns used to bind names must be irrefutable, that is, they must guarantee -that a name will be extracted in all cases. Erroneous code example: - -```compile_fail,E0005 -let x = Some(1); -let Some(y) = x; -// error: refutable pattern in local binding: `None` not covered -``` - -If you encounter this error you probably need to use a `match` or `if let` to -deal with the possibility of failure. Example: - -``` -let x = Some(1); - -match x { - Some(y) => { - // do something - }, - None => {} -} - -// or: - -if let Some(y) = x { - // do something -} -``` -"##, - -E0007: r##" -This error indicates that the bindings in a match arm would require a value to -be moved into more than one location, thus violating unique ownership. Code -like the following is invalid as it requires the entire `Option` to be -moved into a variable called `op_string` while simultaneously requiring the -inner `String` to be moved into a variable called `s`. - -```compile_fail,E0007 -let x = Some("s".to_string()); - -match x { - op_string @ Some(s) => {}, // error: cannot bind by-move with sub-bindings - None => {}, -} -``` - -See also the error E0303. -"##, - -E0008: r##" -Names bound in match arms retain their type in pattern guards. As such, if a -name is bound by move in a pattern, it should also be moved to wherever it is -referenced in the pattern guard code. Doing so however would prevent the name -from being available in the body of the match arm. Consider the following: - -```compile_fail,E0008 -match Some("hi".to_string()) { - Some(s) if s.len() == 0 => {}, // use s. - _ => {}, -} -``` - -The variable `s` has type `String`, and its use in the guard is as a variable of -type `String`. The guard code effectively executes in a separate scope to the -body of the arm, so the value would be moved into this anonymous scope and -therefore becomes unavailable in the body of the arm. - -The problem above can be solved by using the `ref` keyword. - -``` -match Some("hi".to_string()) { - Some(ref s) if s.len() == 0 => {}, - _ => {}, -} -``` - -Though this example seems innocuous and easy to solve, the problem becomes clear -when it encounters functions which consume the value: - -```compile_fail,E0008 -struct A{} - -impl A { - fn consume(self) -> usize { - 0 - } -} - -fn main() { - let a = Some(A{}); - match a { - Some(y) if y.consume() > 0 => {} - _ => {} - } -} -``` - -In this situation, even the `ref` keyword cannot solve it, since borrowed -content cannot be moved. This problem cannot be solved generally. If the value -can be cloned, here is a not-so-specific solution: - -``` -#[derive(Clone)] -struct A{} - -impl A { - fn consume(self) -> usize { - 0 - } -} - -fn main() { - let a = Some(A{}); - match a{ - Some(ref y) if y.clone().consume() > 0 => {} - _ => {} - } -} -``` - -If the value will be consumed in the pattern guard, using its clone will not -move its ownership, so the code works. -"##, - -E0009: r##" -In a pattern, all values that don't implement the `Copy` trait have to be bound -the same way. The goal here is to avoid binding simultaneously by-move and -by-ref. - -This limitation may be removed in a future version of Rust. - -Erroneous code example: - -```compile_fail,E0009 -struct X { x: (), } - -let x = Some((X { x: () }, X { x: () })); -match x { - Some((y, ref z)) => {}, // error: cannot bind by-move and by-ref in the - // same pattern - None => panic!() -} -``` - -You have two solutions: - -Solution #1: Bind the pattern's values the same way. - -``` -struct X { x: (), } - -let x = Some((X { x: () }, X { x: () })); -match x { - Some((ref y, ref z)) => {}, - // or Some((y, z)) => {} - None => panic!() -} -``` - -Solution #2: Implement the `Copy` trait for the `X` structure. - -However, please keep in mind that the first solution should be preferred. - -``` -#[derive(Clone, Copy)] -struct X { x: (), } - -let x = Some((X { x: () }, X { x: () })); -match x { - Some((y, ref z)) => {}, - None => panic!() -} -``` -"##, - -E0158: r##" -`const` and `static` mean different things. A `const` is a compile-time -constant, an alias for a literal value. This property means you can match it -directly within a pattern. - -The `static` keyword, on the other hand, guarantees a fixed location in memory. -This does not always mean that the value is constant. For example, a global -mutex can be declared `static` as well. - -If you want to match against a `static`, consider using a guard instead: - -``` -static FORTY_TWO: i32 = 42; - -match Some(42) { - Some(x) if x == FORTY_TWO => {} - _ => {} -} -``` -"##, - -E0162: r##" -An if-let pattern attempts to match the pattern, and enters the body if the -match was successful. If the match is irrefutable (when it cannot fail to -match), use a regular `let`-binding instead. For instance: - -```compile_fail,E0162 -struct Irrefutable(i32); -let irr = Irrefutable(0); - -// This fails to compile because the match is irrefutable. -if let Irrefutable(x) = irr { - // This body will always be executed. - // ... -} -``` - -Try this instead: - -``` -struct Irrefutable(i32); -let irr = Irrefutable(0); - -let Irrefutable(x) = irr; -println!("{}", x); -``` -"##, - -E0165: r##" -A while-let pattern attempts to match the pattern, and enters the body if the -match was successful. If the match is irrefutable (when it cannot fail to -match), use a regular `let`-binding inside a `loop` instead. For instance: - -```compile_fail,E0165 -struct Irrefutable(i32); -let irr = Irrefutable(0); - -// This fails to compile because the match is irrefutable. -while let Irrefutable(x) = irr { - // ... -} -``` - -Try this instead: - -```no_run -struct Irrefutable(i32); -let irr = Irrefutable(0); - -loop { - let Irrefutable(x) = irr; - // ... -} -``` -"##, - -E0170: r##" -Enum variants are qualified by default. For example, given this type: - -``` -enum Method { - GET, - POST, -} -``` - -You would match it using: - -``` -enum Method { - GET, - POST, -} - -let m = Method::GET; - -match m { - Method::GET => {}, - Method::POST => {}, -} -``` - -If you don't qualify the names, the code will bind new variables named "GET" and -"POST" instead. This behavior is likely not what you want, so `rustc` warns when -that happens. - -Qualified names are good practice, and most code works well with them. But if -you prefer them unqualified, you can import the variants into scope: - -``` -use Method::*; -enum Method { GET, POST } -# fn main() {} -``` - -If you want others to be able to import variants from your module directly, use -`pub use`: - -``` -pub use Method::*; -pub enum Method { GET, POST } -# fn main() {} -``` -"##, - - -E0297: r##" -#### Note: this error code is no longer emitted by the compiler. - -Patterns used to bind names must be irrefutable. That is, they must guarantee -that a name will be extracted in all cases. Instead of pattern matching the -loop variable, consider using a `match` or `if let` inside the loop body. For -instance: - -```compile_fail,E0005 -let xs : Vec> = vec![Some(1), None]; - -// This fails because `None` is not covered. -for Some(x) in xs { - // ... -} -``` - -Match inside the loop instead: - -``` -let xs : Vec> = vec![Some(1), None]; - -for item in xs { - match item { - Some(x) => {}, - None => {}, - } -} -``` - -Or use `if let`: - -``` -let xs : Vec> = vec![Some(1), None]; - -for item in xs { - if let Some(x) = item { - // ... - } -} -``` -"##, - -E0301: r##" -Mutable borrows are not allowed in pattern guards, because matching cannot have -side effects. Side effects could alter the matched object or the environment -on which the match depends in such a way, that the match would not be -exhaustive. For instance, the following would not match any arm if mutable -borrows were allowed: - -```compile_fail,E0301 -match Some(()) { - None => { }, - option if option.take().is_none() => { - /* impossible, option is `Some` */ - }, - Some(_) => { } // When the previous match failed, the option became `None`. -} -``` -"##, - -E0302: r##" -Assignments are not allowed in pattern guards, because matching cannot have -side effects. Side effects could alter the matched object or the environment -on which the match depends in such a way, that the match would not be -exhaustive. For instance, the following would not match any arm if assignments -were allowed: - -```compile_fail,E0302 -match Some(()) { - None => { }, - option if { option = None; false } => { }, - Some(_) => { } // When the previous match failed, the option became `None`. -} -``` -"##, - -E0303: r##" -In certain cases it is possible for sub-bindings to violate memory safety. -Updates to the borrow checker in a future version of Rust may remove this -restriction, but for now patterns must be rewritten without sub-bindings. - -Before: - -```compile_fail,E0303 -match Some("hi".to_string()) { - ref op_string_ref @ Some(s) => {}, - None => {}, -} -``` - -After: - -``` -match Some("hi".to_string()) { - Some(ref s) => { - let op_string_ref = &Some(s); - // ... - }, - None => {}, -} -``` - -The `op_string_ref` binding has type `&Option<&String>` in both cases. - -See also https://github.com/rust-lang/rust/issues/14587 -"##, - -} - - -register_diagnostics! { -// E0298, // cannot compare constants -// E0299, // mismatched types between arms -// E0471, // constant evaluation error (in pattern) -} diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs deleted file mode 100644 index 418bd4b5effc6..0000000000000 --- a/src/librustc_const_eval/eval.rs +++ /dev/null @@ -1,684 +0,0 @@ -// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use rustc::middle::const_val::ConstVal::*; -use rustc::middle::const_val::ConstAggregate::*; -use rustc::middle::const_val::ErrKind::*; -use rustc::middle::const_val::{ByteArray, ConstVal, ConstEvalErr, EvalResult, ErrKind}; - -use rustc::hir::map::blocks::FnLikeNode; -use rustc::hir::def::{Def, CtorKind}; -use rustc::hir::def_id::DefId; -use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::layout::LayoutOf; -use rustc::ty::util::IntTypeExt; -use rustc::ty::subst::{Substs, Subst}; -use rustc::util::common::ErrorReported; -use rustc::util::nodemap::NodeMap; - -use syntax::abi::Abi; -use syntax::ast; -use syntax::attr; -use rustc::hir::{self, Expr}; -use syntax_pos::Span; - -use std::cmp::Ordering; - -use rustc_const_math::*; -macro_rules! signal { - ($e:expr, $exn:expr) => { - return Err(ConstEvalErr { span: $e.span, kind: $exn }) - } -} - -macro_rules! math { - ($e:expr, $op:expr) => { - match $op { - Ok(val) => val, - Err(e) => signal!($e, ErrKind::from(e)), - } - } -} - -/// * `DefId` is the id of the constant. -/// * `Substs` is the monomorphized substitutions for the expression. -pub fn lookup_const_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - key: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>) - -> Option<(DefId, &'tcx Substs<'tcx>)> { - ty::Instance::resolve( - tcx, - key.param_env, - key.value.0, - key.value.1, - ).map(|instance| (instance.def_id(), instance.substs)) -} - -pub struct ConstContext<'a, 'tcx: 'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, - tables: &'a ty::TypeckTables<'tcx>, - param_env: ty::ParamEnv<'tcx>, - substs: &'tcx Substs<'tcx>, - fn_args: Option>> -} - -impl<'a, 'tcx> ConstContext<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, - param_env_and_substs: ty::ParamEnvAnd<'tcx, &'tcx Substs<'tcx>>, - tables: &'a ty::TypeckTables<'tcx>) - -> Self { - ConstContext { - tcx, - param_env: param_env_and_substs.param_env, - tables, - substs: param_env_and_substs.value, - fn_args: None - } - } - - /// Evaluate a constant expression in a context where the expression isn't - /// guaranteed to be evaluable. - pub fn eval(&self, e: &'tcx Expr) -> EvalResult<'tcx> { - if self.tables.tainted_by_errors { - signal!(e, TypeckError); - } - eval_const_expr_partial(self, e) - } -} - -type CastResult<'tcx> = Result, ErrKind<'tcx>>; - -fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, - e: &'tcx Expr) -> EvalResult<'tcx> { - trace!("eval_const_expr_partial: {:?}", e); - let tcx = cx.tcx; - let ty = cx.tables.expr_ty(e).subst(tcx, cx.substs); - let mk_const = |val| tcx.mk_const(ty::Const { val, ty }); - - let result = match e.node { - hir::ExprUnary(hir::UnNeg, ref inner) => { - // unary neg literals already got their sign during creation - if let hir::ExprLit(ref lit) = inner.node { - use syntax::ast::*; - use syntax::ast::LitIntType::*; - const I8_OVERFLOW: u128 = i8::min_value() as u8 as u128; - const I16_OVERFLOW: u128 = i16::min_value() as u16 as u128; - const I32_OVERFLOW: u128 = i32::min_value() as u32 as u128; - const I64_OVERFLOW: u128 = i64::min_value() as u64 as u128; - const I128_OVERFLOW: u128 = i128::min_value() as u128; - let negated = match (&lit.node, &ty.sty) { - (&LitKind::Int(I8_OVERFLOW, _), &ty::TyInt(IntTy::I8)) | - (&LitKind::Int(I8_OVERFLOW, Signed(IntTy::I8)), _) => { - Some(I8(i8::min_value())) - }, - (&LitKind::Int(I16_OVERFLOW, _), &ty::TyInt(IntTy::I16)) | - (&LitKind::Int(I16_OVERFLOW, Signed(IntTy::I16)), _) => { - Some(I16(i16::min_value())) - }, - (&LitKind::Int(I32_OVERFLOW, _), &ty::TyInt(IntTy::I32)) | - (&LitKind::Int(I32_OVERFLOW, Signed(IntTy::I32)), _) => { - Some(I32(i32::min_value())) - }, - (&LitKind::Int(I64_OVERFLOW, _), &ty::TyInt(IntTy::I64)) | - (&LitKind::Int(I64_OVERFLOW, Signed(IntTy::I64)), _) => { - Some(I64(i64::min_value())) - }, - (&LitKind::Int(I128_OVERFLOW, _), &ty::TyInt(IntTy::I128)) | - (&LitKind::Int(I128_OVERFLOW, Signed(IntTy::I128)), _) => { - Some(I128(i128::min_value())) - }, - (&LitKind::Int(n, _), &ty::TyInt(IntTy::Isize)) | - (&LitKind::Int(n, Signed(IntTy::Isize)), _) => { - match tcx.sess.target.isize_ty { - IntTy::I16 => if n == I16_OVERFLOW { - Some(Isize(Is16(i16::min_value()))) - } else { - None - }, - IntTy::I32 => if n == I32_OVERFLOW { - Some(Isize(Is32(i32::min_value()))) - } else { - None - }, - IntTy::I64 => if n == I64_OVERFLOW { - Some(Isize(Is64(i64::min_value()))) - } else { - None - }, - _ => span_bug!(e.span, "typeck error") - } - }, - _ => None - }; - if let Some(i) = negated { - return Ok(mk_const(Integral(i))); - } - } - mk_const(match cx.eval(inner)?.val { - Float(f) => Float(-f), - Integral(i) => Integral(math!(e, -i)), - _ => signal!(e, TypeckError) - }) - } - hir::ExprUnary(hir::UnNot, ref inner) => { - mk_const(match cx.eval(inner)?.val { - Integral(i) => Integral(math!(e, !i)), - Bool(b) => Bool(!b), - _ => signal!(e, TypeckError) - }) - } - hir::ExprUnary(hir::UnDeref, _) => signal!(e, UnimplementedConstVal("deref operation")), - hir::ExprBinary(op, ref a, ref b) => { - // technically, if we don't have type hints, but integral eval - // gives us a type through a type-suffix, cast or const def type - // we need to re-eval the other value of the BinOp if it was - // not inferred - mk_const(match (cx.eval(a)?.val, cx.eval(b)?.val) { - (Float(a), Float(b)) => { - use std::cmp::Ordering::*; - match op.node { - hir::BiAdd => Float(math!(e, a + b)), - hir::BiSub => Float(math!(e, a - b)), - hir::BiMul => Float(math!(e, a * b)), - hir::BiDiv => Float(math!(e, a / b)), - hir::BiRem => Float(math!(e, a % b)), - hir::BiEq => Bool(math!(e, a.try_cmp(b)) == Equal), - hir::BiLt => Bool(math!(e, a.try_cmp(b)) == Less), - hir::BiLe => Bool(math!(e, a.try_cmp(b)) != Greater), - hir::BiNe => Bool(math!(e, a.try_cmp(b)) != Equal), - hir::BiGe => Bool(math!(e, a.try_cmp(b)) != Less), - hir::BiGt => Bool(math!(e, a.try_cmp(b)) == Greater), - _ => span_bug!(e.span, "typeck error"), - } - } - (Integral(a), Integral(b)) => { - use std::cmp::Ordering::*; - match op.node { - hir::BiAdd => Integral(math!(e, a + b)), - hir::BiSub => Integral(math!(e, a - b)), - hir::BiMul => Integral(math!(e, a * b)), - hir::BiDiv => Integral(math!(e, a / b)), - hir::BiRem => Integral(math!(e, a % b)), - hir::BiBitAnd => Integral(math!(e, a & b)), - hir::BiBitOr => Integral(math!(e, a | b)), - hir::BiBitXor => Integral(math!(e, a ^ b)), - hir::BiShl => Integral(math!(e, a << b)), - hir::BiShr => Integral(math!(e, a >> b)), - hir::BiEq => Bool(math!(e, a.try_cmp(b)) == Equal), - hir::BiLt => Bool(math!(e, a.try_cmp(b)) == Less), - hir::BiLe => Bool(math!(e, a.try_cmp(b)) != Greater), - hir::BiNe => Bool(math!(e, a.try_cmp(b)) != Equal), - hir::BiGe => Bool(math!(e, a.try_cmp(b)) != Less), - hir::BiGt => Bool(math!(e, a.try_cmp(b)) == Greater), - _ => span_bug!(e.span, "typeck error"), - } - } - (Bool(a), Bool(b)) => { - Bool(match op.node { - hir::BiAnd => a && b, - hir::BiOr => a || b, - hir::BiBitXor => a ^ b, - hir::BiBitAnd => a & b, - hir::BiBitOr => a | b, - hir::BiEq => a == b, - hir::BiNe => a != b, - hir::BiLt => a < b, - hir::BiLe => a <= b, - hir::BiGe => a >= b, - hir::BiGt => a > b, - _ => span_bug!(e.span, "typeck error"), - }) - } - (Char(a), Char(b)) => { - Bool(match op.node { - hir::BiEq => a == b, - hir::BiNe => a != b, - hir::BiLt => a < b, - hir::BiLe => a <= b, - hir::BiGe => a >= b, - hir::BiGt => a > b, - _ => span_bug!(e.span, "typeck error"), - }) - } - - _ => signal!(e, MiscBinaryOp), - }) - } - hir::ExprCast(ref base, _) => { - let base_val = cx.eval(base)?; - let base_ty = cx.tables.expr_ty(base).subst(tcx, cx.substs); - if ty == base_ty { - base_val - } else { - match cast_const(tcx, base_val.val, ty) { - Ok(val) => mk_const(val), - Err(kind) => signal!(e, kind), - } - } - } - hir::ExprPath(ref qpath) => { - let substs = cx.tables.node_substs(e.hir_id).subst(tcx, cx.substs); - match cx.tables.qpath_def(qpath, e.hir_id) { - Def::Const(def_id) | - Def::AssociatedConst(def_id) => { - let substs = tcx.normalize_associated_type_in_env(&substs, cx.param_env); - match tcx.at(e.span).const_eval(cx.param_env.and((def_id, substs))) { - Ok(val) => val, - Err(ConstEvalErr { kind: TypeckError, .. }) => { - signal!(e, TypeckError); - } - Err(err) => { - debug!("bad reference: {:?}, {:?}", err.description(), err.span); - signal!(e, ErroneousReferencedConstant(box err)) - }, - } - }, - Def::VariantCtor(variant_def, CtorKind::Const) => { - mk_const(Variant(variant_def)) - } - Def::VariantCtor(_, CtorKind::Fn) => { - signal!(e, UnimplementedConstVal("enum variants")); - } - Def::StructCtor(_, CtorKind::Const) => { - mk_const(Aggregate(Struct(&[]))) - } - Def::StructCtor(_, CtorKind::Fn) => { - signal!(e, UnimplementedConstVal("tuple struct constructors")) - } - Def::Local(id) => { - debug!("Def::Local({:?}): {:?}", id, cx.fn_args); - if let Some(&val) = cx.fn_args.as_ref().and_then(|args| args.get(&id)) { - val - } else { - signal!(e, NonConstPath); - } - }, - Def::Method(id) | Def::Fn(id) => mk_const(Function(id, substs)), - Def::Err => span_bug!(e.span, "typeck error"), - _ => signal!(e, NonConstPath), - } - } - hir::ExprCall(ref callee, ref args) => { - let (def_id, substs) = match cx.eval(callee)?.val { - Function(def_id, substs) => (def_id, substs), - _ => signal!(e, TypeckError), - }; - - if tcx.fn_sig(def_id).abi() == Abi::RustIntrinsic { - let layout_of = |ty: Ty<'tcx>| { - let ty = tcx.erase_regions(&ty); - (tcx.at(e.span), cx.param_env).layout_of(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())))); - } - _ => signal!(e, TypeckError) - } - } - - let body = if let Some(node_id) = tcx.hir.as_local_node_id(def_id) { - if let Some(fn_like) = FnLikeNode::from_node(tcx.hir.get(node_id)) { - if fn_like.constness() == hir::Constness::Const { - tcx.hir.body(fn_like.body()) - } else { - signal!(e, TypeckError) - } - } else { - signal!(e, TypeckError) - } - } else { - if tcx.is_const_fn(def_id) { - tcx.extern_const_body(def_id).body - } else { - signal!(e, TypeckError) - } - }; - - let arg_ids = body.arguments.iter().map(|arg| match arg.pat.node { - hir::PatKind::Binding(_, canonical_id, _, _) => Some(canonical_id), - _ => None - }).collect::>(); - assert_eq!(arg_ids.len(), args.len()); - - let mut call_args = NodeMap(); - for (arg, arg_expr) in arg_ids.into_iter().zip(args.iter()) { - let arg_val = cx.eval(arg_expr)?; - debug!("const call arg: {:?}", arg); - if let Some(id) = arg { - assert!(call_args.insert(id, arg_val).is_none()); - } - } - debug!("const call({:?})", call_args); - let callee_cx = ConstContext { - tcx, - param_env: cx.param_env, - tables: tcx.typeck_tables_of(def_id), - substs, - fn_args: Some(call_args) - }; - callee_cx.eval(&body.value)? - }, - hir::ExprLit(ref lit) => match lit_to_const(&lit.node, tcx, ty) { - Ok(val) => mk_const(val), - Err(err) => signal!(e, err), - }, - hir::ExprBlock(ref block) => { - match block.expr { - Some(ref expr) => cx.eval(expr)?, - None => mk_const(Aggregate(Tuple(&[]))), - } - } - hir::ExprType(ref e, _) => cx.eval(e)?, - hir::ExprTup(ref fields) => { - let values = fields.iter().map(|e| cx.eval(e)).collect::, _>>()?; - mk_const(Aggregate(Tuple(tcx.alloc_const_slice(&values)))) - } - hir::ExprStruct(_, ref fields, _) => { - mk_const(Aggregate(Struct(tcx.alloc_name_const_slice(&fields.iter().map(|f| { - cx.eval(&f.expr).map(|v| (f.name.node, v)) - }).collect::, _>>()?)))) - } - hir::ExprIndex(ref arr, ref idx) => { - if !tcx.sess.features.borrow().const_indexing { - signal!(e, IndexOpFeatureGated); - } - let arr = cx.eval(arr)?; - let idx = match cx.eval(idx)?.val { - Integral(Usize(i)) => i.as_u64(), - _ => signal!(idx, IndexNotUsize), - }; - assert_eq!(idx as usize as u64, idx); - match arr.val { - Aggregate(Array(v)) => { - if let Some(&elem) = v.get(idx as usize) { - elem - } else { - let n = v.len() as u64; - signal!(e, IndexOutOfBounds { len: n, index: idx }) - } - } - - Aggregate(Repeat(.., n)) if idx >= n => { - signal!(e, IndexOutOfBounds { len: n, index: idx }) - } - Aggregate(Repeat(elem, _)) => elem, - - ByteStr(b) if idx >= b.data.len() as u64 => { - signal!(e, IndexOutOfBounds { len: b.data.len() as u64, index: idx }) - } - ByteStr(b) => { - mk_const(Integral(U8(b.data[idx as usize]))) - }, - - _ => signal!(e, IndexedNonVec), - } - } - hir::ExprArray(ref v) => { - let values = v.iter().map(|e| cx.eval(e)).collect::, _>>()?; - mk_const(Aggregate(Array(tcx.alloc_const_slice(&values)))) - } - hir::ExprRepeat(ref elem, _) => { - let n = match ty.sty { - ty::TyArray(_, n) => n.val.to_const_int().unwrap().to_u64().unwrap(), - _ => span_bug!(e.span, "typeck error") - }; - mk_const(Aggregate(Repeat(cx.eval(elem)?, n))) - }, - hir::ExprTupField(ref base, index) => { - if let Aggregate(Tuple(fields)) = cx.eval(base)?.val { - fields[index.node] - } else { - signal!(base, ExpectedConstTuple); - } - } - hir::ExprField(ref base, field_name) => { - if let Aggregate(Struct(fields)) = cx.eval(base)?.val { - if let Some(&(_, f)) = fields.iter().find(|&&(name, _)| name == field_name.node) { - f - } else { - signal!(e, MissingStructField); - } - } else { - signal!(base, ExpectedConstStruct); - } - } - hir::ExprAddrOf(..) => signal!(e, UnimplementedConstVal("address operator")), - _ => signal!(e, MiscCatchAll) - }; - - Ok(result) -} - -fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - val: ConstInt, - ty: Ty<'tcx>) - -> CastResult<'tcx> { - let v = val.to_u128_unchecked(); - match ty.sty { - ty::TyBool if v == 0 => Ok(Bool(false)), - ty::TyBool if v == 1 => Ok(Bool(true)), - ty::TyInt(ast::IntTy::I8) => Ok(Integral(I8(v as i128 as i8))), - ty::TyInt(ast::IntTy::I16) => Ok(Integral(I16(v as i128 as i16))), - ty::TyInt(ast::IntTy::I32) => Ok(Integral(I32(v as i128 as i32))), - ty::TyInt(ast::IntTy::I64) => Ok(Integral(I64(v as i128 as i64))), - ty::TyInt(ast::IntTy::I128) => Ok(Integral(I128(v as i128))), - ty::TyInt(ast::IntTy::Isize) => { - Ok(Integral(Isize(ConstIsize::new_truncating(v as i128, tcx.sess.target.isize_ty)))) - }, - ty::TyUint(ast::UintTy::U8) => Ok(Integral(U8(v as u8))), - ty::TyUint(ast::UintTy::U16) => Ok(Integral(U16(v as u16))), - ty::TyUint(ast::UintTy::U32) => Ok(Integral(U32(v as u32))), - ty::TyUint(ast::UintTy::U64) => Ok(Integral(U64(v as u64))), - ty::TyUint(ast::UintTy::U128) => Ok(Integral(U128(v as u128))), - ty::TyUint(ast::UintTy::Usize) => { - Ok(Integral(Usize(ConstUsize::new_truncating(v, tcx.sess.target.usize_ty)))) - }, - ty::TyFloat(fty) => { - if let Some(i) = val.to_u128() { - Ok(Float(ConstFloat::from_u128(i, fty))) - } else { - // The value must be negative, go through signed integers. - let i = val.to_u128_unchecked() as i128; - Ok(Float(ConstFloat::from_i128(i, fty))) - } - } - ty::TyRawPtr(_) => Err(ErrKind::UnimplementedConstVal("casting an address to a raw ptr")), - ty::TyChar => match val { - U8(u) => Ok(Char(u as char)), - _ => bug!(), - }, - _ => Err(CannotCast), - } -} - -fn cast_const_float<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - val: ConstFloat, - ty: Ty<'tcx>) -> CastResult<'tcx> { - let int_width = |ty| { - ty::layout::Integer::from_attr(tcx, ty).size().bits() as usize - }; - match ty.sty { - ty::TyInt(ity) => { - if let Some(i) = val.to_i128(int_width(attr::SignedInt(ity))) { - cast_const_int(tcx, I128(i), ty) - } else { - Err(CannotCast) - } - } - ty::TyUint(uty) => { - if let Some(i) = val.to_u128(int_width(attr::UnsignedInt(uty))) { - cast_const_int(tcx, U128(i), ty) - } else { - Err(CannotCast) - } - } - ty::TyFloat(fty) => Ok(Float(val.convert(fty))), - _ => Err(CannotCast), - } -} - -fn cast_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - val: ConstVal<'tcx>, - ty: Ty<'tcx>) - -> CastResult<'tcx> { - match val { - Integral(i) => cast_const_int(tcx, i, ty), - Bool(b) => cast_const_int(tcx, U8(b as u8), ty), - Float(f) => cast_const_float(tcx, f, ty), - Char(c) => cast_const_int(tcx, U32(c as u32), ty), - Variant(v) => { - let adt = tcx.adt_def(tcx.parent_def_id(v).unwrap()); - let idx = adt.variant_index_with_id(v); - cast_const_int(tcx, adt.discriminant_for_variant(tcx, idx), ty) - } - Function(..) => Err(UnimplementedConstVal("casting fn pointers")), - ByteStr(b) => match ty.sty { - ty::TyRawPtr(_) => { - Err(ErrKind::UnimplementedConstVal("casting a bytestr to a raw ptr")) - }, - ty::TyRef(_, ty::TypeAndMut { ref ty, mutbl: hir::MutImmutable }) => match ty.sty { - ty::TyArray(ty, n) => { - let n = n.val.to_const_int().unwrap().to_u64().unwrap(); - if ty == tcx.types.u8 && n == b.data.len() as u64 { - Ok(val) - } else { - Err(CannotCast) - } - } - ty::TySlice(_) => { - Err(ErrKind::UnimplementedConstVal("casting a bytestr to slice")) - }, - _ => Err(CannotCast), - }, - _ => Err(CannotCast), - }, - Str(s) => match ty.sty { - ty::TyRawPtr(_) => Err(ErrKind::UnimplementedConstVal("casting a str to a raw ptr")), - ty::TyRef(_, ty::TypeAndMut { ref ty, mutbl: hir::MutImmutable }) => match ty.sty { - ty::TyStr => Ok(Str(s)), - _ => Err(CannotCast), - }, - _ => Err(CannotCast), - }, - _ => Err(CannotCast), - } -} - -fn lit_to_const<'a, 'tcx>(lit: &'tcx ast::LitKind, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - mut ty: Ty<'tcx>) - -> Result, ErrKind<'tcx>> { - use syntax::ast::*; - use syntax::ast::LitIntType::*; - - if let ty::TyAdt(adt, _) = ty.sty { - if adt.is_enum() { - ty = adt.repr.discr_type().to_ty(tcx) - } - } - - match *lit { - LitKind::Str(ref s, _) => Ok(Str(s.as_str())), - LitKind::ByteStr(ref data) => Ok(ByteStr(ByteArray { data })), - LitKind::Byte(n) => Ok(Integral(U8(n))), - LitKind::Int(n, hint) => { - match (&ty.sty, hint) { - (&ty::TyInt(ity), _) | - (_, Signed(ity)) => { - Ok(Integral(ConstInt::new_signed_truncating(n as i128, - ity, tcx.sess.target.isize_ty))) - } - (&ty::TyUint(uty), _) | - (_, Unsigned(uty)) => { - Ok(Integral(ConstInt::new_unsigned_truncating(n as u128, - uty, tcx.sess.target.usize_ty))) - } - _ => bug!() - } - } - LitKind::Float(n, fty) => { - parse_float(&n.as_str(), fty).map(Float) - } - LitKind::FloatUnsuffixed(n) => { - let fty = match ty.sty { - ty::TyFloat(fty) => fty, - _ => bug!() - }; - parse_float(&n.as_str(), fty).map(Float) - } - LitKind::Bool(b) => Ok(Bool(b)), - LitKind::Char(c) => Ok(Char(c)), - } -} - -fn parse_float<'tcx>(num: &str, fty: ast::FloatTy) - -> Result> { - ConstFloat::from_str(num, fty).map_err(|_| { - // FIXME(#31407) this is only necessary because float parsing is buggy - UnimplementedConstVal("could not evaluate float literal (see issue #31407)") - }) -} - -pub fn compare_const_vals(tcx: TyCtxt, span: Span, a: &ConstVal, b: &ConstVal) - -> Result -{ - let result = match (a, b) { - (&Integral(a), &Integral(b)) => a.try_cmp(b).ok(), - (&Float(a), &Float(b)) => a.try_cmp(b).ok(), - (&Str(ref a), &Str(ref b)) => Some(a.cmp(b)), - (&Bool(a), &Bool(b)) => Some(a.cmp(&b)), - (&ByteStr(a), &ByteStr(b)) => Some(a.data.cmp(b.data)), - (&Char(a), &Char(b)) => Some(a.cmp(&b)), - _ => None, - }; - - match result { - Some(result) => Ok(result), - None => { - // FIXME: can this ever be reached? - tcx.sess.delay_span_bug(span, - &format!("type mismatch comparing {:?} and {:?}", a, b)); - Err(ErrorReported) - } - } -} - -impl<'a, 'tcx> ConstContext<'a, 'tcx> { - pub fn compare_lit_exprs(&self, - span: Span, - a: &'tcx Expr, - b: &'tcx Expr) -> Result { - let tcx = self.tcx; - let a = match self.eval(a) { - Ok(a) => a, - Err(e) => { - e.report(tcx, a.span, "expression"); - return Err(ErrorReported); - } - }; - let b = match self.eval(b) { - Ok(b) => b, - Err(e) => { - e.report(tcx, b.span, "expression"); - return Err(ErrorReported); - } - }; - compare_const_vals(tcx, span, &a.val, &b.val) - } -} diff --git a/src/librustc_const_eval/lib.rs b/src/librustc_const_eval/lib.rs deleted file mode 100644 index b4563f6cf2e75..0000000000000 --- a/src/librustc_const_eval/lib.rs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! constant evaluation on the HIR and code to validate patterns/matches -//! -//! # Note -//! -//! This API is completely unstable and subject to change. - -#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", - html_favicon_url = "https://doc.rust-lang.org/favicon.ico", - html_root_url = "https://doc.rust-lang.org/nightly/")] -#![deny(warnings)] - -#![feature(rustc_diagnostic_macros)] -#![feature(slice_patterns)] -#![feature(box_patterns)] -#![feature(box_syntax)] -#![feature(i128_type)] -#![feature(from_ref)] - -extern crate arena; -#[macro_use] extern crate syntax; -#[macro_use] extern crate log; -#[macro_use] extern crate rustc; -extern crate rustc_const_math; -extern crate rustc_data_structures; -extern crate rustc_errors; -extern crate syntax_pos; - -// NB: This module needs to be declared first so diagnostics are -// registered before they are used. -mod diagnostics; - -mod eval; -mod _match; -pub mod check_match; -pub mod pattern; - -pub use eval::*; - -use rustc::ty::maps::Providers; - -pub fn provide(providers: &mut Providers) { - *providers = Providers { - check_match: check_match::check_match, - ..*providers - }; -} - -// Build the diagnostics array at the end so that the metadata includes error use sites. -#[cfg(not(stage0))] // remove after the next snapshot -__build_diagnostic_array! { librustc_const_eval, DIAGNOSTICS } diff --git a/src/librustc_data_structures/stable_hasher.rs b/src/librustc_data_structures/stable_hasher.rs index d82b712b5b14b..70733bc6aeda0 100644 --- a/src/librustc_data_structures/stable_hasher.rs +++ b/src/librustc_data_structures/stable_hasher.rs @@ -259,6 +259,14 @@ impl HashStable for f64 { } } +impl HashStable for ::std::cmp::Ordering { + fn hash_stable(&self, + ctx: &mut CTX, + hasher: &mut StableHasher) { + (*self as i8).hash_stable(ctx, hasher); + } +} + impl, CTX> HashStable for (T1,) { fn hash_stable(&self, ctx: &mut CTX, diff --git a/src/librustc_driver/Cargo.toml b/src/librustc_driver/Cargo.toml index 4f7bbd7927575..09a9d782722b3 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 f7e35ba0081e4..3c943aa9255bb 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -38,7 +38,7 @@ use rustc_privacy; use rustc_plugin::registry::Registry; use rustc_plugin as plugin; use rustc_passes::{self, ast_validation, loops, consts, static_recursion, hir_stats}; -use rustc_const_eval::{self, check_match}; +use rustc_mir::const_eval::check_match; use super::Compilation; use ::DefaultTransCrate; @@ -938,7 +938,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); @@ -1093,6 +1092,13 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(control: &CompileController, stability::check_unused_or_stable_features(tcx) }); + + time(time_passes, + "MIR linting", + || for def_id in tcx.body_owners() { + mir::const_eval::check::check(tcx, def_id) + }); + time(time_passes, "lint checking", || lint::check_crate(tcx)); return Ok(f(tcx, analysis, rx, tcx.sess.compile_status())); diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 237656eb43c69..6d8f3e86a94cf 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; @@ -1281,7 +1280,6 @@ pub fn diagnostics_registry() -> errors::registry::Registry { all_errors.extend_from_slice(&rustc_privacy::DIAGNOSTICS); #[cfg(feature="llvm")] all_errors.extend_from_slice(&rustc_trans::DIAGNOSTICS); - all_errors.extend_from_slice(&rustc_const_eval::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_metadata::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_passes::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_plugin::DIAGNOSTICS); diff --git a/src/librustc_lint/Cargo.toml b/src/librustc_lint/Cargo.toml index 9fee2d54e4752..5ff891202dbc5 100644 --- a/src/librustc_lint/Cargo.toml +++ b/src/librustc_lint/Cargo.toml @@ -12,6 +12,6 @@ test = false [dependencies] log = "0.4" rustc = { path = "../librustc" } -rustc_const_eval = { path = "../librustc_const_eval" } +rustc_mir = { path = "../librustc_mir"} syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index de55710bdf3d0..b82014e873f27 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -682,77 +682,6 @@ impl EarlyLintPass for DeprecatedAttr { } } -declare_lint! { - pub ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, - Warn, - "floating-point literals cannot be used in patterns" -} - -/// Checks for floating point literals in patterns. -#[derive(Clone)] -pub struct IllegalFloatLiteralPattern; - -impl LintPass for IllegalFloatLiteralPattern { - fn get_lints(&self) -> LintArray { - lint_array!(ILLEGAL_FLOATING_POINT_LITERAL_PATTERN) - } -} - -fn fl_lit_check_expr(cx: &EarlyContext, expr: &ast::Expr) { - use self::ast::{ExprKind, LitKind}; - match expr.node { - ExprKind::Lit(ref l) => { - match l.node { - LitKind::FloatUnsuffixed(..) | - LitKind::Float(..) => { - cx.span_lint(ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, - l.span, - "floating-point literals cannot be used in patterns"); - }, - _ => (), - } - } - // These may occur in patterns - // and can maybe contain float literals - ExprKind::Unary(_, ref f) => fl_lit_check_expr(cx, f), - // Other kinds of exprs can't occur in patterns so we don't have to check them - // (ast_validation will emit an error if they occur) - _ => (), - } -} - -impl EarlyLintPass for IllegalFloatLiteralPattern { - fn check_pat(&mut self, cx: &EarlyContext, pat: &ast::Pat) { - use self::ast::PatKind; - pat.walk(&mut |p| { - match p.node { - // Wildcard patterns and paths are uninteresting for the lint - PatKind::Wild | - PatKind::Path(..) => (), - - // The walk logic recurses inside these - PatKind::Ident(..) | - PatKind::Struct(..) | - PatKind::Tuple(..) | - PatKind::TupleStruct(..) | - PatKind::Ref(..) | - PatKind::Box(..) | - PatKind::Slice(..) => (), - - // Extract the expressions and check them - PatKind::Lit(ref e) => fl_lit_check_expr(cx, e), - PatKind::Range(ref st, ref en, _) => { - fl_lit_check_expr(cx, st); - fl_lit_check_expr(cx, en); - }, - - PatKind::Mac(_) => bug!("lint must run post-expansion"), - } - true - }); - } -} - declare_lint! { pub UNUSED_DOC_COMMENT, Warn, diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 699765dde03ff..4ccbc5d14704e 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 bf5d16696f9cd..13d765de21905 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -14,8 +14,6 @@ use rustc::hir::def_id::DefId; 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}; @@ -43,12 +41,6 @@ declare_lint! { "literal out of range for its type" } -declare_lint! { - EXCEEDING_BITSHIFTS, - Deny, - "shift exceeds the type's number of bits" -} - declare_lint! { VARIANT_SIZE_DIFFERENCES, Allow, @@ -70,8 +62,7 @@ impl TypeLimits { impl LintPass for TypeLimits { fn get_lints(&self) -> LintArray { lint_array!(UNUSED_COMPARISONS, - OVERFLOWING_LITERALS, - EXCEEDING_BITSHIFTS) + OVERFLOWING_LITERALS) } } @@ -90,49 +81,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 { @@ -250,28 +198,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { } } - fn int_ty_bits(int_ty: ast::IntTy, isize_ty: ast::IntTy) -> u64 { - match int_ty { - ast::IntTy::Isize => int_ty_bits(isize_ty, isize_ty), - ast::IntTy::I8 => 8, - ast::IntTy::I16 => 16 as u64, - ast::IntTy::I32 => 32, - ast::IntTy::I64 => 64, - ast::IntTy::I128 => 128, - } - } - - fn uint_ty_bits(uint_ty: ast::UintTy, usize_ty: ast::UintTy) -> u64 { - match uint_ty { - ast::UintTy::Usize => uint_ty_bits(usize_ty, usize_ty), - ast::UintTy::U8 => 8, - ast::UintTy::U16 => 16, - ast::UintTy::U32 => 32, - ast::UintTy::U64 => 64, - ast::UintTy::U128 => 128, - } - } - fn check_limits(cx: &LateContext, binop: hir::BinOp, l: &hir::Expr, diff --git a/src/librustc_llvm/ffi.rs b/src/librustc_llvm/ffi.rs index b97e37f4c8fb9..e5c796510fe44 100644 --- a/src/librustc_llvm/ffi.rs +++ b/src/librustc_llvm/ffi.rs @@ -659,6 +659,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 06728b2e6257c..f9045a0ffb5eb 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -19,15 +19,16 @@ use rustc::middle::cstore::{LinkagePreference, ExternConstBody, ExternBodyNestedBodies}; 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::util::nodemap::DefIdSet; use rustc::mir::Mir; +use rustc::util::nodemap::FxHashMap; use std::cell::Ref; use std::collections::BTreeMap; @@ -54,6 +55,9 @@ pub struct DecodeContext<'a, 'tcx: 'a> { last_filemap_index: usize, lazy_state: LazyState, + + // interpreter allocation cache + interpret_alloc_cache: FxHashMap, } /// Abstract over the various ways one can create metadata decoders. @@ -72,6 +76,7 @@ pub trait Metadata<'a, 'tcx>: Copy { tcx, last_filemap_index: 0, lazy_state: LazyState::NoNode, + interpret_alloc_cache: FxHashMap::default(), } } } @@ -268,6 +273,59 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { } } +impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { + #[inline] + fn specialized_decode(&mut self) -> Result { + self.specialized_decode().map(|i| LocalDefId::from_def_id(i)) + } +} + +impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { + fn specialized_decode(&mut self) -> Result { + const MAX1: usize = usize::max_value() - 1; + let tcx = self.tcx; + let interpret_interner = || tcx.unwrap().interpret_interner.borrow_mut(); + let pos = self.position(); + match usize::decode(self)? { + ::std::usize::MAX => { + let alloc_id = interpret_interner().reserve(); + trace!("creating alloc id {:?} at {}", alloc_id, pos); + // insert early to allow recursive allocs + self.interpret_alloc_cache.insert(pos, alloc_id); + + let allocation = interpret::Allocation::decode(self)?; + trace!("decoded alloc {:?} {:#?}", alloc_id, allocation); + let allocation = self.tcx.unwrap().intern_const_alloc(allocation); + interpret_interner().intern_at_reserved(alloc_id, allocation); + + if let Some(glob) = Option::::decode(self)? { + interpret_interner().cache(glob, alloc_id); + } + + Ok(alloc_id) + }, + MAX1 => { + trace!("creating fn alloc id at {}", pos); + let instance = ty::Instance::decode(self)?; + trace!("decoded fn alloc instance: {:?}", instance); + let id = interpret_interner().create_fn_alloc(instance); + trace!("created fn alloc id: {:?}", id); + self.interpret_alloc_cache.insert(pos, id); + Ok(id) + }, + shorthand => { + trace!("loading shorthand {}", shorthand); + if let Some(&alloc_id) = self.interpret_alloc_cache.get(&shorthand) { + return Ok(alloc_id); + } + trace!("shorthand {} not cached, loading entire allocation", shorthand); + // need to load allocation + self.with_position(shorthand, |this| interpret::AllocId::decode(this)) + }, + } + } +} + impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { fn specialized_decode(&mut self) -> Result { let tag = u8::decode(self)?; diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 78578ca179c82..953e2e41b417f 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -16,12 +16,12 @@ 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::lang_items; -use rustc::mir; +use rustc::mir::{self, interpret}; use rustc::traits::specialization_graph; use rustc::ty::{self, Ty, TyCtxt, ReprOptions}; use rustc::ty::codec::{self as ty_codec, TyEncoder}; @@ -58,6 +58,7 @@ pub struct EncodeContext<'a, 'tcx: 'a> { lazy_state: LazyState, type_shorthands: FxHashMap, usize>, predicate_shorthands: FxHashMap, usize>, + interpret_alloc_shorthands: FxHashMap, // This is used to speed up Span encoding. filemap_cache: Rc, @@ -179,12 +180,49 @@ impl<'a, 'tcx> SpecializedEncoder for EncodeContext<'a, 'tcx> { } } +impl<'a, 'tcx> SpecializedEncoder for EncodeContext<'a, 'tcx> { + #[inline] + fn specialized_encode(&mut self, def_id: &LocalDefId) -> Result<(), Self::Error> { + self.specialized_encode(&def_id.to_def_id()) + } +} + impl<'a, 'tcx> SpecializedEncoder> for EncodeContext<'a, 'tcx> { fn specialized_encode(&mut self, ty: &Ty<'tcx>) -> Result<(), Self::Error> { ty_codec::encode_with_shorthand(self, ty, |ecx| &mut ecx.type_shorthands) } } +impl<'a, 'tcx> SpecializedEncoder for EncodeContext<'a, 'tcx> { + fn specialized_encode(&mut self, alloc_id: &interpret::AllocId) -> Result<(), Self::Error> { + trace!("encoding {:?} at {}", alloc_id, self.position()); + if let Some(shorthand) = self.interpret_alloc_shorthands.get(alloc_id).cloned() { + trace!("encoding {:?} as shorthand to {}", alloc_id, shorthand); + return shorthand.encode(self); + } + let start = self.position(); + // cache the allocation shorthand now, because the allocation itself might recursively + // point to itself. + self.interpret_alloc_shorthands.insert(*alloc_id, start); + let interpret_interner = self.tcx.interpret_interner.borrow(); + if let Some(alloc) = interpret_interner.get_alloc(*alloc_id) { + trace!("encoding {:?} with {:#?}", alloc_id, alloc); + usize::max_value().encode(self)?; + alloc.encode(self)?; + interpret_interner + .get_corresponding_static_def_id(*alloc_id) + .encode(self)?; + } else if let Some(fn_instance) = interpret_interner.get_fn(*alloc_id) { + trace!("encoding {:?} with {:#?}", alloc_id, fn_instance); + (usize::max_value() - 1).encode(self)?; + fn_instance.encode(self)?; + } else { + bug!("alloc id without corresponding allocation: {}", alloc_id); + } + Ok(()) + } +} + impl<'a, 'tcx> SpecializedEncoder> for EncodeContext<'a, 'tcx> { fn specialized_encode(&mut self, predicates: &ty::GenericPredicates<'tcx>) @@ -1683,6 +1721,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 c542f65dcecf3..6b020903d0055 100644 --- a/src/librustc_metadata/schema.rs +++ b/src/librustc_metadata/schema.rs @@ -225,9 +225,9 @@ pub struct TraitImpls { pub impls: LazySeq, } -impl<'gcx> HashStable> for TraitImpls { +impl<'a, 'gcx> HashStable> for TraitImpls { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let TraitImpls { trait_id: (krate, def_index), @@ -308,9 +308,9 @@ pub enum EntryKind<'tcx> { AssociatedConst(AssociatedContainer, u8), } -impl<'gcx> HashStable> for EntryKind<'gcx> { +impl<'a, 'gcx> HashStable> for EntryKind<'gcx> { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { diff --git a/src/librustc_mir/Cargo.toml b/src/librustc_mir/Cargo.toml index 195335dd64c13..4a8a5ee51a4b4 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 b5836c65d675b..f5610c8054a68 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -1487,11 +1487,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 => { @@ -1644,7 +1645,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 901b73c610e3b..15b3f41db6c49 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -26,6 +26,7 @@ use rustc::ty::fold::TypeFoldable; use rustc::ty::{self, ToPolyTraitRef, Ty, TyCtxt, TypeVariants}; use rustc::middle::const_val::ConstVal; use rustc::mir::*; +use rustc::mir::interpret::{Value, PrimVal}; use rustc::mir::tcx::PlaceTy; use rustc::mir::visit::{PlaceContext, Visitor}; use std::fmt; @@ -258,7 +259,23 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { // constraints on `'a` and `'b`. These constraints // would be lost if we just look at the normalized // value. - if let ConstVal::Function(def_id, ..) = value.val { + let did = match value.val { + ConstVal::Value(Value::ByVal(PrimVal::Ptr(p))) => { + self.tcx() + .interpret_interner + .borrow() + .get_fn(p.alloc_id) + .map(|instance| instance.def_id()) + }, + ConstVal::Value(Value::ByVal(PrimVal::Undef)) => { + match value.ty.sty { + ty::TyFnDef(ty_def_id, _) => Some(ty_def_id), + _ => None, + } + }, + _ => None, + }; + if let Some(def_id) = did { let tcx = self.tcx(); let type_checker = &mut self.cx; @@ -429,7 +446,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) @@ -1007,13 +1024,29 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { Literal::Value { value: &ty::Const { - val: ConstVal::Function(def_id, _), - .. + val, + ty, }, .. }, .. - }) => Some(def_id) == self.tcx().lang_items().box_free_fn(), + }) => match val { + ConstVal::Value(Value::ByVal(PrimVal::Ptr(p))) => { + let inst = self.tcx().interpret_interner.borrow().get_fn(p.alloc_id); + inst.map_or(false, |inst| { + Some(inst.def_id()) == self.tcx().lang_items().box_free_fn() + }) + }, + ConstVal::Value(Value::ByVal(PrimVal::Undef)) => { + match ty.sty { + ty::TyFnDef(ty_def_id, _) => { + Some(ty_def_id) == self.tcx().lang_items().box_free_fn() + } + _ => false, + } + } + _ => false, + } _ => false, } } diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index d3cc952759058..ca6e7c2c41580 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -24,6 +24,7 @@ use rustc::middle::const_val::ConstVal; use rustc::middle::region; use rustc::ty::{self, Ty}; use rustc::mir::*; +use rustc::mir::interpret::{Value, PrimVal}; use syntax::ast; use syntax_pos::Span; @@ -203,7 +204,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 }), }, @@ -401,7 +402,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { Literal::Value { value: self.hir.tcx().mk_const(ty::Const { - val: ConstVal::Integral(val), + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(val.to_u128_unchecked()))), ty }) } @@ -439,7 +440,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { Literal::Value { value: self.hir.tcx().mk_const(ty::Const { - val: ConstVal::Integral(val), + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes( + val.to_u128_unchecked() + ))), ty }) } diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 6cb9217776648..5459a40ea156d 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -338,7 +338,7 @@ enum TestKind<'tcx> { // test the branches of enum SwitchInt { switch_ty: Ty<'tcx>, - options: Vec<&'tcx ty::Const<'tcx>>, + options: Vec, indices: FxHashMap<&'tcx ty::Const<'tcx>, usize>, }, diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index b2357b771572f..e11a7968ff378 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -20,11 +20,10 @@ use build::matches::{Candidate, MatchPair, Test, TestKind}; use hair::*; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::bitvec::BitVector; -use rustc::middle::const_val::ConstVal; use rustc::ty::{self, Ty}; use rustc::ty::util::IntTypeExt; use rustc::mir::*; -use rustc::hir::RangeEnd; +use rustc::hir::{RangeEnd, Mutability}; use syntax_pos::Span; use std::cmp::Ordering; @@ -112,7 +111,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { test_place: &Place<'tcx>, candidate: &Candidate<'pat, 'tcx>, switch_ty: Ty<'tcx>, - options: &mut Vec<&'tcx ty::Const<'tcx>>, + options: &mut Vec, indices: &mut FxHashMap<&'tcx ty::Const<'tcx>, usize>) -> bool { @@ -128,7 +127,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { indices.entry(value) .or_insert_with(|| { - options.push(value); + options.push(value.val.to_u128().expect("switching on int")); options.len() - 1 }); true @@ -193,7 +192,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let tcx = self.hir.tcx(); for (idx, discr) in adt_def.discriminants(tcx).enumerate() { target_blocks.place_back() <- if variants.contains(idx) { - values.push(discr); + values.push(discr.to_u128_unchecked()); *(targets.place_back() <- self.cfg.start_new_block()) } else { if otherwise_block.is_none() { @@ -228,9 +227,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()), @@ -244,13 +243,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, }) }; @@ -260,49 +256,81 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, '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]. - let expect = if let ConstVal::ByteStr(bytes) = value.val { - let tcx = self.hir.tcx(); - - // Unsize the place to &[u8], too, if necessary. - if let ty::TyRef(region, mt) = ty.sty { - if let ty::TyArray(_, _) = mt.ty.sty { - ty = tcx.mk_imm_ref(region, tcx.mk_slice(tcx.types.u8)); - let val_slice = self.temp(ty, test.span); - self.cfg.push_assign(block, source_info, &val_slice, - Rvalue::Cast(CastKind::Unsize, val, ty)); - val = Operand::Move(val_slice); - } - } - - assert!(ty.is_slice()); - - let array_ty = tcx.mk_array(tcx.types.u8, bytes.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 slice = self.temp(ty, test.span); - self.cfg.push_assign(block, source_info, &slice, - Rvalue::Cast(CastKind::Unsize, array, ty)); - Operand::Move(slice) - } else { - self.literal_operand(test.span, ty, Literal::Value { - value - }) - }; - - // 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(); - if let ty::TyRef(_, mt) = ty.sty { - assert!(ty.is_slice()); + if !ty.is_scalar() { + // unsize arrays to slices + 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 a3350cb1671d2..78c7004ef6034 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> { @@ -62,49 +61,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { ty::TyChar => { Literal::Value { value: self.hir.tcx().mk_const(ty::Const { - val: ConstVal::Char('\0'), + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(0))), ty }) } } - ty::TyUint(ity) => { - let val = match ity { - ast::UintTy::U8 => ConstInt::U8(0), - ast::UintTy::U16 => ConstInt::U16(0), - ast::UintTy::U32 => ConstInt::U32(0), - ast::UintTy::U64 => ConstInt::U64(0), - ast::UintTy::U128 => ConstInt::U128(0), - ast::UintTy::Usize => { - let uint_ty = self.hir.tcx().sess.target.usize_ty; - let val = ConstUsize::new(0, uint_ty).unwrap(); - ConstInt::Usize(val) - } - }; - - Literal::Value { - value: self.hir.tcx().mk_const(ty::Const { - val: ConstVal::Integral(val), - ty - }) - } - } - ty::TyInt(ity) => { - let val = match ity { - ast::IntTy::I8 => ConstInt::I8(0), - ast::IntTy::I16 => ConstInt::I16(0), - ast::IntTy::I32 => ConstInt::I32(0), - ast::IntTy::I64 => ConstInt::I64(0), - ast::IntTy::I128 => ConstInt::I128(0), - ast::IntTy::Isize => { - let int_ty = self.hir.tcx().sess.target.isize_ty; - let val = ConstIsize::new(0, int_ty).unwrap(); - ConstInt::Isize(val) - } - }; - + ty::TyUint(_) | + ty::TyInt(_) => { Literal::Value { value: self.hir.tcx().mk_const(ty::Const { - val: ConstVal::Integral(val), + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(0))), ty }) } diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 57059cd31a1bc..4b73484306029 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -21,7 +21,7 @@ use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::subst::Substs; use rustc::util::nodemap::NodeMap; use rustc_back::PanicStrategy; -use rustc_const_eval::pattern::{BindingMode, PatternKind}; +use const_eval::pattern::{BindingMode, PatternKind}; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; use shim; use std::mem; diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_mir/const_eval/_match.rs similarity index 85% rename from src/librustc_const_eval/_match.rs rename to src/librustc_mir/const_eval/_match.rs index 33d9bfa6e6b9c..2c6a66816e6fe 100644 --- a/src/librustc_const_eval/_match.rs +++ b/src/librustc_mir/const_eval/_match.rs @@ -13,21 +13,20 @@ use self::Usefulness::*; use self::WitnessPreference::*; use rustc::middle::const_val::ConstVal; -use eval::{compare_const_vals}; - -use rustc_const_math::ConstInt; +use const_eval::eval::{compare_const_vals}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::indexed_vec::Idx; -use pattern::{FieldPattern, Pattern, PatternKind}; -use pattern::{PatternFoldable, PatternFolder}; +use const_eval::pattern::{FieldPattern, Pattern, PatternKind}; +use const_eval::pattern::{PatternFoldable, PatternFolder}; use rustc::hir::def_id::DefId; use rustc::hir::RangeEnd; 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 +181,39 @@ 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, ty::NoPreference) + .and_then(|t| t.ty.builtin_index()) + .map_or(false, |t| t == tcx.types.u8); + assert!(is_array_ptr); + let alloc = tcx + .interpret_interner + .borrow() + .get_alloc(ptr.alloc_id) + .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 +442,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_u128().is_some() => { + let len = len.val.unwrap_u64(); if len != 0 && cx.is_uninhabited(sub_ty) { vec![] } else { @@ -461,7 +481,7 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, } fn max_slice_length<'p, 'a: 'p, 'tcx: 'a, I>( - _cx: &mut MatchCheckCtxt<'a, 'tcx>, + cx: &mut MatchCheckCtxt<'a, 'tcx>, patterns: I) -> u64 where I: Iterator> { @@ -535,8 +555,24 @@ 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, ty::NoPreference) + .and_then(|t| t.ty.builtin_index()) + .map_or(false, |t| t == cx.tcx.types.u8); + if is_array_ptr { + let alloc = cx.tcx + .interpret_interner + .borrow() + .get_alloc(ptr.alloc_id) + .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; @@ -575,7 +611,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. @@ -601,10 +637,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) @@ -614,9 +650,9 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, let used_ctors: Vec = rows.iter().flat_map(|row| { pat_constructors(cx, row[0], pcx).unwrap_or(vec![]) }).collect(); - debug!("used_ctors = {:?}", used_ctors); + debug!("used_ctors = {:#?}", used_ctors); let all_ctors = all_constructors(cx, pcx); - debug!("all_ctors = {:?}", all_ctors); + debug!("all_ctors = {:#?}", all_ctors); let missing_ctors: Vec = all_ctors.iter().filter(|c| { !used_ctors.contains(*c) }).cloned().collect(); @@ -644,7 +680,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" @@ -744,7 +780,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 { @@ -796,7 +832,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) }, @@ -817,7 +853,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 { @@ -841,12 +877,13 @@ fn constructor_sub_pattern_tys<'a, 'tcx: 'a>(cx: &MatchCheckCtxt<'a, 'tcx>, ctor: &Constructor, ty: Ty<'tcx>) -> Vec> { - debug!("constructor_sub_pattern_tys({:?}, {:?})", ctor, ty); + debug!("constructor_sub_pattern_tys({:#?}, {:?})", ctor, ty); match ty.sty { ty::TyTuple(ref fs, _) => fs.into_iter().map(|t| *t).collect(), ty::TySlice(ty) | ty::TyArray(ty, _) => match *ctor { Slice(length) => (0..length).map(|_| ty).collect(), ConstantValue(_) => vec![], + Single => vec![ty], _ => bug!("bad slice pattern {:?} {:?}", ctor, ty) }, ty::TyRef(_, ref ty_and_mut) => vec![ty_and_mut.ty], @@ -855,6 +892,9 @@ fn constructor_sub_pattern_tys<'a, 'tcx: 'a>(cx: &MatchCheckCtxt<'a, 'tcx>, // Use T as the sub pattern type of Box. vec![substs[0].as_type().unwrap()] } else { + if let ConstantValue(_) = *ctor { + return vec![]; + } adt.variants[ctor.variant_index_for_adt(adt)].fields.iter().map(|field| { let is_visible = adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx); @@ -876,14 +916,29 @@ fn constructor_sub_pattern_tys<'a, 'tcx: 'a>(cx: &MatchCheckCtxt<'a, 'tcx>, } } -fn slice_pat_covered_by_constructor(_tcx: TyCtxt, _span: Span, +fn slice_pat_covered_by_constructor(tcx: TyCtxt, _span: Span, ctor: &Constructor, prefix: &[Pattern], slice: &Option, suffix: &[Pattern]) -> Result { - let data = match *ctor { - ConstantValue(&ty::Const { val: ConstVal::ByteStr(b), .. }) => b.data, + let data: &[u8] = match *ctor { + ConstantValue(&ty::Const { val: ConstVal::Value( + Value::ByVal(PrimVal::Ptr(ptr)) + ), ty }) => { + let is_array_ptr = ty + .builtin_deref(true, ty::NoPreference) + .and_then(|t| t.ty.builtin_index()) + .map_or(false, |t| t == tcx.types.u8); + assert!(is_array_ptr); + tcx + .interpret_interner + .borrow() + .get_alloc(ptr.alloc_id) + .unwrap() + .bytes + .as_ref() + } _ => bug!() }; @@ -898,11 +953,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) }, _ => {} @@ -912,32 +968,43 @@ fn slice_pat_covered_by_constructor(_tcx: TyCtxt, _span: Span, Ok(true) } -fn constructor_covered_by_range(tcx: TyCtxt, span: Span, - ctor: &Constructor, +fn constructor_covered_by_range(ctor: &Constructor, from: &ConstVal, to: &ConstVal, - end: RangeEnd) + end: RangeEnd, + ty: Ty) -> Result { - let cmp_from = |c_from| Ok(compare_const_vals(tcx, span, c_from, from)? != Ordering::Less); - let cmp_to = |c_to| compare_const_vals(tcx, span, c_to, to); + trace!("constructor_covered_by_range {:#?}, {:#?}, {:#?}, {}", ctor, from, to, ty); + let cmp_from = |c_from| compare_const_vals(c_from, from, ty) + .map(|res| res != Ordering::Less); + let cmp_to = |c_to| compare_const_vals(c_to, to, ty); + macro_rules! some_or_ok { + ($e:expr) => { + match $e { + Some(to) => to, + None => return Ok(false), // not char or int + } + }; + } match *ctor { ConstantValue(value) => { - let to = cmp_to(&value.val)?; + let to = some_or_ok!(cmp_to(&value.val)); let end = (to == Ordering::Less) || (end == RangeEnd::Included && to == Ordering::Equal); - Ok(cmp_from(&value.val)? && end) + Ok(some_or_ok!(cmp_from(&value.val)) && end) }, ConstantRange(from, to, RangeEnd::Included) => { - let to = cmp_to(&to.val)?; + let to = some_or_ok!(cmp_to(&to.val)); let end = (to == Ordering::Less) || (end == RangeEnd::Included && to == Ordering::Equal); - Ok(cmp_from(&from.val)? && end) + Ok(some_or_ok!(cmp_from(&from.val)) && end) }, ConstantRange(from, to, RangeEnd::Excluded) => { - let to = cmp_to(&to.val)?; + let to = some_or_ok!(cmp_to(&to.val)); let end = (to == Ordering::Less) || (end == RangeEnd::Excluded && to == Ordering::Equal); - Ok(cmp_from(&from.val)? && end) + Ok(some_or_ok!(cmp_from(&from.val)) && end) } + Variant(_) | Single => Ok(true), _ => bug!(), } @@ -954,7 +1021,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 } @@ -969,7 +1036,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>> { @@ -999,8 +1066,20 @@ 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, ty::NoPreference) + .and_then(|t| t.ty.builtin_index()) + .map_or(false, |t| t == cx.tcx.types.u8); + assert!(is_array_ptr); + let data_len = cx.tcx + .interpret_interner + .borrow() + .get_alloc(ptr.alloc_id) + .unwrap() + .bytes + .len(); + if wild_patterns.len() == data_len { Some(cx.lower_byte_str_pattern(pat)) } else { None @@ -1011,7 +1090,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, @@ -1023,7 +1103,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, @@ -1067,7 +1147,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_mir/const_eval/check.rs b/src/librustc_mir/const_eval/check.rs new file mode 100644 index 0000000000000..9827dd58cd640 --- /dev/null +++ b/src/librustc_mir/const_eval/check.rs @@ -0,0 +1,197 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Lints statically known runtime failures + +use rustc::mir::*; +use rustc::hir; +use rustc::hir::map::Node; +use rustc::mir::visit::Visitor; +use rustc::mir::interpret::{Value, PrimVal, GlobalId}; +use rustc::middle::const_val::{ConstVal, ConstEvalErr, ErrKind}; +use rustc::hir::def::Def; +use rustc::traits; +use interpret::eval_body_with_mir; +use rustc::ty::{TyCtxt, ParamEnv}; +use rustc::ty::Instance; +use rustc::ty::layout::LayoutOf; +use rustc::hir::def_id::DefId; +use rustc::ty::subst::Substs; + +fn is_const<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool { + if let Some(node) = tcx.hir.get_if_local(def_id) { + match node { + Node::NodeItem(&hir::Item { + node: hir::ItemConst(..), .. + }) => true, + _ => false + } + } else { + match tcx.describe_def(def_id) { + Some(Def::Const(_)) => true, + _ => false + } + } +} + +pub fn check<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) { + let mir = &tcx.optimized_mir(def_id); + let substs = Substs::identity_for_item(tcx, def_id); + let instance = Instance::new(def_id, substs); + let param_env = tcx.param_env(def_id); + + if is_const(tcx, def_id) { + let cid = GlobalId { + instance, + promoted: None, + }; + eval_body_with_mir(tcx, cid, mir, param_env); + } + + ConstErrVisitor { + tcx, + mir, + }.visit_mir(mir); + let outer_def_id = if tcx.is_closure(def_id) { + tcx.closure_base_def_id(def_id) + } else { + def_id + }; + let generics = tcx.generics_of(outer_def_id); + // FIXME: miri should be able to eval stuff that doesn't need info + // from the generics + if generics.parent_types as usize + generics.types.len() > 0 { + return; + } + for i in 0.. mir.promoted.len() { + use rustc_data_structures::indexed_vec::Idx; + let cid = GlobalId { + instance, + promoted: Some(Promoted::new(i)), + }; + eval_body_with_mir(tcx, cid, mir, param_env); + } +} + +struct ConstErrVisitor<'a, 'tcx: 'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir: &'a Mir<'tcx>, +} + +impl<'a, 'tcx> ConstErrVisitor<'a, 'tcx> { + fn eval_op(&self, op: &Operand<'tcx>) -> Option { + let op = match *op { + Operand::Constant(ref c) => c, + _ => return None, + }; + match op.literal { + Literal::Value { value } => match value.val { + ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))) => Some(b), + _ => return None, + }, + _ => None, + } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for ConstErrVisitor<'a, 'tcx> { + fn visit_terminator(&mut self, + block: BasicBlock, + terminator: &Terminator<'tcx>, + location: Location) { + self.super_terminator(block, terminator, location); + match terminator.kind { + TerminatorKind::Assert { ref cond, expected, ref msg, .. } => { + let cond = match self.eval_op(cond) { + Some(val) => val, + None => return, + }; + if (cond == 1) == expected { + return; + } + assert!(cond <= 1); + // If we know we always panic, and the error message + // is also constant, then we can produce a warning. + + let kind = match *msg { + AssertMessage::BoundsCheck { ref len, ref index } => { + let len = match self.eval_op(len) { + Some(val) => val, + None => return, + }; + let index = match self.eval_op(index) { + Some(val) => val, + None => return, + }; + ErrKind::IndexOutOfBounds { + len: len as u64, + index: index as u64 + } + } + AssertMessage::Math(ref err) => ErrKind::Math(err.clone()), + AssertMessage::GeneratorResumedAfterReturn | + // FIXME(oli-obk): can we report a const_err warning here? + AssertMessage::GeneratorResumedAfterPanic => return, + }; + let span = terminator.source_info.span; + let msg = ConstEvalErr{ span, kind }; + let scope_info = match self.mir.visibility_scope_info { + ClearCrossCrate::Set(ref data) => data, + ClearCrossCrate::Clear => return, + }; + let node_id = scope_info[terminator.source_info.scope].lint_root; + self.tcx.lint_node(::rustc::lint::builtin::CONST_ERR, + node_id, + msg.span, + &msg.description().into_oneline().into_owned()); + }, + _ => {}, + } + } + fn visit_rvalue(&mut self, + rvalue: &Rvalue<'tcx>, + location: Location) { + self.super_rvalue(rvalue, location); + use rustc::mir::BinOp; + match *rvalue { + Rvalue::BinaryOp(BinOp::Shr, ref lop, ref rop) | + Rvalue::BinaryOp(BinOp::Shl, ref lop, ref rop) => { + let val = match self.eval_op(rop) { + Some(val) => val, + None => return, + }; + let ty = lop.ty(self.mir, self.tcx); + let param_env = ParamEnv::empty(traits::Reveal::All); + let bits = (self.tcx, param_env).layout_of(ty).unwrap().size.bits(); + if val >= bits as u128 { + let data = &self.mir[location.block]; + let stmt_idx = location.statement_index; + let source_info = if stmt_idx < data.statements.len() { + data.statements[stmt_idx].source_info + } else { + data.terminator().source_info + }; + let span = source_info.span; + let scope_info = match self.mir.visibility_scope_info { + ClearCrossCrate::Set(ref data) => data, + ClearCrossCrate::Clear => return, + }; + let node_id = scope_info[source_info.scope].lint_root; + self.tcx.lint_node( + ::rustc::lint::builtin::EXCEEDING_BITSHIFTS, + node_id, + span, + "bitshift exceeds the type's number of bits"); + } + } + _ => {} + } + } +} diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_mir/const_eval/check_match.rs similarity index 96% rename from src/librustc_const_eval/check_match.rs rename to src/librustc_mir/const_eval/check_match.rs index fd171b8992470..9de43d6376ec7 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_mir/const_eval/check_match.rs @@ -8,11 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use _match::{MatchCheckCtxt, Matrix, expand_pattern, is_useful}; -use _match::Usefulness::*; -use _match::WitnessPreference::*; +use const_eval::_match::{MatchCheckCtxt, Matrix, expand_pattern, is_useful}; +use const_eval::_match::Usefulness::*; +use const_eval::_match::WitnessPreference::*; -use pattern::{Pattern, PatternContext, PatternError, PatternKind}; +use const_eval::pattern::{Pattern, PatternContext, PatternError, PatternKind}; use rustc::middle::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor}; use rustc::middle::expr_use_visitor::{LoanCause, MutateMode}; @@ -135,8 +135,18 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { span_err!(self.tcx.sess, span, E0158, "statics cannot be referenced in patterns"); } - PatternError::ConstEval(ref err) => { - err.report(self.tcx, pat_span, "pattern"); + PatternError::FloatBug => { + // FIXME(#31407) this is only necessary because float parsing is buggy + ::rustc::middle::const_val::struct_error( + self.tcx, pat_span, + "could not evaluate float literal (see issue #31407)", + ).emit(); + } + PatternError::NonConstPath(span) => { + ::rustc::middle::const_val::struct_error( + self.tcx, span, + "runtime values cannot be referenced in patterns", + ).emit(); } } } diff --git a/src/librustc_mir/const_eval/eval.rs b/src/librustc_mir/const_eval/eval.rs new file mode 100644 index 0000000000000..25a9e52236773 --- /dev/null +++ b/src/librustc_mir/const_eval/eval.rs @@ -0,0 +1,158 @@ +// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::middle::const_val::ConstVal::*; +use rustc::middle::const_val::ConstVal; + +use rustc::hir::def_id::DefId; +use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::subst::Substs; + +use syntax::ast; + +use std::cmp::Ordering; + +use rustc_const_math::*; + +/// * `DefId` is the id of the constant. +/// * `Substs` is the monomorphized substitutions for the expression. +pub fn lookup_const_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + key: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>) + -> Option<(DefId, &'tcx Substs<'tcx>)> { + ty::Instance::resolve( + tcx, + key.param_env, + key.value.0, + key.value.1, + ).map(|instance| (instance.def_id(), instance.substs)) +} + +pub fn lit_to_const<'a, 'tcx>(lit: &'tcx ast::LitKind, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + ty: Ty<'tcx>, + neg: bool) + -> Result, ()> { + use syntax::ast::*; + + use rustc::mir::interpret::*; + let lit = match *lit { + LitKind::Str(ref s, _) => { + let s = s.as_str(); + let id = tcx.allocate_cached(s.as_bytes()); + let ptr = MemoryPointer::new(id, 0); + Value::ByValPair( + PrimVal::Ptr(ptr), + PrimVal::from_u128(s.len() as u128), + ) + }, + LitKind::ByteStr(ref data) => { + let id = tcx.allocate_cached(data); + let ptr = MemoryPointer::new(id, 0); + Value::ByVal(PrimVal::Ptr(ptr)) + }, + LitKind::Byte(n) => Value::ByVal(PrimVal::Bytes(n as u128)), + LitKind::Int(n, _) => { + enum Int { + Signed(IntTy), + Unsigned(UintTy), + } + let ty = match ty.sty { + ty::TyInt(IntTy::Isize) => Int::Signed(tcx.sess.target.isize_ty), + ty::TyInt(other) => Int::Signed(other), + ty::TyUint(UintTy::Usize) => Int::Unsigned(tcx.sess.target.usize_ty), + ty::TyUint(other) => Int::Unsigned(other), + _ => bug!(), + }; + let n = match ty { + // FIXME(oli-obk): are these casts correct? + Int::Signed(IntTy::I8) if neg => + (n as i128 as i8).overflowing_neg().0 as i128 as u128, + Int::Signed(IntTy::I16) if neg => + (n as i128 as i16).overflowing_neg().0 as i128 as u128, + Int::Signed(IntTy::I32) if neg => + (n as i128 as i32).overflowing_neg().0 as i128 as u128, + Int::Signed(IntTy::I64) if neg => + (n as i128 as i64).overflowing_neg().0 as i128 as u128, + Int::Signed(IntTy::I128) if neg => + (n as i128).overflowing_neg().0 as u128, + Int::Signed(IntTy::I8) => n as i128 as i8 as i128 as u128, + Int::Signed(IntTy::I16) => n as i128 as i16 as i128 as u128, + Int::Signed(IntTy::I32) => n as i128 as i32 as i128 as u128, + Int::Signed(IntTy::I64) => n as i128 as i64 as i128 as u128, + Int::Signed(IntTy::I128) => n, + Int::Unsigned(UintTy::U8) => n as u8 as u128, + Int::Unsigned(UintTy::U16) => n as u16 as u128, + Int::Unsigned(UintTy::U32) => n as u32 as u128, + Int::Unsigned(UintTy::U64) => n as u64 as u128, + Int::Unsigned(UintTy::U128) => n, + _ => bug!(), + }; + Value::ByVal(PrimVal::Bytes(n)) + }, + LitKind::Float(n, fty) => { + let n = n.as_str(); + let mut f = parse_float(&n, fty)?; + if neg { + f = -f; + } + let bits = f.bits; + Value::ByVal(PrimVal::Bytes(bits)) + } + LitKind::FloatUnsuffixed(n) => { + let fty = match ty.sty { + ty::TyFloat(fty) => fty, + _ => bug!() + }; + let n = n.as_str(); + let mut f = parse_float(&n, fty)?; + if neg { + f = -f; + } + let bits = f.bits; + Value::ByVal(PrimVal::Bytes(bits)) + } + LitKind::Bool(b) => Value::ByVal(PrimVal::Bytes(b as u128)), + LitKind::Char(c) => Value::ByVal(PrimVal::Bytes(c as u128)), + }; + Ok(ConstVal::Value(lit)) +} + +fn parse_float<'tcx>(num: &str, fty: ast::FloatTy) + -> Result { + ConstFloat::from_str(num, fty).map_err(|_| ()) +} + +pub fn compare_const_vals(a: &ConstVal, b: &ConstVal, ty: Ty) -> Option { + trace!("compare_const_vals: {:?}, {:?}", a, b); + use rustc::mir::interpret::{Value, PrimVal}; + match (a, b) { + (&Value(Value::ByVal(PrimVal::Bytes(a))), + &Value(Value::ByVal(PrimVal::Bytes(b)))) => { + match ty.sty { + ty::TyFloat(ty) => { + let l = ConstFloat { + bits: a, + ty, + }; + let r = ConstFloat { + bits: b, + ty, + }; + // FIXME(oli-obk): report cmp errors? + l.try_cmp(r).ok() + }, + ty::TyInt(_) => Some((a as i128).cmp(&(b as i128))), + _ => Some(a.cmp(&b)), + } + }, + _ if a == b => Some(Ordering::Equal), + _ => None, + } +} diff --git a/src/librustc_mir/const_eval/mod.rs b/src/librustc_mir/const_eval/mod.rs new file mode 100644 index 0000000000000..f47dc61c27c4d --- /dev/null +++ b/src/librustc_mir/const_eval/mod.rs @@ -0,0 +1,19 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! constant evaluation on the HIR and code to validate patterns/matches + +mod eval; +mod _match; +pub mod check_match; +pub mod pattern; +pub mod check; + +pub use self::eval::*; diff --git a/src/librustc_const_eval/pattern.rs b/src/librustc_mir/const_eval/pattern.rs similarity index 72% rename from src/librustc_const_eval/pattern.rs rename to src/librustc_mir/const_eval/pattern.rs index 3cfa1d6797d1f..e3fe1230f308a 100644 --- a/src/librustc_const_eval/pattern.rs +++ b/src/librustc_mir/const_eval/pattern.rs @@ -8,15 +8,17 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use eval; +use interpret::{const_val_field, const_discr}; -use rustc::middle::const_val::{ConstEvalErr, ConstVal}; +use rustc::middle::const_val::ConstVal; use rustc::mir::{Field, BorrowKind, Mutability}; +use rustc::mir::interpret::{GlobalId, Value, PrimVal}; use rustc::ty::{self, TyCtxt, AdtDef, Ty, Region}; use rustc::ty::subst::{Substs, Kind}; use rustc::hir::{self, PatKind, RangeEnd}; use rustc::hir::def::{Def, CtorKind}; use rustc::hir::pat_util::EnumerateAndAdjustIterator; +use const_eval::eval::compare_const_vals; use rustc_data_structures::indexed_vec::Idx; @@ -26,9 +28,10 @@ use syntax::ptr::P; use syntax_pos::Span; #[derive(Clone, Debug)] -pub enum PatternError<'tcx> { +pub enum PatternError { StaticInPattern(Span), - ConstEval(ConstEvalErr<'tcx>), + FloatBug, + NonConstPath(Span), } #[derive(Copy, Clone, Debug)] @@ -107,21 +110,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 { @@ -229,15 +237,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 } => { @@ -269,7 +277,7 @@ pub struct PatternContext<'a, 'tcx: 'a> { pub param_env: ty::ParamEnv<'tcx>, pub tables: &'a ty::TypeckTables<'tcx>, pub substs: &'tcx Substs<'tcx>, - pub errors: Vec>, + pub errors: Vec, } impl<'a, 'tcx> Pattern<'tcx> { @@ -347,10 +355,27 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { PatKind::Lit(ref value) => self.lower_lit(value), - PatKind::Range(ref lo, ref hi, end) => { - match (self.lower_lit(lo), self.lower_lit(hi)) { + PatKind::Range(ref lo_expr, ref hi_expr, end) => { + match (self.lower_lit(lo_expr), self.lower_lit(hi_expr)) { (PatternKind::Constant { value: lo }, PatternKind::Constant { value: hi }) => { + use std::cmp::Ordering; + match (end, compare_const_vals(&lo.val, &hi.val, ty).unwrap()) { + (RangeEnd::Excluded, Ordering::Less) => {}, + (RangeEnd::Excluded, _) => span_err!( + self.tcx.sess, + lo_expr.span, + E0579, + "lower range bound must be less than upper", + ), + (RangeEnd::Included, Ordering::Greater) => { + struct_span_err!(self.tcx.sess, lo_expr.span, E0030, + "lower range bound must be less than or equal to upper") + .span_label(lo_expr.span, "lower bound larger than upper bound") + .emit(); + }, + (RangeEnd::Included, _) => {} + } PatternKind::Range { lo, hi, end } } _ => PatternKind::Wild @@ -358,7 +383,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } PatKind::Path(ref qpath) => { - return self.lower_path(qpath, pat.hir_id, pat.id, pat.span); + return self.lower_path(qpath, pat.hir_id, pat.span); } PatKind::Ref(ref subpattern, _) | @@ -468,7 +493,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, _) => { @@ -500,7 +525,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) } }; @@ -577,7 +602,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 } } @@ -591,6 +616,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { fn lower_variant_or_leaf( &mut self, def: Def, + span: Span, ty: Ty<'tcx>, subpatterns: Vec>) -> PatternKind<'tcx> @@ -621,14 +647,16 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { PatternKind::Leaf { subpatterns: subpatterns } } - _ => bug!() + _ => { + self.errors.push(PatternError::NonConstPath(span)); + PatternKind::Wild + } } } 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); @@ -636,30 +664,34 @@ 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(PatternError::StaticInPattern(span)); PatternKind::Wild - } + }, } } - _ => self.lower_variant_or_leaf(def, ty, vec![]), + _ => self.lower_variant_or_leaf(def, span, ty, vec![]), }; Pattern { @@ -670,137 +702,190 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } 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 super::eval::lit_to_const(&lit.node, self.tcx, ty, false) { + Ok(val) => { + let instance = ty::Instance::new( + self.tables.local_id_root.expect("literal outside any scope"), + self.substs, + ); + let cv = self.tcx.mk_const(ty::Const { val, ty }); + *self.const_to_pat(instance, cv, expr.hir_id, lit.span).kind + }, + Err(()) => { + 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 super::eval::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 { + fn const_to_pat( + &self, + instance: ty::Instance<'tcx>, + cv: &'tcx ty::Const<'tcx>, + id: hir::HirId, + span: Span, + ) -> Pattern<'tcx> { + debug!("const_to_pat: cv={:#?}", cv); + let kind = match cv.ty.sty { ty::TyFloat(_) => { - 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.to_u128_unchecked() == discr) + .unwrap(); + PatternKind::Variant { + adt_def, + substs, + variant_index, + subpatterns: adt_def + .variants[variant_index] + .fields + .iter() + .enumerate() + .map(|(i, _)| { + let field = Field::new(i); + let val = match cv.val { + ConstVal::Value(miri) => const_val_field( + self.tcx, self.param_env, instance, + Some(variant_index), field, miri, cv.ty, + ).unwrap(), + _ => bug!("{:#?} is not a valid tuple", cv), + }; + FieldPattern { + field, + pattern: self.const_to_pat(instance, val, id, span), + } + }).collect(), + } + }, + _ => return Pattern { + span, + ty: cv.ty, + kind: Box::new(PatternKind::Constant { + value: cv, + }), + } } - } - _ => { } - } - 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)| { + subpatterns: struct_var.fields.iter().enumerate().map(|(i, _)| { + let field = Field::new(i); + let val = match cv.val { + ConstVal::Value(miri) => const_val_field( + self.tcx, self.param_env, instance, None, field, miri, cv.ty, + ).unwrap(), + _ => bug!("{:#?} is not a valid tuple", cv), + }; FieldPattern { - field: Field::new(i), - pattern: self.lower_const_expr(expr, pat_id, span) + field, + pattern: self.const_to_pat(instance, val, id, span), } }).collect() } } - - hir::ExprCall(ref callee, ref args) => { - let qpath = match callee.node { - hir::ExprPath(ref qpath) => qpath, - _ => bug!() - }; - let ty = self.tables.node_id_to_type(callee.hir_id); - let def = self.tables.qpath_def(qpath, callee.hir_id); - match def { - Def::Fn(..) | Def::Method(..) => self.lower_lit(expr), - _ => { - let subpatterns = args.iter().enumerate().map(|(i, expr)| { - FieldPattern { - field: Field::new(i), - pattern: self.lower_const_expr(expr, pat_id, span) - } - }).collect(); - self.lower_variant_or_leaf(def, ty, subpatterns) - } + ty::TyTuple(fields, _) => { + PatternKind::Leaf { + subpatterns: (0..fields.len()).map(|i| { + let field = Field::new(i); + let val = match cv.val { + ConstVal::Value(miri) => const_val_field( + self.tcx, self.param_env, instance, None, field, miri, cv.ty, + ).unwrap(), + _ => bug!("{:#?} is not a valid tuple", cv), + }; + FieldPattern { + field, + pattern: self.const_to_pat(instance, val, id, span), + } + }).collect() } } - - 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| { + let i = i as usize; + let field = Field::new(i); + let val = match cv.val { + ConstVal::Value(miri) => const_val_field( + self.tcx, self.param_env, instance, None, field, miri, cv.ty, + ).unwrap(), + _ => bug!("{:#?} is not a valid tuple", cv), + }; + self.const_to_pat(instance, val, id, span) + }).collect(), slice: None, - suffix: vec![] + 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), } } diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index f76aea19677d9..3946f9b2d45f3 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -782,7 +782,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 619c0dc847ebc..f53fe35b15d93 100644 --- a/src/librustc_mir/diagnostics.rs +++ b/src/librustc_mir/diagnostics.rs @@ -12,6 +12,541 @@ register_long_diagnostics! { + +E0001: r##" +#### Note: this error code is no longer emitted by the compiler. + +This error suggests that the expression arm corresponding to the noted pattern +will never be reached as for all possible values of the expression being +matched, one of the preceding patterns will match. + +This means that perhaps some of the preceding patterns are too general, this +one is too specific or the ordering is incorrect. + +For example, the following `match` block has too many arms: + +``` +match Some(0) { + Some(bar) => {/* ... */} + x => {/* ... */} // This handles the `None` case + _ => {/* ... */} // All possible cases have already been handled +} +``` + +`match` blocks have their patterns matched in order, so, for example, putting +a wildcard arm above a more specific arm will make the latter arm irrelevant. + +Ensure the ordering of the match arm is correct and remove any superfluous +arms. +"##, + +E0002: r##" +#### Note: this error code is no longer emitted by the compiler. + +This error indicates that an empty match expression is invalid because the type +it is matching on is non-empty (there exist values of this type). In safe code +it is impossible to create an instance of an empty type, so empty match +expressions are almost never desired. This error is typically fixed by adding +one or more cases to the match expression. + +An example of an empty type is `enum Empty { }`. So, the following will work: + +``` +enum Empty {} + +fn foo(x: Empty) { + match x { + // empty + } +} +``` + +However, this won't: + +```compile_fail +fn foo(x: Option) { + match x { + // empty + } +} +``` +"##, + +E0004: r##" +This error indicates that the compiler cannot guarantee a matching pattern for +one or more possible inputs to a match expression. Guaranteed matches are +required in order to assign values to match expressions, or alternatively, +determine the flow of execution. Erroneous code example: + +```compile_fail,E0004 +enum Terminator { + HastaLaVistaBaby, + TalkToMyHand, +} + +let x = Terminator::HastaLaVistaBaby; + +match x { // error: non-exhaustive patterns: `HastaLaVistaBaby` not covered + Terminator::TalkToMyHand => {} +} +``` + +If you encounter this error you must alter your patterns so that every possible +value of the input type is matched. For types with a small number of variants +(like enums) you should probably cover all cases explicitly. Alternatively, the +underscore `_` wildcard pattern can be added after all other patterns to match +"anything else". Example: + +``` +enum Terminator { + HastaLaVistaBaby, + TalkToMyHand, +} + +let x = Terminator::HastaLaVistaBaby; + +match x { + Terminator::TalkToMyHand => {} + Terminator::HastaLaVistaBaby => {} +} + +// or: + +match x { + Terminator::TalkToMyHand => {} + _ => {} +} +``` +"##, + +E0005: r##" +Patterns used to bind names must be irrefutable, that is, they must guarantee +that a name will be extracted in all cases. Erroneous code example: + +```compile_fail,E0005 +let x = Some(1); +let Some(y) = x; +// error: refutable pattern in local binding: `None` not covered +``` + +If you encounter this error you probably need to use a `match` or `if let` to +deal with the possibility of failure. Example: + +``` +let x = Some(1); + +match x { + Some(y) => { + // do something + }, + None => {} +} + +// or: + +if let Some(y) = x { + // do something +} +``` +"##, + +E0007: r##" +This error indicates that the bindings in a match arm would require a value to +be moved into more than one location, thus violating unique ownership. Code +like the following is invalid as it requires the entire `Option` to be +moved into a variable called `op_string` while simultaneously requiring the +inner `String` to be moved into a variable called `s`. + +```compile_fail,E0007 +let x = Some("s".to_string()); + +match x { + op_string @ Some(s) => {}, // error: cannot bind by-move with sub-bindings + None => {}, +} +``` + +See also the error E0303. +"##, + +E0008: r##" +Names bound in match arms retain their type in pattern guards. As such, if a +name is bound by move in a pattern, it should also be moved to wherever it is +referenced in the pattern guard code. Doing so however would prevent the name +from being available in the body of the match arm. Consider the following: + +```compile_fail,E0008 +match Some("hi".to_string()) { + Some(s) if s.len() == 0 => {}, // use s. + _ => {}, +} +``` + +The variable `s` has type `String`, and its use in the guard is as a variable of +type `String`. The guard code effectively executes in a separate scope to the +body of the arm, so the value would be moved into this anonymous scope and +therefore becomes unavailable in the body of the arm. + +The problem above can be solved by using the `ref` keyword. + +``` +match Some("hi".to_string()) { + Some(ref s) if s.len() == 0 => {}, + _ => {}, +} +``` + +Though this example seems innocuous and easy to solve, the problem becomes clear +when it encounters functions which consume the value: + +```compile_fail,E0008 +struct A{} + +impl A { + fn consume(self) -> usize { + 0 + } +} + +fn main() { + let a = Some(A{}); + match a { + Some(y) if y.consume() > 0 => {} + _ => {} + } +} +``` + +In this situation, even the `ref` keyword cannot solve it, since borrowed +content cannot be moved. This problem cannot be solved generally. If the value +can be cloned, here is a not-so-specific solution: + +``` +#[derive(Clone)] +struct A{} + +impl A { + fn consume(self) -> usize { + 0 + } +} + +fn main() { + let a = Some(A{}); + match a{ + Some(ref y) if y.clone().consume() > 0 => {} + _ => {} + } +} +``` + +If the value will be consumed in the pattern guard, using its clone will not +move its ownership, so the code works. +"##, + +E0009: r##" +In a pattern, all values that don't implement the `Copy` trait have to be bound +the same way. The goal here is to avoid binding simultaneously by-move and +by-ref. + +This limitation may be removed in a future version of Rust. + +Erroneous code example: + +```compile_fail,E0009 +struct X { x: (), } + +let x = Some((X { x: () }, X { x: () })); +match x { + Some((y, ref z)) => {}, // error: cannot bind by-move and by-ref in the + // same pattern + None => panic!() +} +``` + +You have two solutions: + +Solution #1: Bind the pattern's values the same way. + +``` +struct X { x: (), } + +let x = Some((X { x: () }, X { x: () })); +match x { + Some((ref y, ref z)) => {}, + // or Some((y, z)) => {} + None => panic!() +} +``` + +Solution #2: Implement the `Copy` trait for the `X` structure. + +However, please keep in mind that the first solution should be preferred. + +``` +#[derive(Clone, Copy)] +struct X { x: (), } + +let x = Some((X { x: () }, X { x: () })); +match x { + Some((y, ref z)) => {}, + None => panic!() +} +``` +"##, + +E0030: r##" +When matching against a range, the compiler verifies that the range is +non-empty. Range patterns include both end-points, so this is equivalent to +requiring the start of the range to be less than or equal to the end of the +range. + +For example: + +```compile_fail +match 5u32 { + // This range is ok, albeit pointless. + 1 ... 1 => {} + // This range is empty, and the compiler can tell. + 1000 ... 5 => {} +} +``` +"##, + +E0158: r##" +`const` and `static` mean different things. A `const` is a compile-time +constant, an alias for a literal value. This property means you can match it +directly within a pattern. + +The `static` keyword, on the other hand, guarantees a fixed location in memory. +This does not always mean that the value is constant. For example, a global +mutex can be declared `static` as well. + +If you want to match against a `static`, consider using a guard instead: + +``` +static FORTY_TWO: i32 = 42; + +match Some(42) { + Some(x) if x == FORTY_TWO => {} + _ => {} +} +``` +"##, + +E0162: r##" +An if-let pattern attempts to match the pattern, and enters the body if the +match was successful. If the match is irrefutable (when it cannot fail to +match), use a regular `let`-binding instead. For instance: + +```compile_fail,E0162 +struct Irrefutable(i32); +let irr = Irrefutable(0); + +// This fails to compile because the match is irrefutable. +if let Irrefutable(x) = irr { + // This body will always be executed. + // ... +} +``` + +Try this instead: + +``` +struct Irrefutable(i32); +let irr = Irrefutable(0); + +let Irrefutable(x) = irr; +println!("{}", x); +``` +"##, + +E0165: r##" +A while-let pattern attempts to match the pattern, and enters the body if the +match was successful. If the match is irrefutable (when it cannot fail to +match), use a regular `let`-binding inside a `loop` instead. For instance: + +```compile_fail,E0165 +struct Irrefutable(i32); +let irr = Irrefutable(0); + +// This fails to compile because the match is irrefutable. +while let Irrefutable(x) = irr { + // ... +} +``` + +Try this instead: + +```no_run +struct Irrefutable(i32); +let irr = Irrefutable(0); + +loop { + let Irrefutable(x) = irr; + // ... +} +``` +"##, + +E0170: r##" +Enum variants are qualified by default. For example, given this type: + +``` +enum Method { + GET, + POST, +} +``` + +You would match it using: + +``` +enum Method { + GET, + POST, +} + +let m = Method::GET; + +match m { + Method::GET => {}, + Method::POST => {}, +} +``` + +If you don't qualify the names, the code will bind new variables named "GET" and +"POST" instead. This behavior is likely not what you want, so `rustc` warns when +that happens. + +Qualified names are good practice, and most code works well with them. But if +you prefer them unqualified, you can import the variants into scope: + +``` +use Method::*; +enum Method { GET, POST } +# fn main() {} +``` + +If you want others to be able to import variants from your module directly, use +`pub use`: + +``` +pub use Method::*; +pub enum Method { GET, POST } +# fn main() {} +``` +"##, + + +E0297: r##" +#### Note: this error code is no longer emitted by the compiler. + +Patterns used to bind names must be irrefutable. That is, they must guarantee +that a name will be extracted in all cases. Instead of pattern matching the +loop variable, consider using a `match` or `if let` inside the loop body. For +instance: + +```compile_fail,E0005 +let xs : Vec> = vec![Some(1), None]; + +// This fails because `None` is not covered. +for Some(x) in xs { + // ... +} +``` + +Match inside the loop instead: + +``` +let xs : Vec> = vec![Some(1), None]; + +for item in xs { + match item { + Some(x) => {}, + None => {}, + } +} +``` + +Or use `if let`: + +``` +let xs : Vec> = vec![Some(1), None]; + +for item in xs { + if let Some(x) = item { + // ... + } +} +``` +"##, + +E0301: r##" +Mutable borrows are not allowed in pattern guards, because matching cannot have +side effects. Side effects could alter the matched object or the environment +on which the match depends in such a way, that the match would not be +exhaustive. For instance, the following would not match any arm if mutable +borrows were allowed: + +```compile_fail,E0301 +match Some(()) { + None => { }, + option if option.take().is_none() => { + /* impossible, option is `Some` */ + }, + Some(_) => { } // When the previous match failed, the option became `None`. +} +``` +"##, + +E0302: r##" +Assignments are not allowed in pattern guards, because matching cannot have +side effects. Side effects could alter the matched object or the environment +on which the match depends in such a way, that the match would not be +exhaustive. For instance, the following would not match any arm if assignments +were allowed: + +```compile_fail,E0302 +match Some(()) { + None => { }, + option if { option = None; false } => { }, + Some(_) => { } // When the previous match failed, the option became `None`. +} +``` +"##, + +E0303: r##" +In certain cases it is possible for sub-bindings to violate memory safety. +Updates to the borrow checker in a future version of Rust may remove this +restriction, but for now patterns must be rewritten without sub-bindings. + +Before: + +```compile_fail,E0303 +match Some("hi".to_string()) { + ref op_string_ref @ Some(s) => {}, + None => {}, +} +``` + +After: + +``` +match Some("hi".to_string()) { + Some(ref s) => { + let op_string_ref = &Some(s); + // ... + }, + None => {}, +} +``` + +The `op_string_ref` binding has type `&Option<&String>` in both cases. + +See also https://github.com/rust-lang/rust/issues/14587 +"##, + E0010: r##" The value of statics and constants must be known at compile time, and they live for the entire lifetime of a program. Creating a boxed value allocates memory on @@ -1613,6 +2148,24 @@ fn main() { ``` "##, +E0579: r##" +When matching against an exclusive range, the compiler verifies that the range +is non-empty. Exclusive range patterns include the start point but not the end +point, so this is equivalent to requiring the start of the range to be less +than the end of the range. + +For example: + +```compile_fail +match 5u32 { + // This range is ok, albeit pointless. + 1 .. 2 => {} + // This range is empty, and the compiler can tell. + 5 .. 5 => {} +} +``` +"##, + E0595: r##" Closures cannot mutate immutable captured variables. @@ -1771,6 +2324,9 @@ b.resume(); } register_diagnostics! { +// E0298, // cannot compare constants +// E0299, // mismatched types between arms +// E0471, // constant evaluation error (in pattern) // E0385, // {} in an aliasable location E0493, // destructors cannot be evaluated at compile-time E0524, // two closures require unique access to `..` at the same time diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index 848c2d3c811e9..2df3a10d89614 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}; use rustc::ty::cast::CastKind as TyCastKind; @@ -99,7 +99,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, @@ -281,7 +281,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) { @@ -367,9 +369,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, @@ -475,10 +478,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_usize(cx.tcx), + Err(e) => { + e.report(cx.tcx, cx.tcx.def_span(def_id), "array length"); + ConstUsize::new(0, cx.tcx.sess.target.usize_ty).unwrap() + }, }; ExprKind::Repeat { @@ -601,8 +616,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 }), }, @@ -634,13 +649,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 725a1b845e7ea..f65029fed0a9a 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -16,8 +16,7 @@ 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; @@ -26,11 +25,12 @@ use rustc::infer::InferCtxt; use rustc::ty::subst::Subst; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::subst::Substs; -use syntax::ast; +use syntax::ast::{self, LitKind}; use syntax::attr; use syntax::symbol::Symbol; use rustc::hir; -use rustc_const_math::{ConstInt, ConstUsize}; +use rustc_const_math::{ConstUsize, ConstFloat}; +use rustc::mir::interpret::{Value, PrimVal}; use std::rc::Rc; #[derive(Clone)] @@ -119,7 +119,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { Ok(val) => { Literal::Value { value: self.tcx.mk_const(ty::Const { - val: ConstVal::Integral(ConstInt::Usize(val)), + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(val.as_u64() as u128))), ty: self.tcx.types.usize }) } @@ -139,7 +139,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { pub fn true_literal(&mut self) -> Literal<'tcx> { Literal::Value { value: self.tcx.mk_const(ty::Const { - val: ConstVal::Bool(true), + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(1))), ty: self.tcx.types.bool }) } @@ -148,20 +148,81 @@ 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 const_eval_literal( + &mut self, + lit: &'tcx ast::LitKind, + ty: Ty<'tcx>, + sp: Span, + neg: bool, + ) -> Literal<'tcx> { let tcx = self.tcx.global_tcx(); - let const_cx = ConstContext::new(tcx, - self.param_env.and(self.identity_substs), - self.tables()); - match const_cx.eval(tcx.hir.expect_expr(e.id)) { - Ok(value) => Literal::Value { value }, - Err(s) => self.fatal_const_eval_err(&s, e.span, "expression") + + let 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)"); + }) + }; + + 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; + Value::ByVal(PrimVal::Bytes(n as u128)) + }, + LitKind::Int(n, _) => Value::ByVal(PrimVal::Bytes(n)), + LitKind::Float(n, fty) => { + let n = n.as_str(); + let mut f = parse_float(&n, fty); + if neg { + f = -f; + } + let bits = f.bits; + Value::ByVal(PrimVal::Bytes(bits)) + } + LitKind::FloatUnsuffixed(n) => { + let fty = match ty.sty { + ty::TyFloat(fty) => fty, + _ => bug!() + }; + let n = n.as_str(); + let mut f = parse_float(&n, fty); + if neg { + f = -f; + } + let bits = f.bits; + Value::ByVal(PrimVal::Bytes(bits)) + } + LitKind::Bool(b) => Value::ByVal(PrimVal::Bytes(b as u128)), + LitKind::Char(c) => Value::ByVal(PrimVal::Bytes(c as u128)), + }; + Literal::Value { + value: self.tcx.mk_const(ty::Const { + val: ConstVal::Value(lit), + ty, + }), } } @@ -177,17 +238,6 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { p) } - pub fn fatal_const_eval_err(&mut self, - err: &ConstEvalErr<'tcx>, - primary_span: Span, - primary_kind: &str) - -> ! - { - err.report(self.tcx, primary_span, primary_kind); - self.tcx.sess.abort_if_errors(); - unreachable!() - } - pub fn trait_method(&mut self, trait_def_id: DefId, method_name: &str, @@ -203,7 +253,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 09a31f9ab8fa5..329365c4415e8 100644 --- a/src/librustc_mir/hair/mod.rs +++ b/src/librustc_mir/hair/mod.rs @@ -27,7 +27,7 @@ use self::cx::Cx; pub mod cx; -pub use rustc_const_eval::pattern::{BindingMode, Pattern, PatternKind, FieldPattern}; +pub use const_eval::pattern::{BindingMode, Pattern, PatternKind, FieldPattern}; #[derive(Copy, Clone, Debug)] pub enum LintLevel { diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index b476ea5685229..7d7e6ec9451ed 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -7,7 +7,7 @@ use rustc::mir::interpret::{PrimVal, EvalResult, MemoryPointer, PointerArithmeti use rustc_apfloat::ieee::{Single, Double}; use rustc_apfloat::Float; -impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { pub(super) fn cast_primval( &self, val: PrimVal, diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index 2b95449f767dd..89edb61af7838 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -1,31 +1,46 @@ +use rustc::hir; +use rustc::middle::const_val::{ConstEvalErr, ConstVal}; +use rustc::middle::const_val::ErrKind::{TypeckError, CheckMatchError}; +use rustc::mir; use rustc::ty::{self, TyCtxt, Ty, Instance}; use rustc::ty::layout::{self, LayoutOf}; -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; +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 limits = super::ResourceLimits::default(); + let mut ecx = EvalContext::new(tcx, param_env, limits, CompileTimeEvaluator, ()); + // insert a stack frame so any queries have the correct substs + ecx.push_stack_frame( + instance, + 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, ()); @@ -41,99 +56,110 @@ 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); + let limits = super::ResourceLimits::default(); + let mut ecx = EvalContext::new(tcx, param_env, limits, 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]; } - }) + let layout = ecx.layout_of(mir.return_ty().subst(tcx, cid.instance.substs))?; + let alloc = tcx.interpret_interner.borrow().get_cached(cid.instance.def_id()); + let alloc = match alloc { + Some(alloc) => { + assert!(cid.promoted.is_none()); + assert!(param_env.caller_bounds.is_empty()); + alloc + }, + None => { + assert!(!layout.is_unsized()); + let ptr = ecx.memory.allocate( + layout.size.bytes(), + layout.align, + None, + )?; + if tcx.is_static(cid.instance.def_id()).is_some() { + tcx.interpret_interner.borrow_mut().cache(cid.instance.def_id(), ptr.alloc_id); + } + let span = tcx.def_span(cid.instance.def_id()); + let internally_mutable = !layout.ty.is_freeze(tcx, param_env, span); + let mutability = tcx.is_static(cid.instance.def_id()); + let mutability = if mutability == Some(hir::Mutability::MutMutable) || internally_mutable { + Mutability::Mutable + } else { + Mutability::Immutable + }; + let cleanup = StackPopCleanup::MarkStatic(mutability); + let name = ty::tls::with(|tcx| tcx.item_path_str(cid.instance.def_id())); + trace!("const_eval: pushing stack frame for global: {}", name); + ecx.push_stack_frame( + cid.instance, + mir.span, + mir, + Place::from_ptr(ptr, layout.align), + cleanup, + )?; + + while ecx.step()? {} + ptr.alloc_id + } + }; + let ptr = MemoryPointer::new(alloc, 0).into(); + let value = match ecx.try_read_value(ptr, layout.align, layout.ty)? { + Some(val) => val, + _ => Value::ByRef(ptr, layout.align), + }; + Ok((value, ptr, layout.ty)) + })(); + (res, ecx) } pub struct CompileTimeEvaluator; impl<'tcx> Into> for ConstEvalError { fn into(self) -> EvalError<'tcx> { - EvalErrorKind::MachineError(Box::new(self)).into() + EvalErrorKind::MachineError(self.to_string()).into() } } @@ -173,27 +199,40 @@ 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(), @@ -219,7 +258,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, @@ -255,7 +294,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>, @@ -271,12 +310,16 @@ impl<'tcx> super::Machine<'tcx> for CompileTimeEvaluator { } } - fn mark_static_initialized(m: !) -> EvalResult<'tcx> { - m + fn mark_static_initialized<'a>( + _mem: &mut Memory<'a, 'mir, 'tcx, Self>, + _id: AllocId, + _mutability: Mutability, + ) -> EvalResult<'tcx, bool> { + Ok(false) } fn box_alloc<'a>( - _ecx: &mut EvalContext<'a, 'tcx, Self>, + _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, _ty: Ty<'tcx>, _dest: Place, ) -> EvalResult<'tcx> { @@ -286,7 +329,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> { @@ -296,274 +339,150 @@ impl<'tcx> super::Machine<'tcx> for CompileTimeEvaluator { } } +pub fn const_val_field<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + instance: ty::Instance<'tcx>, + variant: Option, + field: mir::Field, + val: Value, + ty: Ty<'tcx>, +) -> ::rustc::middle::const_val::EvalResult<'tcx> { + match const_val_field_inner(tcx, param_env, instance, variant, field, val, ty) { + Ok((field, ty)) => Ok(tcx.mk_const(ty::Const { + val: ConstVal::Value(field), + ty, + })), + Err(err) => Err(ConstEvalErr { + span: tcx.def_span(instance.def_id()), + kind: err.into(), + }), + } +} + +fn const_val_field_inner<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + instance: ty::Instance<'tcx>, + variant: Option, + field: mir::Field, + value: Value, + ty: Ty<'tcx>, +) -> EvalResult<'tcx, (Value, Ty<'tcx>)> { + trace!("const_val_field: {:?}, {:?}, {:?}, {:?}", instance, field, value, ty); + let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap(); + let (mut field, ty) = match value { + Value::ByValPair(..) | Value::ByVal(_) => ecx.read_field(value, variant, field, ty)?.expect("const_val_field on non-field"), + Value::ByRef(ptr, align) => { + let place = Place::Ptr { + ptr, + align, + extra: variant.map_or(PlaceExtra::None, PlaceExtra::DowncastVariant), + }; + let layout = ecx.layout_of(ty)?; + let (place, layout) = ecx.place_field(place, field, layout)?; + let (ptr, align) = place.to_ptr_align(); + (Value::ByRef(ptr, align), layout.ty) + } + }; + if let Value::ByRef(ptr, align) = field { + if let Some(val) = ecx.try_read_value(ptr, align, ty)? { + field = val; + } + } + Ok((field, ty)) +} + +pub fn const_discr<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + instance: ty::Instance<'tcx>, + value: Value, + ty: Ty<'tcx>, +) -> EvalResult<'tcx, u128> { + trace!("const_discr: {:?}, {:?}, {:?}", instance, value, ty); + let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap(); + let (ptr, align) = match value { + Value::ByValPair(..) | Value::ByVal(_) => { + let layout = ecx.layout_of(ty)?; + use super::MemoryKind; + let ptr = ecx.memory.allocate(layout.size.bytes(), layout.align, Some(MemoryKind::Stack))?; + let ptr: Pointer = ptr.into(); + ecx.write_value_to_ptr(value, ptr, layout.align, ty)?; + (ptr, layout.align) + }, + Value::ByRef(ptr, align) => (ptr, align), + }; + let place = Place::from_primval_ptr(ptr, align); + ecx.read_discriminant_value(place, ty) +} + pub fn const_eval_provider<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, - key: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>, + key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, ) -> ::rustc::middle::const_val::EvalResult<'tcx> { trace!("const eval: {:?}", key); - let (def_id, substs) = if let Some(resolved) = lookup_const_by_id(tcx, key) { - resolved - } else { - return Err(ConstEvalErr { - span: tcx.def_span(key.value.0), - kind: TypeckError - }); - }; + let cid = key.value; + let def_id = cid.instance.def.def_id(); + let span = tcx.def_span(def_id); + + if tcx.is_foreign_item(def_id) { + let id = tcx.interpret_interner.borrow().get_cached(def_id); + let id = match id { + // FIXME: due to caches this shouldn't happen, add some assertions + Some(id) => id, + None => { + let id = tcx.interpret_interner.borrow_mut().reserve(); + tcx.interpret_interner.borrow_mut().cache(def_id, id); + id + }, + }; + let ty = tcx.type_of(def_id); + let layout = (tcx, key.param_env).layout_of(ty).unwrap(); + let ptr = MemoryPointer::new(id, 0); + return Ok(tcx.mk_const(ty::Const { + val: ConstVal::Value(Value::ByRef(ptr.into(), layout.align)), + ty, + })) + } - let tables = tcx.typeck_tables_of(def_id); - let body = if let Some(id) = tcx.hir.as_local_node_id(def_id) { - let body_id = tcx.hir.body_owned_by(id); + if let Some(id) = tcx.hir.as_local_node_id(def_id) { + let tables = tcx.typeck_tables_of(def_id); // Do match-check before building MIR if tcx.check_match(def_id).is_err() { return Err(ConstEvalErr { - span: tcx.def_span(key.value.0), kind: 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: 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"), - TyNever => bug!("miri produced a value of the never type"), - TyProjection(_) => bug!("miri produced a projection"), - TyAnon(..) => bug!("miri produced an impl Trait type"), - TyParam(_) => bug!("miri produced an unmonomorphized type"), - TyInfer(_) => bug!("miri produced an uninferred type"), - TyError => bug!("miri produced a type error"), - TyForeign(_) => bug!("miri produced an extern type"), - // should be fine - TyFnDef(..) => {} - TyFnPtr(_) => { - let value = ecx.value_to_primval(miri_val); - let ptr = match value { - Ok(PrimVal::Ptr(ptr)) => ptr, - value => bug!("expected fn ptr, got {:?}", value), - }; - let inst = ecx.memory.get_fn(ptr).unwrap(); - match ctfe { - ConstVal::Function(did, substs) => { - let ctfe = ty::Instance::resolve( - ecx.tcx, - ecx.param_env, - did, - substs, - ).unwrap(); - assert_eq!(inst, ctfe, "expected fn ptr {:?}, but got {:?}", ctfe, inst); - }, - _ => bug!("ctfe produced {:?}, but miri produced function {:?}", ctfe, inst), - } - }, - } -} -fn get_prim<'a, 'tcx>( - ecx: &mut EvalContext<'a, 'tcx, CompileTimeEvaluator>, - val: ValTy<'tcx>, -) -> u128 { - let res = ecx.value_to_primval(val).and_then(|prim| prim.to_bytes()); - unwrap_miri(ecx, res) -} - -fn unwrap_miri<'a, 'tcx, T>( - ecx: &EvalContext<'a, 'tcx, CompileTimeEvaluator>, - res: Result>, -) -> T { - match res { - Ok(val) => val, - Err(mut err) => { - ecx.report(&mut err); - ecx.tcx.sess.abort_if_errors(); - bug!("{:#?}", err); + let (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); } - } + ConstEvalErr { + kind: err.into(), + span, + } + }) } diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index baec0fea50f1e..59634cc6e6770 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -3,14 +3,14 @@ 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, Kind}; use rustc::ty::{self, Ty, TyCtxt}; use rustc_data_structures::indexed_vec::Idx; -use syntax::codemap::{self, DUMMY_SP}; +use syntax::codemap::{self, DUMMY_SP, Span}; use syntax::ast::Mutability; use rustc::mir::interpret::{ GlobalId, Value, Pointer, PrimVal, PrimValKind, @@ -21,7 +21,7 @@ use super::{Place, PlaceExtra, Memory, HasMemory, MemoryKind, operator, Machine}; -pub struct EvalContext<'a, 'tcx: 'a, M: Machine<'tcx>> { +pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { /// Stores the `Machine` instance. pub machine: M, @@ -32,10 +32,10 @@ pub struct EvalContext<'a, 'tcx: 'a, M: Machine<'tcx>> { pub param_env: ty::ParamEnv<'tcx>, /// The virtual memory system. - pub memory: Memory<'a, 'tcx, M>, + pub memory: Memory<'a, 'mir, 'tcx, M>, /// The virtual call stack. - pub(crate) stack: Vec>, + pub(crate) stack: Vec>, /// The maximum number of stack frames allowed pub(crate) stack_limit: usize, @@ -47,12 +47,12 @@ pub struct EvalContext<'a, 'tcx: 'a, M: Machine<'tcx>> { } /// A stack frame. -pub struct Frame<'tcx> { +pub struct Frame<'mir, 'tcx: 'mir> { //////////////////////////////////////////////////////////////////////////////// // Function and callsite information //////////////////////////////////////////////////////////////////////////////// /// The MIR for the function called on this frame. - pub mir: &'tcx mir::Mir<'tcx>, + pub mir: &'mir mir::Mir<'tcx>, /// The def_id and substs of the current function pub instance: ty::Instance<'tcx>, @@ -131,6 +131,15 @@ pub struct ValTy<'tcx> { pub ty: Ty<'tcx>, } +impl<'tcx> ValTy<'tcx> { + pub fn from(val: &ty::Const<'tcx>) -> Option { + match val.val { + ConstVal::Value(value) => Some(ValTy { value, ty: val.ty }), + ConstVal::Unevaluated { .. } => None, + } + } +} + impl<'tcx> ::std::ops::Deref for ValTy<'tcx> { type Target = Value; fn deref(&self) -> &Value { @@ -138,37 +147,37 @@ impl<'tcx> ::std::ops::Deref for ValTy<'tcx> { } } -impl<'a, 'tcx, M: Machine<'tcx>> HasDataLayout for &'a EvalContext<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for &'a EvalContext<'a, 'mir, 'tcx, M> { #[inline] fn data_layout(&self) -> &layout::TargetDataLayout { &self.tcx.data_layout } } -impl<'c, 'b, 'a, 'tcx, M: Machine<'tcx>> HasDataLayout - for &'c &'b mut EvalContext<'a, 'tcx, M> { +impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout + for &'c &'b mut EvalContext<'a, 'mir, 'tcx, M> { #[inline] fn data_layout(&self) -> &layout::TargetDataLayout { &self.tcx.data_layout } } -impl<'a, 'tcx, M: Machine<'tcx>> layout::HasTyCtxt<'tcx> for &'a EvalContext<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> layout::HasTyCtxt<'tcx> for &'a EvalContext<'a, 'mir, 'tcx, M> { #[inline] fn tcx<'b>(&'b self) -> TyCtxt<'b, 'tcx, 'tcx> { self.tcx } } -impl<'c, 'b, 'a, 'tcx, M: Machine<'tcx>> layout::HasTyCtxt<'tcx> - for &'c &'b mut EvalContext<'a, 'tcx, M> { +impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> layout::HasTyCtxt<'tcx> + for &'c &'b mut EvalContext<'a, 'mir, 'tcx, M> { #[inline] fn tcx<'d>(&'d self) -> TyCtxt<'d, 'tcx, 'tcx> { self.tcx } } -impl<'a, 'tcx, M: Machine<'tcx>> LayoutOf> for &'a EvalContext<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> LayoutOf> for &'a EvalContext<'a, 'mir, 'tcx, M> { type TyLayout = EvalResult<'tcx, TyLayout<'tcx>>; fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout { @@ -177,8 +186,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> LayoutOf> for &'a EvalContext<'a, 'tcx } } -impl<'c, 'b, 'a, 'tcx, M: Machine<'tcx>> LayoutOf> - for &'c &'b mut EvalContext<'a, 'tcx, M> { +impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> LayoutOf> + for &'c &'b mut EvalContext<'a, 'mir, 'tcx, M> { type TyLayout = EvalResult<'tcx, TyLayout<'tcx>>; #[inline] @@ -187,7 +196,7 @@ impl<'c, 'b, 'a, 'tcx, M: Machine<'tcx>> LayoutOf> } } -impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { pub fn new( tcx: TyCtxt<'a, 'tcx, 'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -214,15 +223,15 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { self.memory.allocate(size, layout.align, Some(MemoryKind::Stack)) } - pub fn memory(&self) -> &Memory<'a, 'tcx, M> { + pub fn memory(&self) -> &Memory<'a, 'mir, 'tcx, M> { &self.memory } - pub fn memory_mut(&mut self) -> &mut Memory<'a, 'tcx, M> { + pub fn memory_mut(&mut self) -> &mut Memory<'a, 'mir, 'tcx, M> { &mut self.memory } - pub fn stack(&self) -> &[Frame<'tcx>] { + pub fn stack(&self) -> &[Frame<'mir, 'tcx>] { &self.stack } @@ -240,39 +249,17 @@ 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>> { @@ -402,14 +389,14 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { &mut self, instance: ty::Instance<'tcx>, span: codemap::Span, - mir: &'tcx mir::Mir<'tcx>, + mir: &'mir mir::Mir<'tcx>, return_place: Place, return_to_block: StackPopCleanup, ) -> EvalResult<'tcx> { ::log_settings::settings().indentation += 1; /// Return the set of locals that have a storage annotation anywhere - fn collect_storage_annotations<'tcx>(mir: &'tcx mir::Mir<'tcx>) -> HashSet { + fn collect_storage_annotations<'mir, 'tcx>(mir: &'mir mir::Mir<'tcx>) -> HashSet { use rustc::mir::StatementKind::*; let mut set = HashSet::new(); @@ -600,7 +587,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 {:?}", @@ -837,7 +824,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)? } }; @@ -949,9 +936,28 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { Ok(()) } - pub fn read_global_as_value(&self, gid: GlobalId, layout: TyLayout) -> Value { - let alloc = self.tcx.interpret_interner.borrow().get_cached(gid).expect("global not cached"); - Value::ByRef(MemoryPointer::new(alloc, 0).into(), layout.align) + pub fn read_global_as_value(&self, gid: GlobalId<'tcx>, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { + if gid.promoted.is_none() { + let cached = self + .tcx + .interpret_interner + .borrow() + .get_cached(gid.instance.def_id()); + if let Some(alloc_id) = cached { + let layout = self.layout_of(ty)?; + let ptr = MemoryPointer::new(alloc_id, 0); + return Ok(Value::ByRef(ptr.into(), layout.align)) + } + } + let cv = match self.tcx.const_eval(self.param_env.and(gid)) { + Ok(val) => val, + Err(err) => match err.kind { + ErrKind::Miri(miri) => return Err(miri), + ErrKind::TypeckError => return err!(TypeckError), + other => bug!("const eval returned {:?}", other), + }, + }; + self.const_to_value(&cv.val, ty) } pub fn force_allocation(&mut self, place: Place) -> EvalResult<'tcx, Place> { @@ -1344,15 +1350,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 } @@ -1381,7 +1387,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) @@ -1562,7 +1568,10 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { Ok(()) } - pub fn report(&self, e: &mut EvalError) { + pub fn report(&self, e: &mut EvalError, as_err: bool, explicit_span: Option) { + if let EvalErrorKind::TypeckError = e.kind { + return; + } if let Some(ref mut backtrace) = e.backtrace { let mut trace_text = "\n\nAn error occurred in miri:\n".to_string(); backtrace.resolve(); @@ -1595,13 +1604,43 @@ 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 node_id = self + .stack() + .iter() + .rev() + .filter_map(|frame| self.tcx.hir.as_local_node_id(frame.instance.def_id())) + .next() + .expect("some part of a failing const eval must be local"); + let mut err = if as_err { + ::rustc::middle::const_val::struct_error(self.tcx, span, "constant evaluation error") + } else { + self.tcx.struct_span_lint_node( + ::rustc::lint::builtin::CONST_ERR, + node_id, + span, + "constant evaluation error", + ) }; - let mut err = self.tcx.sess.struct_span_err(span, &e.to_string()); + err.span_label(span, e.to_string()); + let mut last_span = None; for &Frame { instance, span, .. } in self.stack().iter().rev() { + // make sure we don't emit frames that are duplicates of the previous + if explicit_span == Some(span) { + continue; + } + if let Some(last) = last_span { + if last == span { + continue; + } + } else { + last_span = Some(span); + } if self.tcx.def_key(instance.def_id()).disambiguated_data.data == DefPathData::ClosureExpr { @@ -1617,7 +1656,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } } -impl<'tcx> Frame<'tcx> { +impl<'mir, 'tcx> Frame<'mir, 'tcx> { pub fn get_local(&self, local: mir::Local) -> EvalResult<'tcx, Value> { // Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0. self.locals[local.index() - 1].ok_or(EvalErrorKind::DeadLocal.into()) diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs index c2989dbaaf11f..d5e57d3317c55 100644 --- a/src/librustc_mir/interpret/machine.rs +++ b/src/librustc_mir/interpret/machine.rs @@ -12,7 +12,7 @@ use syntax::ast::Mutability; /// Methods of this trait signifies a point where CTFE evaluation would fail /// and some use case dependent behaviour can instead be applied -pub trait Machine<'tcx>: Sized { +pub trait Machine<'mir, 'tcx>: Sized { /// Additional data that can be accessed via the Memory type MemoryData; @@ -26,7 +26,7 @@ pub trait Machine<'tcx>: Sized { /// /// Returns Ok(false) if a new stack frame was pushed fn eval_fn_call<'a>( - ecx: &mut EvalContext<'a, 'tcx, Self>, + ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, instance: ty::Instance<'tcx>, destination: Option<(Place, mir::BasicBlock)>, args: &[ValTy<'tcx>], @@ -36,7 +36,7 @@ pub trait Machine<'tcx>: Sized { /// directly process an intrinsic without pushing a stack frame. fn call_intrinsic<'a>( - ecx: &mut EvalContext<'a, 'tcx, Self>, + ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, instance: ty::Instance<'tcx>, args: &[ValTy<'tcx>], dest: Place, @@ -51,7 +51,7 @@ pub trait Machine<'tcx>: Sized { /// /// Returns a (value, overflowed) pair if the operation succeeded fn try_ptr_op<'a>( - ecx: &EvalContext<'a, 'tcx, Self>, + ecx: &EvalContext<'a, 'mir, 'tcx, Self>, bin_op: mir::BinOp, left: PrimVal, left_ty: Ty<'tcx>, @@ -60,26 +60,30 @@ pub trait Machine<'tcx>: Sized { ) -> EvalResult<'tcx, Option<(PrimVal, bool)>>; /// Called when trying to mark machine defined `MemoryKinds` as static - fn mark_static_initialized(m: Self::MemoryKinds) -> EvalResult<'tcx>; + fn mark_static_initialized<'a>( + _mem: &mut Memory<'a, 'mir, 'tcx, Self>, + _id: AllocId, + _mutability: Mutability, + ) -> EvalResult<'tcx, bool>; /// Heap allocations via the `box` keyword /// /// Returns a pointer to the allocated memory fn box_alloc<'a>( - ecx: &mut EvalContext<'a, 'tcx, Self>, + ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, ty: Ty<'tcx>, dest: Place, ) -> EvalResult<'tcx>; /// Called when trying to access a global declared with a `linkage` attribute fn global_item_with_linkage<'a>( - ecx: &mut EvalContext<'a, 'tcx, Self>, + ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, instance: ty::Instance<'tcx>, mutability: Mutability, ) -> EvalResult<'tcx>; fn check_locks<'a>( - _mem: &Memory<'a, 'tcx, Self>, + _mem: &Memory<'a, 'mir, 'tcx, Self>, _ptr: MemoryPointer, _size: u64, _access: AccessKind, @@ -88,12 +92,12 @@ pub trait Machine<'tcx>: Sized { } fn add_lock<'a>( - _mem: &mut Memory<'a, 'tcx, Self>, + _mem: &mut Memory<'a, 'mir, 'tcx, Self>, _id: AllocId, ) {} fn free_lock<'a>( - _mem: &mut Memory<'a, 'tcx, Self>, + _mem: &mut Memory<'a, 'mir, 'tcx, Self>, _id: AllocId, _len: u64, ) -> EvalResult<'tcx> { @@ -101,14 +105,14 @@ pub trait Machine<'tcx>: Sized { } fn end_region<'a>( - _ecx: &mut EvalContext<'a, 'tcx, Self>, + _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, _reg: Option<::rustc::middle::region::Scope>, ) -> EvalResult<'tcx> { Ok(()) } fn validation_op<'a>( - _ecx: &mut EvalContext<'a, 'tcx, Self>, + _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, _op: ::rustc::mir::ValidationOp, _operand: &::rustc::mir::ValidationOperand<'tcx, ::rustc::mir::Place<'tcx>>, ) -> EvalResult<'tcx> { diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 3a28eae2d1c49..63c0c950dd49b 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -1,6 +1,6 @@ use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian, BigEndian}; use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque}; -use std::{ptr, mem, io}; +use std::{ptr, io}; use rustc::ty::{Instance, TyCtxt}; use rustc::ty::layout::{self, Align, TargetDataLayout}; @@ -19,8 +19,6 @@ use super::{EvalContext, Machine}; pub enum MemoryKind { /// Error if deallocated except during a stack pop Stack, - /// A mutable Static. All the others are interned in the tcx - MutableStatic, // FIXME: move me into the machine, rustc const eval doesn't need them /// Additional memory kinds a machine wishes to distinguish from the builtin ones Machine(T), } @@ -29,7 +27,7 @@ pub enum MemoryKind { // Top-level interpreter memory //////////////////////////////////////////////////////////////////////////////// -pub struct Memory<'a, 'tcx: 'a, M: Machine<'tcx>> { +pub struct Memory<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { /// Additional data required by the Machine pub data: M::MemoryData, @@ -56,7 +54,7 @@ pub struct Memory<'a, 'tcx: 'a, M: Machine<'tcx>> { pub tcx: TyCtxt<'a, 'tcx, 'tcx>, } -impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, max_memory: u64, data: M::MemoryData) -> Self { Memory { data, @@ -107,6 +105,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { relocations: BTreeMap::new(), undef_mask: UndefMask::new(size), align, + mutable: false, }; let id = self.tcx.interpret_interner.borrow_mut().reserve(); M::add_lock(self, id); @@ -119,7 +118,6 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { None => { self.uninitialized_statics.insert(id, alloc); }, - Some(MemoryKind::MutableStatic) => bug!("don't allocate mutable statics directly") } Ok(MemoryPointer::new(id, 0)) } @@ -164,10 +162,6 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { pub fn deallocate_local(&mut self, ptr: MemoryPointer) -> EvalResult<'tcx> { match self.alloc_kind.get(&ptr.alloc_id).cloned() { - // for a constant like `const FOO: &i32 = &1;` the local containing - // the `1` is referred to by the global. We transitively marked everything - // the global refers to as static itself, so we don't free it here - Some(MemoryKind::MutableStatic) => Ok(()), Some(MemoryKind::Stack) => self.deallocate(ptr, None, MemoryKind::Stack), // Happens if the memory was interned into immutable memory None => Ok(()), @@ -292,7 +286,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { } /// Allocation accessors -impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { pub fn get(&self, id: AllocId) -> EvalResult<'tcx, &Allocation> { // normal alloc? match self.alloc_map.get(&id) { @@ -376,7 +370,6 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { Some(a) => (a, match self.alloc_kind[&id] { MemoryKind::Stack => " (stack)".to_owned(), MemoryKind::Machine(m) => format!(" ({:?})", m), - MemoryKind::MutableStatic => " (static mut)".to_owned(), }), // uninitialized static alloc? None => match self.uninitialized_statics.get(&id) { @@ -388,15 +381,15 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { Some(a) => (a, "(immutable)".to_owned()), None => if let Some(func) = int.get_fn(id) { trace!("{} {}", msg, func); - continue; + continue; } else { - trace!("{} (deallocated)", msg); - continue; + trace!("{} (deallocated)", msg); + continue; }, - } + } }, }, - }; + }; for i in 0..(alloc.bytes.len() as u64) { if let Some(&target_id) = alloc.relocations.get(&i) { @@ -441,14 +434,9 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { pub fn leak_report(&self) -> usize { trace!("### LEAK REPORT ###"); - let kinds = &self.alloc_kind; let leaks: Vec<_> = self.alloc_map .keys() - .filter_map(|key| if kinds[key] != MemoryKind::MutableStatic { - Some(*key) - } else { - None - }) + .cloned() .collect(); let n = leaks.len(); self.dump_allocs(leaks); @@ -457,7 +445,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { } /// Byte accessors -impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { fn get_bytes_unchecked( &self, ptr: MemoryPointer, @@ -521,7 +509,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { } /// Reading and writing -impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { /// mark an allocation pointed to by a static as static and initialized fn mark_inner_allocation_initialized( &mut self, @@ -529,10 +517,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { mutability: Mutability, ) -> EvalResult<'tcx> { match self.alloc_kind.get(&alloc) { - // do not go into immutable statics - None | - // or mutable statics - Some(&MemoryKind::MutableStatic) => Ok(()), + // do not go into statics + None => Ok(()), // just locals and machine allocs Some(_) => self.mark_static_initalized(alloc, mutability), } @@ -549,60 +535,27 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { alloc_id, mutability ); - if mutability == Mutability::Immutable { - let alloc = self.alloc_map.remove(&alloc_id); - let kind = self.alloc_kind.remove(&alloc_id); - assert_ne!(kind, Some(MemoryKind::MutableStatic)); - let uninit = self.uninitialized_statics.remove(&alloc_id); - if let Some(alloc) = alloc.or(uninit) { - let alloc = self.tcx.intern_const_alloc(alloc); - self.tcx.interpret_interner.borrow_mut().intern_at_reserved(alloc_id, alloc); - // recurse into inner allocations - for &alloc in alloc.relocations.values() { - self.mark_inner_allocation_initialized(alloc, mutability)?; - } - } - return Ok(()); + // The machine handled it + if M::mark_static_initialized(self, alloc_id, mutability)? { + return Ok(()) } - // We are marking the static as initialized, so move it out of the uninit map - if let Some(uninit) = self.uninitialized_statics.remove(&alloc_id) { - self.alloc_map.insert(alloc_id, uninit); + let alloc = self.alloc_map.remove(&alloc_id); + match self.alloc_kind.remove(&alloc_id) { + None => {}, + Some(MemoryKind::Machine(_)) => bug!("machine didn't handle machine alloc"), + Some(MemoryKind::Stack) => {}, } - // do not use `self.get_mut(alloc_id)` here, because we might have already marked a - // sub-element or have circular pointers (e.g. `Rc`-cycles) - let relocations = match self.alloc_map.get_mut(&alloc_id) { - Some(&mut Allocation { - ref mut relocations, - .. - }) => { - match self.alloc_kind.get(&alloc_id) { - // const eval results can refer to "locals". - // E.g. `const Foo: &u32 = &1;` refers to the temp local that stores the `1` - None | - Some(&MemoryKind::Stack) => {}, - Some(&MemoryKind::Machine(m)) => M::mark_static_initialized(m)?, - Some(&MemoryKind::MutableStatic) => { - trace!("mark_static_initalized: skipping already initialized static referred to by static currently being initialized"); - return Ok(()); - }, - } - // overwrite or insert - self.alloc_kind.insert(alloc_id, MemoryKind::MutableStatic); - // take out the relocations vector to free the borrow on self, so we can call - // mark recursively - mem::replace(relocations, Default::default()) + let uninit = self.uninitialized_statics.remove(&alloc_id); + if let Some(mut alloc) = alloc.or(uninit) { + // ensure llvm knows not to put this into immutable memroy + alloc.mutable = mutability == Mutability::Mutable; + let alloc = self.tcx.intern_const_alloc(alloc); + self.tcx.interpret_interner.borrow_mut().intern_at_reserved(alloc_id, alloc); + // recurse into inner allocations + for &alloc in alloc.relocations.values() { + self.mark_inner_allocation_initialized(alloc, mutability)?; } - None => return err!(DanglingPointerDeref), - }; - // recurse into inner allocations - for &alloc in relocations.values() { - self.mark_inner_allocation_initialized(alloc, mutability)?; } - // put back the relocations - self.alloc_map - .get_mut(&alloc_id) - .expect("checked above") - .relocations = relocations; Ok(()) } @@ -829,7 +782,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { } /// Relocations -impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { fn relocations( &self, ptr: MemoryPointer, @@ -883,7 +836,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { } /// Undefined bytes -impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { // FIXME(solson): This is a very naive, slow version. fn copy_undef_mask( &mut self, @@ -944,7 +897,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { // Methods to access integers in the target endianess //////////////////////////////////////////////////////////////////////////////// -fn write_target_uint( +pub fn write_target_uint( endianess: layout::Endian, mut target: &mut [u8], data: u128, @@ -955,7 +908,8 @@ fn write_target_uint( layout::Endian::Big => target.write_uint128::(data, len), } } -fn write_target_int( + +pub fn write_target_int( endianess: layout::Endian, mut target: &mut [u8], data: i128, @@ -967,14 +921,14 @@ fn write_target_int( } } -fn read_target_uint(endianess: layout::Endian, mut source: &[u8]) -> Result { +pub fn read_target_uint(endianess: layout::Endian, mut source: &[u8]) -> Result { match endianess { layout::Endian::Little => source.read_uint128::(source.len()), layout::Endian::Big => source.read_uint128::(source.len()), } } -fn read_target_int(endianess: layout::Endian, mut source: &[u8]) -> Result { +pub fn read_target_int(endianess: layout::Endian, mut source: &[u8]) -> Result { match endianess { layout::Endian::Little => source.read_int128::(source.len()), layout::Endian::Big => source.read_int128::(source.len()), @@ -985,9 +939,9 @@ fn read_target_int(endianess: layout::Endian, mut source: &[u8]) -> Result> { - fn memory_mut(&mut self) -> &mut Memory<'a, 'tcx, M>; - fn memory(&self) -> &Memory<'a, 'tcx, M>; +pub trait HasMemory<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { + fn memory_mut(&mut self) -> &mut Memory<'a, 'mir, 'tcx, M>; + fn memory(&self) -> &Memory<'a, 'mir, 'tcx, M>; /// Convert the value into a pointer (or a pointer-sized integer). If the value is a ByRef, /// this may have to perform a load. @@ -1051,31 +1005,31 @@ pub trait HasMemory<'a, 'tcx: 'a, M: Machine<'tcx>> { } } -impl<'a, 'tcx, M: Machine<'tcx>> HasMemory<'a, 'tcx, M> for Memory<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasMemory<'a, 'mir, 'tcx, M> for Memory<'a, 'mir, 'tcx, M> { #[inline] - fn memory_mut(&mut self) -> &mut Memory<'a, 'tcx, M> { + fn memory_mut(&mut self) -> &mut Memory<'a, 'mir, 'tcx, M> { self } #[inline] - fn memory(&self) -> &Memory<'a, 'tcx, M> { + fn memory(&self) -> &Memory<'a, 'mir, 'tcx, M> { self } } -impl<'a, 'tcx, M: Machine<'tcx>> HasMemory<'a, 'tcx, M> for EvalContext<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasMemory<'a, 'mir, 'tcx, M> for EvalContext<'a, 'mir, 'tcx, M> { #[inline] - fn memory_mut(&mut self) -> &mut Memory<'a, 'tcx, M> { + fn memory_mut(&mut self) -> &mut Memory<'a, 'mir, 'tcx, M> { &mut self.memory } #[inline] - fn memory(&self) -> &Memory<'a, 'tcx, M> { + fn memory(&self) -> &Memory<'a, 'mir, 'tcx, M> { &self.memory } } -impl<'a, 'tcx, M: Machine<'tcx>> layout::HasDataLayout for &'a Memory<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> layout::HasDataLayout for &'a Memory<'a, 'mir, 'tcx, M> { #[inline] fn data_layout(&self) -> &TargetDataLayout { &self.tcx.data_layout diff --git a/src/librustc_mir/interpret/mod.rs b/src/librustc_mir/interpret/mod.rs index fee62c8a82e2f..f23ba90fd4c3f 100644 --- a/src/librustc_mir/interpret/mod.rs +++ b/src/librustc_mir/interpret/mod.rs @@ -18,6 +18,17 @@ pub use self::place::{Place, PlaceExtra}; pub use self::memory::{Memory, MemoryKind, HasMemory}; -pub use self::const_eval::{eval_body_as_integer, eval_body, CompileTimeEvaluator, const_eval_provider}; +pub use self::const_eval::{ + eval_body_with_mir, + mk_borrowck_eval_cx, + eval_body, + CompileTimeEvaluator, + const_eval_provider, + const_val_field, + const_discr, +}; pub use self::machine::Machine; + +pub use self::operator::unary_op; +pub use self::memory::{write_target_uint, write_target_int, read_target_uint, read_target_int}; diff --git a/src/librustc_mir/interpret/operator.rs b/src/librustc_mir/interpret/operator.rs index 6ab1aec38b863..bad744194d5e3 100644 --- a/src/librustc_mir/interpret/operator.rs +++ b/src/librustc_mir/interpret/operator.rs @@ -8,7 +8,7 @@ use super::{EvalContext, Place, Machine, ValTy}; use rustc::mir::interpret::{EvalResult, PrimVal, PrimValKind, Value, bytes_to_f32, bytes_to_f64}; -impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { fn binop_with_overflow( &mut self, op: mir::BinOp, @@ -56,6 +56,24 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } macro_rules! overflow { + (overflowing_div, $l:expr, $r:expr) => ({ + let (val, overflowed) = if $r == 0 { + ($l, true) + } else { + $l.overflowing_div($r) + }; + let primval = PrimVal::Bytes(val as u128); + Ok((primval, overflowed)) + }); + (overflowing_rem, $l:expr, $r:expr) => ({ + let (val, overflowed) = if $r == 0 { + ($l, true) + } else { + $l.overflowing_rem($r) + }; + let primval = PrimVal::Bytes(val as u128); + Ok((primval, overflowed)) + }); ($op:ident, $l:expr, $r:expr) => ({ let (val, overflowed) = $l.$op($r); let primval = PrimVal::Bytes(val as u128); @@ -105,7 +123,7 @@ macro_rules! int_shift { }) } -impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { /// Returns the result of the specified operation and whether it overflowed. pub fn binary_op( &self, @@ -248,10 +266,15 @@ pub fn unary_op<'tcx>( (Not, I64) => !(bytes as i64) as u128, (Not, I128) => !(bytes as i128) as u128, + (Neg, I8) if bytes == i8::min_value() as u128 => return err!(OverflowingMath), (Neg, I8) => -(bytes as i8) as u128, + (Neg, I16) if bytes == i16::min_value() as u128 => return err!(OverflowingMath), (Neg, I16) => -(bytes as i16) as u128, + (Neg, I32) if bytes == i32::min_value() as u128 => return err!(OverflowingMath), (Neg, I32) => -(bytes as i32) as u128, + (Neg, I64) if bytes == i64::min_value() as u128 => return err!(OverflowingMath), (Neg, I64) => -(bytes as i64) as u128, + (Neg, I128) if bytes == i128::min_value() as u128 => return err!(OverflowingMath), (Neg, I128) => -(bytes as i128) as u128, (Neg, F32) => (-bytes_to_f32(bytes)).bits, diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index c2c6b23c083a2..67801c4e1d481 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -1,11 +1,13 @@ use rustc::mir; use rustc::ty::{self, Ty}; use rustc::ty::layout::{self, Align, LayoutOf, TyLayout}; +use rustc::traits; use rustc_data_structures::indexed_vec::Idx; use rustc::mir::interpret::{GlobalId, Value, PrimVal, EvalResult, Pointer, MemoryPointer}; use super::{EvalContext, Machine, ValTy}; use interpret::memory::HasMemory; +use rustc::middle::const_val::ErrKind; #[derive(Copy, Clone, Debug)] pub enum Place { @@ -71,7 +73,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 +92,7 @@ impl<'tcx> Place { } } -impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { /// Reads a value from the place without going through the intermediate step of obtaining /// a `miri::Place` pub fn try_read_place( @@ -106,15 +108,42 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { // Directly reading a static will always succeed Static(ref static_) => { let instance = ty::Instance::mono(self.tcx, static_.def_id); - Ok(Some(self.read_global_as_value(GlobalId { + self.read_global_as_value(GlobalId { instance, promoted: None, - }, self.layout_of(self.place_ty(place))?))) + }, self.place_ty(place)).map(Some) } Projection(ref proj) => self.try_read_place_projection(proj), } } + pub fn read_field( + &self, + base: Value, + variant: Option, + field: mir::Field, + base_ty: Ty<'tcx>, + ) -> EvalResult<'tcx, Option<(Value, Ty<'tcx>)>> { + let mut base_layout = self.layout_of(base_ty)?; + if let Some(variant_index) = variant { + base_layout = base_layout.for_variant(self, variant_index); + } + let field_index = field.index(); + let field = base_layout.field(self, field_index)?; + 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 +155,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 +201,44 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { }, Static(ref static_) => { - let instance = ty::Instance::mono(self.tcx, static_.def_id); - let gid = GlobalId { - instance, - promoted: None, - }; + let alloc = self + .tcx + .interpret_interner + .borrow() + .get_cached(static_.def_id); let layout = self.layout_of(self.place_ty(mir_place))?; - let alloc = self.tcx.interpret_interner.borrow().get_cached(gid).expect("uncached global"); - Place::Ptr { - ptr: MemoryPointer::new(alloc, 0).into(), - align: layout.align, - extra: PlaceExtra::None, + if let Some(alloc) = alloc { + Place::Ptr { + ptr: MemoryPointer::new(alloc, 0).into(), + align: layout.align, + extra: PlaceExtra::None, + } + } else { + let instance = ty::Instance::mono(self.tcx, static_.def_id); + let cid = GlobalId { + instance, + promoted: None + }; + let param_env = ty::ParamEnv::empty(traits::Reveal::All); + // ensure the static is computed + if let Err(err) = self.tcx.const_eval(param_env.and(cid)) { + match err.kind { + ErrKind::Miri(miri) => return Err(miri), + ErrKind::TypeckError => return err!(TypeckError), + other => bug!("const eval returned {:?}", other), + } + }; + let alloc = self + .tcx + .interpret_interner + .borrow() + .get_cached(static_.def_id) + .expect("uncached static"); + Place::Ptr { + ptr: MemoryPointer::new(alloc, 0).into(), + align: layout.align, + extra: PlaceExtra::None, + } } } diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs index 2b0f9041d5115..21e81ff668ea5 100644 --- a/src/librustc_mir/interpret/step.rs +++ b/src/librustc_mir/interpret/step.rs @@ -2,21 +2,12 @@ //! //! The main entry point is the `step` method. -use rustc::hir; -use rustc::mir::visit::{Visitor, PlaceContext}; use rustc::mir; -use rustc::ty::{self, Instance}; -use rustc::ty::layout::LayoutOf; -use rustc::middle::const_val::ConstVal; -use rustc::mir::interpret::GlobalId; -use rustc::mir::interpret::{EvalResult, EvalErrorKind}; -use super::{EvalContext, StackPopCleanup, Place, Machine}; +use rustc::mir::interpret::EvalResult; +use super::{EvalContext, Machine}; -use syntax::codemap::Span; -use syntax::ast::Mutability; - -impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { pub fn inc_step_counter_and_check_limit(&mut self, n: u64) -> EvalResult<'tcx> { self.steps_remaining = self.steps_remaining.saturating_sub(n); if self.steps_remaining > 0 { @@ -41,52 +32,14 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { let old_frames = self.cur_frame(); if let Some(stmt) = basic_block.statements.get(stmt_id) { - let mut new = Ok(false); - ConstantExtractor { - span: stmt.source_info.span, - instance: self.frame().instance, - ecx: self, - mir, - new_constant: &mut new, - }.visit_statement( - block, - stmt, - mir::Location { - block, - statement_index: stmt_id, - }, - ); - // if ConstantExtractor added a new frame, we don't execute anything here - // but await the next call to step - if !new? { - assert_eq!(old_frames, self.cur_frame()); - self.statement(stmt)?; - } + assert_eq!(old_frames, self.cur_frame()); + self.statement(stmt)?; return Ok(true); } let terminator = basic_block.terminator(); - let mut new = Ok(false); - ConstantExtractor { - span: terminator.source_info.span, - instance: self.frame().instance, - ecx: self, - mir, - new_constant: &mut new, - }.visit_terminator( - block, - terminator, - mir::Location { - block, - statement_index: stmt_id, - }, - ); - // if ConstantExtractor added a new frame, we don't execute anything here - // but await the next call to step - if !new? { - assert_eq!(old_frames, self.cur_frame()); - self.terminator(terminator)?; - } + assert_eq!(old_frames, self.cur_frame()); + self.terminator(terminator)?; Ok(true) } @@ -152,184 +105,4 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } Ok(()) } - - /// returns `true` if a stackframe was pushed - fn global_item( - &mut self, - instance: Instance<'tcx>, - span: Span, - mutability: Mutability, - ) -> EvalResult<'tcx, bool> { - debug!("global_item: {:?}", instance); - let cid = GlobalId { - instance, - promoted: None, - }; - if self.tcx.interpret_interner.borrow().get_cached(cid).is_some() { - return Ok(false); - } - if self.tcx.has_attr(instance.def_id(), "linkage") { - M::global_item_with_linkage(self, cid.instance, mutability)?; - return Ok(false); - } - let instance_ty = instance.ty(self.tcx); - let layout = self.layout_of(instance_ty)?; - assert!(!layout.is_unsized()); - let ptr = self.memory.allocate( - layout.size.bytes(), - layout.align, - None, - )?; - self.tcx.interpret_interner.borrow_mut().cache(cid, ptr.alloc_id); - let internally_mutable = !layout.ty.is_freeze(self.tcx, self.param_env, span); - let mutability = if mutability == Mutability::Mutable || internally_mutable { - Mutability::Mutable - } else { - Mutability::Immutable - }; - let cleanup = StackPopCleanup::MarkStatic(mutability); - let name = ty::tls::with(|tcx| tcx.item_path_str(instance.def_id())); - trace!("pushing stack frame for global: {}", name); - let mir = self.load_mir(instance.def)?; - self.push_stack_frame( - instance, - span, - mir, - Place::from_ptr(ptr, layout.align), - cleanup, - )?; - Ok(true) - } -} - -struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b, M: Machine<'tcx> + 'a> { - span: Span, - ecx: &'a mut EvalContext<'b, 'tcx, M>, - mir: &'tcx mir::Mir<'tcx>, - instance: ty::Instance<'tcx>, - // Whether a stackframe for a new constant has been pushed - new_constant: &'a mut EvalResult<'tcx, bool>, -} - -impl<'a, 'b, 'tcx, M: Machine<'tcx>> ConstantExtractor<'a, 'b, 'tcx, M> { - fn try EvalResult<'tcx, bool>>(&mut self, f: F) { - match *self.new_constant { - // already computed a constant, don't do more than one per iteration - Ok(true) => {}, - // no constants computed yet - Ok(false) => *self.new_constant = f(self), - // error happened, abort the visitor traversing - Err(_) => {}, - } - } -} - -impl<'a, 'b, 'tcx, M: Machine<'tcx>> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx, M> { - fn visit_constant(&mut self, constant: &mir::Constant<'tcx>, location: mir::Location) { - self.super_constant(constant, location); - self.try(|this| { - match constant.literal { - // already computed by rustc - mir::Literal::Value { value: &ty::Const { val: ConstVal::Unevaluated(def_id, substs), .. } } => { - debug!("global_item: {:?}, {:#?}", def_id, substs); - let substs = this.ecx.tcx.trans_apply_param_substs(this.instance.substs, &substs); - debug!("global_item_new_substs: {:#?}", substs); - debug!("global_item_param_env: {:#?}", this.ecx.param_env); - let instance = Instance::resolve( - this.ecx.tcx, - this.ecx.param_env, - def_id, - substs, - ).ok_or(EvalErrorKind::TypeckError)?; // turn error prop into a panic to expose associated type in const issue - this.ecx.global_item( - instance, - constant.span, - Mutability::Immutable, - ) - } - mir::Literal::Value { .. } => Ok(false), - mir::Literal::Promoted { index } => { - let cid = GlobalId { - instance: this.instance, - promoted: Some(index), - }; - if this.ecx.tcx.interpret_interner.borrow().get_cached(cid).is_some() { - return Ok(false); - } - let mir = &this.mir.promoted[index]; - let ty = this.ecx.monomorphize(mir.return_ty(), this.instance.substs); - let layout = this.ecx.layout_of(ty)?; - assert!(!layout.is_unsized()); - let ptr = this.ecx.memory.allocate( - layout.size.bytes(), - layout.align, - None, - )?; - this.ecx.tcx.interpret_interner.borrow_mut().cache(cid, ptr.alloc_id); - trace!("pushing stack frame for {:?}", index); - this.ecx.push_stack_frame( - this.instance, - constant.span, - mir, - Place::from_ptr(ptr, layout.align), - StackPopCleanup::MarkStatic(Mutability::Immutable), - )?; - Ok(true) - } - } - }); - } - - fn visit_place( - &mut self, - place: &mir::Place<'tcx>, - context: PlaceContext<'tcx>, - location: mir::Location, - ) { - self.super_place(place, context, location); - self.try(|this| { - if let mir::Place::Static(ref static_) = *place { - let def_id = static_.def_id; - let span = this.span; - if let Some(node_item) = this.ecx.tcx.hir.get_if_local(def_id) { - if let hir::map::Node::NodeItem(&hir::Item { ref node, .. }) = node_item { - if let hir::ItemStatic(_, m, _) = *node { - let instance = Instance::mono(this.ecx.tcx, def_id); - this.ecx.global_item( - instance, - span, - if m == hir::MutMutable { - Mutability::Mutable - } else { - Mutability::Immutable - }, - ) - } else { - bug!("static def id doesn't point to static"); - } - } else { - bug!("static def id doesn't point to item"); - } - } else { - let def = this.ecx.tcx.describe_def(def_id).expect("static not found"); - if let hir::def::Def::Static(_, mutable) = def { - let instance = Instance::mono(this.ecx.tcx, def_id); - this.ecx.global_item( - instance, - span, - if mutable { - Mutability::Mutable - } else { - Mutability::Immutable - }, - ) - } else { - bug!("static found but isn't a static: {:?}", def); - } - } - } else { - Ok(false) - } - }); - } } diff --git a/src/librustc_mir/interpret/terminator/drop.rs b/src/librustc_mir/interpret/terminator/drop.rs index c5942712b87dd..fbc0c499e59de 100644 --- a/src/librustc_mir/interpret/terminator/drop.rs +++ b/src/librustc_mir/interpret/terminator/drop.rs @@ -5,7 +5,7 @@ use syntax::codemap::Span; use rustc::mir::interpret::{EvalResult, PrimVal, Value}; use interpret::{Machine, ValTy, EvalContext, Place, PlaceExtra}; -impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { pub(crate) fn drop_place( &mut self, place: Place, diff --git a/src/librustc_mir/interpret/terminator/mod.rs b/src/librustc_mir/interpret/terminator/mod.rs index c8a0dbdd90308..783755ea553ce 100644 --- a/src/librustc_mir/interpret/terminator/mod.rs +++ b/src/librustc_mir/interpret/terminator/mod.rs @@ -13,7 +13,7 @@ use interpret::memory::HasMemory; mod drop; -impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { pub fn goto_block(&mut self, target: mir::BasicBlock) { self.frame_mut().block = target; self.frame_mut().stmt = 0; @@ -45,8 +45,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { // Branch to the `otherwise` case by default, if no match is found. let mut target_block = targets[targets.len() - 1]; - for (index, const_int) in values.iter().enumerate() { - let prim = PrimVal::Bytes(const_int.to_u128_unchecked()); + for (index, &const_int) in values.iter().enumerate() { + let prim = PrimVal::Bytes(const_int); if discr_prim.to_bytes()? == prim.to_bytes()? { target_block = targets[index]; break; diff --git a/src/librustc_mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs index 22417201f0dc5..4dc0879c85d52 100644 --- a/src/librustc_mir/interpret/traits.rs +++ b/src/librustc_mir/interpret/traits.rs @@ -6,7 +6,7 @@ use rustc::mir::interpret::{PrimVal, Value, MemoryPointer, EvalResult}; use super::{EvalContext, eval_context, Machine}; -impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { /// Creates a dynamic vtable for the given type and vtable origin. This is used only for /// objects. /// @@ -54,7 +54,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { self.memory.mark_static_initalized( vtable.alloc_id, - Mutability::Mutable, + Mutability::Immutable, )?; Ok(vtable) diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 5379bf3f5a7ae..58cfc50744701 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 transform; pub mod util; pub mod interpret; pub mod monomorphize; +pub mod const_eval; use rustc::ty::maps::Providers; @@ -78,6 +81,7 @@ pub fn provide(providers: &mut Providers) { shim::provide(providers); transform::provide(providers); providers.const_eval = interpret::const_eval_provider; + providers.check_match = const_eval::check_match::check_match; } #[cfg(not(stage0))] // remove after the next snapshot diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index f16187797d4e5..3a665ba4f6c07 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -194,6 +194,7 @@ use rustc::hir::itemlikevisit::ItemLikeVisitor; use rustc::hir::map as hir_map; use rustc::hir::def_id::DefId; use rustc::middle::const_val::ConstVal; +use rustc::mir::interpret::{Value, PrimVal, AllocId, Pointer}; use rustc::middle::lang_items::{ExchangeMallocFnLangItem, StartFnLangItem}; use rustc::traits; use rustc::ty::subst::{Substs, Kind}; @@ -571,14 +572,26 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, location: Location) { debug!("visiting const {:?} @ {:?}", *constant, location); - if let ConstVal::Unevaluated(def_id, substs) = constant.val { - let substs = self.tcx.trans_apply_param_substs(self.param_substs, - &substs); - let instance = ty::Instance::resolve(self.tcx, - ty::ParamEnv::empty(traits::Reveal::All), - def_id, - substs).unwrap(); - collect_neighbours(self.tcx, instance, true, self.output); + match constant.val { + ConstVal::Unevaluated(def_id, substs) => { + let substs = self.tcx.trans_apply_param_substs(self.param_substs, + &substs); + let instance = ty::Instance::resolve(self.tcx, + ty::ParamEnv::empty(traits::Reveal::All), + def_id, + substs).unwrap(); + collect_neighbours(self.tcx, instance, true, self.output); + }, + ConstVal::Value(Value::ByValPair(PrimVal::Ptr(a), PrimVal::Ptr(b))) => { + collect_miri(self.tcx, a.alloc_id, self.output); + collect_miri(self.tcx, b.alloc_id, self.output); + } + ConstVal::Value(Value::ByValPair(_, PrimVal::Ptr(ptr))) | + ConstVal::Value(Value::ByValPair(PrimVal::Ptr(ptr), _)) | + ConstVal::Value(Value::ByVal(PrimVal::Ptr(ptr))) | + ConstVal::Value(Value::ByRef(Pointer { primval: PrimVal::Ptr(ptr) }, _)) => + collect_miri(self.tcx, ptr.alloc_id, self.output), + _ => {}, } self.super_const(constant); @@ -1101,6 +1114,28 @@ fn create_mono_items_for_default_impls<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } +/// Scan the miri alloc in order to find function calls, closures, and drop-glue +fn collect_miri<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + alloc_id: AllocId, + output: &mut Vec>, +) { + let interpret_interner = tcx.interpret_interner.borrow(); + if let Some(alloc) = interpret_interner.get_alloc(alloc_id) { + trace!("collecting {:?} with {:#?}", alloc_id, alloc); + for &inner in alloc.relocations.values() { + collect_miri(tcx, inner, output); + } + } else if let Some(fn_instance) = interpret_interner.get_fn(alloc_id) { + if should_monomorphize_locally(tcx, &fn_instance) { + trace!("collecting {:?} with {:#?}", alloc_id, fn_instance); + output.push(create_fn_mono_item(fn_instance)); + } + } else { + bug!("alloc id without corresponding allocation: {}", alloc_id); + } +} + /// Scan the MIR in order to find function calls, closures, and drop-glue fn collect_neighbours<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>, diff --git a/src/librustc_mir/monomorphize/item.rs b/src/librustc_mir/monomorphize/item.rs index 024334f1c849c..4f177943d542d 100644 --- a/src/librustc_mir/monomorphize/item.rs +++ b/src/librustc_mir/monomorphize/item.rs @@ -341,7 +341,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 c206d0ea9b5fd..9a3b707360689 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -16,7 +16,8 @@ use rustc::mir::*; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::subst::{Kind, Subst, Substs}; use rustc::ty::maps::Providers; -use rustc_const_math::{ConstInt, ConstUsize}; +use rustc_const_math::ConstUsize; +use rustc::mir::interpret::{Value, PrimVal}; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; @@ -300,7 +301,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(ty, len) } ty::TyClosure(def_id, substs) => { @@ -425,7 +426,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 }), }, @@ -487,13 +489,18 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { } fn make_usize(&self, value: u64) -> Box> { - let value = ConstUsize::new(value, self.tcx.sess.target.usize_ty).unwrap(); box Constant { span: self.span, ty: self.tcx.types.usize, literal: Literal::Value { value: self.tcx.mk_const(ty::Const { - val: ConstVal::Integral(ConstInt::Usize(value)), + val: { + let value = ConstUsize::new( + value, + self.tcx.sess.target.usize_ty, + ).unwrap().as_u64(); + ConstVal::Value(Value::ByVal(PrimVal::Bytes(value.into()))) + }, ty: self.tcx.types.usize, }) } @@ -741,8 +748,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 e9ba5de3cc694..c57325d9324df 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -204,7 +204,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/elaborate_drops.rs b/src/librustc_mir/transform/elaborate_drops.rs index 106bc39d0fc5b..46670df8422ef 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 9734edbe193c1..9dec25e60a50b 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::{Kind, 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; @@ -79,6 +78,7 @@ use transform::{MirPass, MirSource}; use transform::simplify; use transform::no_landing_pads::no_landing_pads; use dataflow::{do_dataflow, DebugFormatted, MaybeStorageLive, state_for_location}; +use rustc::mir::interpret::{Value, PrimVal}; pub struct StateTransform; @@ -180,7 +180,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 }), }, @@ -454,7 +454,7 @@ fn insert_switch<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let switch = TerminatorKind::SwitchInt { discr: Operand::Copy(transform.make_field(transform.state_field, tcx.types.u32)), switch_ty: tcx.types.u32, - values: Cow::from(cases.iter().map(|&(i, _)| ConstInt::U32(i)).collect::>()), + values: Cow::from(cases.iter().map(|&(i, _)| i.into()).collect::>()), targets: cases.iter().map(|&(_, d)| d).chain(once(default_block)).collect(), }; @@ -618,7 +618,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 43ee75d1e2ba2..25470147c7350 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 attrs = tcx.get_attrs(callsite.callee); let hint = attr::find_inline_attr(None, &attrs[..]); diff --git a/src/librustc_mir/transform/instcombine.rs b/src/librustc_mir/transform/instcombine.rs index 8856d263864cd..6079e1929b578 100644 --- a/src/librustc_mir/transform/instcombine.rs +++ b/src/librustc_mir/transform/instcombine.rs @@ -11,44 +11,52 @@ //! Performs various peephole optimizations. use rustc::mir::{Constant, Literal, Location, Place, Mir, Operand, ProjectionElem, Rvalue, Local}; -use rustc::mir::visit::{MutVisitor, Visitor}; -use rustc::ty::{TyCtxt, TypeVariants}; +use rustc::mir::{NullOp, StatementKind, Statement, BasicBlock}; +use rustc::mir::{SourceInfo, ARGUMENT_VISIBILITY_SCOPE, TerminatorKind}; +use rustc::mir::visit::{MutVisitor, Visitor, TyContext}; +use rustc::middle::const_val::ConstVal; +use rustc::ty::{TyCtxt, TypeVariants, self, Instance}; +use rustc::mir::interpret::{Value, PrimVal, GlobalId}; +use interpret::{eval_body_with_mir, eval_body, mk_borrowck_eval_cx, unary_op, ValTy}; use rustc::util::nodemap::{FxHashMap, FxHashSet}; use rustc_data_structures::indexed_vec::Idx; use std::mem; +use std::collections::VecDeque; use transform::{MirPass, MirSource}; +use syntax::codemap::Span; +use rustc_data_structures::control_flow_graph::ControlFlowGraph; +use rustc::ty::subst::Substs; pub struct InstCombine; impl MirPass for InstCombine { fn run_pass<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, - _: MirSource, + source: MirSource, mir: &mut Mir<'tcx>) { - // We only run when optimizing MIR (at any level). - if tcx.sess.opts.debugging_opts.mir_opt_level == 0 { - return - } // First, find optimization opportunities. This is done in a pre-pass to keep the MIR // read-only so that we can do global analyses on the MIR in the process (e.g. // `Place::ty()`). let optimizations = { - let mut optimization_finder = OptimizationFinder::new(mir, tcx); + let mut optimization_finder = OptimizationFinder::new(mir, tcx, source); optimization_finder.visit_mir(mir); optimization_finder.optimizations }; // Then carry out those optimizations. - MutVisitor::visit_mir(&mut InstCombineVisitor { optimizations }, mir); + MutVisitor::visit_mir(&mut InstCombineVisitor { optimizations, tcx }, mir); } } -pub struct InstCombineVisitor<'tcx> { +type Const<'tcx> = (Value, ty::Ty<'tcx>, Span); + +pub struct InstCombineVisitor<'a, 'tcx: 'a> { optimizations: OptimizationList<'tcx>, + tcx: TyCtxt<'a, 'tcx, 'tcx>, } -impl<'tcx> MutVisitor<'tcx> for InstCombineVisitor<'tcx> { +impl<'a, 'tcx> MutVisitor<'tcx> for InstCombineVisitor<'a, 'tcx> { fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) { if self.optimizations.and_stars.remove(&location) { debug!("Replacing `&*`: {:?}", rvalue); @@ -67,28 +75,459 @@ impl<'tcx> MutVisitor<'tcx> for InstCombineVisitor<'tcx> { *rvalue = Rvalue::Use(Operand::Constant(box constant)); } + if let Some((value, ty, span)) = self.optimizations.const_prop.remove(&location) { + let value = self.tcx.mk_const(ty::Const { + val: ConstVal::Value(value), + ty, + }); + debug!("Replacing `{:?}` with {:?}", rvalue, value); + let constant = Constant { + ty, + literal: Literal::Value { value }, + span, + }; + *rvalue = Rvalue::Use(Operand::Constant(box constant)); + } + self.super_rvalue(rvalue, location) } + + fn visit_constant( + &mut self, + constant: &mut Constant<'tcx>, + location: Location, + ) { + self.super_constant(constant, location); + if let Some(&(val, ty, _)) = self.optimizations.constants.get(constant) { + constant.literal = Literal::Value { + value: self.tcx.mk_const(ty::Const { + val: ConstVal::Value(val), + ty, + }), + }; + } + } + + fn visit_operand( + &mut self, + operand: &mut Operand<'tcx>, + location: Location, + ) { + self.super_operand(operand, location); + let new = match operand { + Operand::Move(Place::Local(local)) | + Operand::Copy(Place::Local(local)) => { + trace!("trying to read {:?}", local); + self.optimizations.places.get(&local).cloned() + }, + _ => return, + }; + if let Some((value, ty, span)) = new { + let value = self.tcx.mk_const(ty::Const { + val: ConstVal::Value(value), + ty, + }); + debug!("Replacing `{:?}` with {:?}", operand, value); + let constant = Constant { + ty, + literal: Literal::Value { value }, + span, + }; + *operand = Operand::Constant(box constant); + } + } + + fn visit_terminator_kind( + &mut self, + block: BasicBlock, + kind: &mut TerminatorKind<'tcx>, + location: Location, + ) { + match kind { + TerminatorKind::SwitchInt { discr: value, .. } | + TerminatorKind::Yield { value, .. } | + TerminatorKind::Assert { cond: value, .. } => { + if let Some((new, ty, span)) = self.optimizations.terminators.remove(&block) { + let new = self.tcx.mk_const(ty::Const { + val: ConstVal::Value(new), + ty, + }); + debug!("Replacing `{:?}` with {:?}", value, new); + let constant = Constant { + ty, + literal: Literal::Value { value: new }, + span, + }; + *value = Operand::Constant(box constant); + } + } + // FIXME: do this optimization for function calls + _ => {}, + } + self.super_terminator_kind(block, kind, location) + } } /// Finds optimization opportunities on the MIR. struct OptimizationFinder<'b, 'a, 'tcx:'a+'b> { mir: &'b Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>, + source: MirSource, optimizations: OptimizationList<'tcx>, } impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { - fn new(mir: &'b Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> OptimizationFinder<'b, 'a, 'tcx> { + fn new( + mir: &'b Mir<'tcx>, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + source: MirSource, + ) -> OptimizationFinder<'b, 'a, 'tcx> { OptimizationFinder { mir, tcx, + source, optimizations: OptimizationList::default(), } } + + fn eval_constant(&mut self, c: &Constant<'tcx>) -> Option> { + if let Some(&val) = self.optimizations.constants.get(c) { + return Some(val); + } + match c.literal { + Literal::Value { value } => match value.val { + ConstVal::Value(v) => Some((v, value.ty, c.span)), + ConstVal::Unevaluated(did, substs) => { + let param_env = self.tcx.param_env(self.source.def_id); + let span = self.tcx.def_span(did); + let instance = Instance::resolve( + self.tcx, + param_env, + did, + substs, + )?; + let cid = GlobalId { + instance, + promoted: None, + }; + let (value, _, ty) = eval_body(self.tcx, cid, param_env)?; + let val = (value, ty, span); + trace!("evaluated {:?} to {:?}", c, val); + self.optimizations.constants.insert(c.clone(), val); + Some(val) + }, + }, + // evaluate the promoted and replace the constant with the evaluated result + Literal::Promoted { index } => { + let generics = self.tcx.generics_of(self.source.def_id); + if generics.parent_types as usize + generics.types.len() > 0 { + // FIXME: can't handle code with generics + return None; + } + let substs = Substs::identity_for_item(self.tcx, self.source.def_id); + let instance = Instance::new(self.source.def_id, substs); + let cid = GlobalId { + instance, + promoted: Some(index), + }; + let param_env = self.tcx.param_env(self.source.def_id); + let (value, _, ty) = eval_body_with_mir(self.tcx, cid, self.mir, param_env)?; + let val = (value, ty, c.span); + trace!("evaluated {:?} to {:?}", c, val); + self.optimizations.constants.insert(c.clone(), val); + Some(val) + } + } + } + + fn eval_operand(&mut self, op: &Operand<'tcx>) -> Option> { + match *op { + Operand::Constant(ref c) => self.eval_constant(c), + Operand::Move(ref place) | Operand::Copy(ref place) => match *place { + Place::Local(loc) => self.optimizations.places.get(&loc).cloned(), + // FIXME(oli-obk): field and index projections + Place::Projection(_) => None, + _ => None, + }, + } + } + + fn simplify_operand(&mut self, op: &Operand<'tcx>) -> Option> { + match *op { + Operand::Constant(ref c) => match c.literal { + Literal::Value { .. } => None, + _ => self.eval_operand(op), + }, + _ => self.eval_operand(op), + } + } + + fn const_prop( + &mut self, + rvalue: &Rvalue<'tcx>, + place_ty: ty::Ty<'tcx>, + span: Span, + ) -> Option> { + match *rvalue { + Rvalue::Use(ref op) => self.simplify_operand(op), + Rvalue::Repeat(..) | + Rvalue::Ref(..) | + Rvalue::Cast(..) | + Rvalue::Aggregate(..) | + Rvalue::NullaryOp(NullOp::Box, _) | + Rvalue::Discriminant(..) => None, + // FIXME(oli-obk): evaluate static/constant slice lengths + Rvalue::Len(_) => None, + Rvalue::NullaryOp(NullOp::SizeOf, ty) => { + let param_env = self.tcx.param_env(self.source.def_id); + type_size_of(self.tcx, param_env, ty).map(|n| ( + Value::ByVal(PrimVal::Bytes(n as u128)), + self.tcx.types.usize, + span, + )) + } + Rvalue::UnaryOp(op, ref arg) => { + let def_id = if self.tcx.is_closure(self.source.def_id) { + self.tcx.closure_base_def_id(self.source.def_id) + } else { + self.source.def_id + }; + let generics = self.tcx.generics_of(def_id); + if generics.parent_types as usize + generics.types.len() > 0 { + // FIXME: can't handle code with generics + return None; + } + let substs = Substs::identity_for_item(self.tcx, self.source.def_id); + let instance = Instance::new(self.source.def_id, substs); + let ecx = mk_borrowck_eval_cx(self.tcx, instance, self.mir, span).unwrap(); + + let val = self.eval_operand(arg)?; + let prim = ecx.value_to_primval(ValTy { value: val.0, ty: val.1 }).ok()?; + let kind = ecx.ty_to_primval_kind(val.1).ok()?; + match unary_op(op, prim, kind) { + Ok(val) => Some((Value::ByVal(val), place_ty, span)), + Err(mut err) => { + ecx.report(&mut err, false, Some(span)); + None + }, + } + } + Rvalue::CheckedBinaryOp(op, ref left, ref right) | + Rvalue::BinaryOp(op, ref left, ref right) => { + trace!("rvalue binop {:?} for {:?} and {:?}", op, left, right); + let left = self.eval_operand(left)?; + let right = self.eval_operand(right)?; + let def_id = if self.tcx.is_closure(self.source.def_id) { + self.tcx.closure_base_def_id(self.source.def_id) + } else { + self.source.def_id + }; + let generics = self.tcx.generics_of(def_id); + let has_generics = generics.parent_types as usize + generics.types.len() > 0; + if has_generics { + // FIXME: can't handle code with generics + return None; + } + let substs = Substs::identity_for_item(self.tcx, self.source.def_id); + let instance = Instance::new(self.source.def_id, substs); + let ecx = mk_borrowck_eval_cx(self.tcx, instance, self.mir, span).unwrap(); + + let l = ecx.value_to_primval(ValTy { value: left.0, ty: left.1 }).ok()?; + let r = ecx.value_to_primval(ValTy { value: right.0, ty: right.1 }).ok()?; + trace!("const evaluating {:?} for {:?} and {:?}", op, left, right); + match ecx.binary_op(op, l, left.1, r, right.1) { + Ok((val, overflow)) => { + let val = if let Rvalue::CheckedBinaryOp(..) = *rvalue { + Value::ByValPair( + val, + PrimVal::from_bool(overflow), + ) + } else { + if overflow { + use rustc::mir::interpret::EvalError; + use rustc::mir::interpret::EvalErrorKind; + let mut err = EvalError { + kind: EvalErrorKind::OverflowingMath, + backtrace: None, + }; + ecx.report(&mut err, false, Some(span)); + return None; + } + Value::ByVal(val) + }; + Some((val, place_ty, span)) + }, + Err(mut err) => { + ecx.report(&mut err, false, Some(span)); + None + }, + } + }, + } + } +} + +fn type_size_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty: ty::Ty<'tcx>) -> Option { + use rustc::ty::layout::LayoutOf; + (tcx, param_env).layout_of(ty).ok().map(|layout| layout.size.bytes()) +} + +struct ConstPropVisitor { + local: Local, + can_const_prop: bool, + // false at the beginning, once set, there are not allowed to be any more assignments + found_assignment: bool, +} + +impl ConstPropVisitor { + /// returns true if `local` can be propagated + fn check<'tcx>(local: Local, mir: &Mir<'tcx>) -> bool { + let mut cpv = ConstPropVisitor { + local, + can_const_prop: true, + found_assignment: false, + }; + cpv.visit_mir(mir); + cpv.can_const_prop + } +} + +impl<'tcx> Visitor<'tcx> for ConstPropVisitor { + fn visit_statement( + &mut self, + block: BasicBlock, + statement: &Statement<'tcx>, + location: Location, + ) { + self.super_statement(block, statement, location); + match statement.kind { + StatementKind::SetDiscriminant { place: Place::Local(local), .. } | + StatementKind::Assign(Place::Local(local), _) => { + if local == self.local { + if self.found_assignment { + self.can_const_prop = false; + } else { + self.found_assignment = true + } + } + }, + StatementKind::InlineAsm { ref outputs, .. } => { + for place in outputs { + if let Place::Local(local) = *place { + if local == self.local { + if self.found_assignment { + self.can_const_prop = false; + } else { + self.found_assignment = true + } + return; + } + } + } + } + _ => {} + } + } + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { + self.super_rvalue(rvalue, location); + if let Rvalue::Ref(_, _, Place::Local(local)) = *rvalue { + if local == self.local { + self.can_const_prop = false; + } + } + } } impl<'b, 'a, 'tcx> Visitor<'tcx> for OptimizationFinder<'b, 'a, 'tcx> { + // override to visit basic blocks in execution order + fn super_mir(&mut self, mir: &Mir<'tcx>) { + let mut seen = FxHashSet::default(); + seen.insert(mir.start_node()); + let mut sorted = Vec::new(); + let mut next = VecDeque::new(); + sorted.push(mir.start_node()); + next.push_back(mir.start_node()); + while let Some(current) = next.pop_front() { + for successor in mir.successors(current) { + trace!("checking successor of {:?}: {:?}", current, successor); + trace!("{:?}, {:?}", sorted, next); + if seen.contains(&successor) { + for &pending in &next { + // not a back-edge, just a branch merging back into a single execution + if pending == successor { + // move to the back of the queue + let i = sorted.iter().position(|&b| b == successor).unwrap(); + sorted.remove(i); + sorted.push(successor); + break; + } + } + } else { + seen.insert(successor); + sorted.push(successor); + next.push_back(successor); + } + } + } + trace!("checking basic blocks: {:?}", sorted); + for bb in sorted { + self.visit_basic_block_data(bb, &mir[bb]); + } + + for scope in &mir.visibility_scopes { + self.visit_visibility_scope_data(scope); + } + + self.visit_ty(&mir.return_ty(), TyContext::ReturnTy(SourceInfo { + span: mir.span, + scope: ARGUMENT_VISIBILITY_SCOPE, + })); + + for local in mir.local_decls.indices() { + self.visit_local_decl(local, &mir.local_decls[local]); + } + + self.visit_span(&mir.span); + } + + fn visit_constant( + &mut self, + constant: &Constant<'tcx>, + location: Location, + ) { + trace!("visit_constant: {:?}", constant); + self.super_constant(constant, location); + self.eval_constant(constant); + } + + fn visit_statement( + &mut self, + block: BasicBlock, + statement: &Statement<'tcx>, + location: Location, + ) { + trace!("visit_statement: {:?}", statement); + if let StatementKind::Assign(ref place, ref rval) = statement.kind { + let place_ty = place + .ty(&self.mir.local_decls, self.tcx) + .to_ty(self.tcx); + let span = statement.source_info.span; + if let Some(value) = self.const_prop(rval, place_ty, span) { + self.optimizations.const_prop.insert(location, value); + if let Place::Local(local) = *place { + if !self.mir.local_decls[local].is_user_variable + && ConstPropVisitor::check(local, self.mir) { + trace!("storing {:?} to {:?}", value, local); + assert!(self.optimizations.places.insert(local, value).is_none()); + } + } + } + } + self.super_statement(block, statement, location); + } + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { if let Rvalue::Ref(_, _, Place::Projection(ref projection)) = *rvalue { if let ProjectionElem::Deref = projection.elem { @@ -111,10 +550,34 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for OptimizationFinder<'b, 'a, 'tcx> { self.super_rvalue(rvalue, location) } + + fn visit_terminator_kind( + &mut self, + block: BasicBlock, + kind: &TerminatorKind<'tcx>, + _location: Location, + ) { + match kind { + TerminatorKind::SwitchInt { discr: value, .. } | + TerminatorKind::Yield { value, .. } | + TerminatorKind::Assert { cond: value, .. } => { + if let Some(value) = self.simplify_operand(value) { + self.optimizations.terminators.insert(block, value); + } + } + // FIXME: do this optimization for function calls + _ => {}, + } + } } #[derive(Default)] struct OptimizationList<'tcx> { and_stars: FxHashSet, arrays_lengths: FxHashMap>, + const_prop: FxHashMap>, + /// Terminators that get their Operand(s) turned into constants. + terminators: FxHashMap>, + places: FxHashMap>, + constants: FxHashMap, Const<'tcx>>, } diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 563405fccc970..ff3b5888e340c 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -256,6 +256,7 @@ fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx // Optimizations begin. inline::Inline, instcombine::InstCombine, + 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 b896e6ca85343..77ba521a684b8 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -611,7 +611,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 20c33bab1aacb..a91f40491e92a 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_u128() { let (otherwise, targets) = targets.split_last().unwrap(); let mut ret = TerminatorKind::Goto { target: *otherwise }; - for (v, t) in values.iter().zip(targets.iter()) { + for (&v, t) in values.iter().zip(targets.iter()) { if v == constint { ret = TerminatorKind::Goto { target: *t }; break; @@ -56,9 +57,12 @@ impl MirPass for SimplifyBranches { }, TerminatorKind::Assert { target, cond: Operand::Constant(box Constant { literal: Literal::Value { - value: &ty::Const { val: ConstVal::Bool(cond), .. } + value: &ty::Const { + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(cond))), + .. } }, .. - }), expected, .. } if cond == expected => { + }), expected, .. } if (cond == 1) == expected => { + assert!(cond <= 1); TerminatorKind::Goto { target: target } }, TerminatorKind::FalseEdges { real_target, .. } => { diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs index 3331bc9e59e0b..076a7bac3172e 100644 --- a/src/librustc_mir/util/elaborate_drops.rs +++ b/src/librustc_mir/util/elaborate_drops.rs @@ -11,13 +11,14 @@ use std::fmt; use rustc::hir; use rustc::mir::*; -use rustc::middle::const_val::{ConstInt, ConstVal}; +use rustc::middle::const_val::ConstVal; use rustc::middle::lang_items; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::subst::{Kind, Substs}; use rustc::ty::util::IntTypeExt; use rustc_data_structures::indexed_vec::Idx; use util::patch::MirPatch; +use rustc::mir::interpret::{Value, PrimVal}; use std::{iter, u32}; @@ -425,7 +426,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> variant_path, &adt.variants[variant_index], substs); - values.push(discr); + values.push(discr.to_u128().unwrap()); if let Unwind::To(unwind) = unwind { // We can't use the half-ladder from the original // drop ladder, because this breaks the @@ -480,7 +481,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> fn adt_switch_block(&mut self, adt: &'tcx ty::AdtDef, blocks: Vec, - values: &[ConstInt], + values: &[u128], succ: BasicBlock, unwind: Unwind) -> BasicBlock { @@ -799,7 +800,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> self.complete_drop(Some(DropFlagMode::Deep), succ, unwind) } ty::TyArray(ety, size) => self.open_drop_for_array( - ety, size.val.to_const_int().and_then(|v| v.to_u64())), + ety, size.val.to_u128().map(|i| i as u64)), ty::TySlice(ety) => self.open_drop_for_array(ety, None), _ => bug!("open drop from non-ADT `{:?}`", ty) @@ -945,7 +946,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 776f330a58bdd..3f25c08aff537 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" } syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs index 6df860492f05a..0c47a00a07281 100644 --- a/src/librustc_passes/consts.rs +++ b/src/librustc_passes/consts.rs @@ -25,12 +25,6 @@ // by borrowck::gather_loans use rustc::ty::cast::CastKind; -use rustc_const_eval::ConstContext; -use rustc::middle::const_val::ConstEvalErr; -use rustc::middle::const_val::ErrKind::{IndexOpFeatureGated, UnimplementedConstVal, MiscCatchAll}; -use rustc::middle::const_val::ErrKind::{ErroneousReferencedConstant, MiscBinaryOp, NonConstPath}; -use rustc::middle::const_val::ErrKind::{TypeckError, Math, LayoutError}; -use rustc_const_math::{ConstMathErr, Op}; use rustc::hir::def::{Def, CtorKind}; use rustc::hir::def_id::DefId; use rustc::hir::map::blocks::FnLikeNode; @@ -41,17 +35,13 @@ use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::maps::{queries, Providers}; use rustc::ty::subst::Substs; use rustc::traits::Reveal; -use rustc::util::common::ErrorReported; use rustc::util::nodemap::{ItemLocalSet, NodeSet}; -use rustc::lint::builtin::CONST_ERR; -use rustc::hir::{self, PatKind, RangeEnd}; +use rustc::hir; use std::rc::Rc; 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,42 +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) => { - struct_span_err!(self.tcx.sess, start.span, E0030, - "lower range bound must be less than or equal to upper") - .span_label(start.span, "lower bound larger than upper bound") - .emit(); - } - Err(ErrorReported) => {} - } - } - _ => {} - } - intravisit::walk_pat(self, p); - } - fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt) { match stmt.node { hir::StmtDecl(ref decl, _) => { @@ -298,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); } diff --git a/src/librustc_passes/diagnostics.rs b/src/librustc_passes/diagnostics.rs index 743f7b7326e9e..fba7a89511d9f 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. @@ -244,24 +227,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 73c71ec0b2f00..2bb4cfcbe5231 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; #[macro_use] diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 633ed9b32cd1e..a8c92b81c7759 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -197,7 +197,7 @@ pub fn unsized_info<'cx, 'tcx>(cx: &CodegenCx<'cx, 'tcx>, let (source, target) = cx.tcx.struct_lockstep_tails(source, target); match (&source.sty, &target.sty) { (&ty::TyArray(_, len), &ty::TySlice(_)) => { - C_usize(cx, len.val.to_const_int().unwrap().to_u64().unwrap()) + C_usize(cx, len.val.unwrap_u64()) } (&ty::TyDynamic(..), &ty::TyDynamic(..)) => { // For now, upcasts are limited to changes in marker diff --git a/src/librustc_trans/consts.rs b/src/librustc_trans/consts.rs index d5b33d837c5bd..7cab7a5482225 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, MonoItemExt}; @@ -247,13 +246,16 @@ pub fn get_static(cx: &CodegenCx, def_id: DefId) -> ValueRef { pub fn trans_static<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, m: hir::Mutability, id: ast::NodeId, - attrs: &[ast::Attribute]) - -> Result> { + attrs: &[ast::Attribute]) { unsafe { let def_id = cx.tcx.hir.local_def_id(id); 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 @@ -317,7 +319,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 62ba91840d95e..6111b243720ff 100644 --- a/src/librustc_trans/debuginfo/metadata.rs +++ b/src/librustc_trans/debuginfo/metadata.rs @@ -276,7 +276,7 @@ fn fixed_vec_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, let upper_bound = match array_or_slice_type.sty { ty::TyArray(_, len) => { - len.val.to_const_int().unwrap().to_u64().unwrap() as c_longlong + len.val.unwrap_u64() as c_longlong } _ => -1 }; diff --git a/src/librustc_trans/debuginfo/type_names.rs b/src/librustc_trans/debuginfo/type_names.rs index 0aec92b0d66cf..381a48205e737 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 bf82e1d50c473..c7f8d1a69c8f4 100644 --- a/src/librustc_trans/mir/analyze.rs +++ b/src/librustc_trans/mir/analyze.rs @@ -17,6 +17,7 @@ use rustc::middle::const_val::ConstVal; use rustc::mir::{self, Location, TerminatorKind, Literal}; use rustc::mir::visit::{Visitor, PlaceContext}; use rustc::mir::traversal; +use rustc::mir::interpret::{Value, PrimVal}; use rustc::ty; use rustc::ty::layout::LayoutOf; use type_of::LayoutLlvmExt; @@ -109,15 +110,25 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> { block: mir::BasicBlock, kind: &mir::TerminatorKind<'tcx>, location: Location) { - match *kind { + let check = match *kind { mir::TerminatorKind::Call { func: mir::Operand::Constant(box mir::Constant { literal: Literal::Value { - value: &ty::Const { val: ConstVal::Function(def_id, _), .. }, .. + value: &ty::Const { val, ty }, .. }, .. }), ref args, .. - } if Some(def_id) == self.fx.cx.tcx.lang_items().box_free_fn() => { + } => match val { + ConstVal::Value(Value::ByVal(PrimVal::Undef)) => match ty.sty { + ty::TyFnDef(did, _) => Some((did, args)), + _ => None, + }, + _ => None, + } + _ => None, + }; + if let Some((def_id, args)) = check { + if Some(def_id) == self.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 +136,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 af1e30a4b19a6..d0121ebe1e011 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) } } } @@ -358,10 +358,10 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { let const_err = common::const_to_opt_u128(len, false) .and_then(|len| common::const_to_opt_u128(index, false) - .map(|index| ErrKind::IndexOutOfBounds { - len: len as u64, - index: index as u64 - })); + .map(|index| format!( + "index out of bounds: the len is {} but the index is {}", + len, index, + ))); let file_line_col = C_struct(bx.cx, &[filename, line, col], false); let file_line_col = consts::addr_of(bx.cx, @@ -384,7 +384,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { "panic_loc"); (lang_items::PanicFnLangItem, vec![msg_file_line_col], - Some(ErrKind::Math(err.clone()))) + Some(err.description().to_owned())) } mir::AssertMessage::GeneratorResumedAfterReturn | mir::AssertMessage::GeneratorResumedAfterPanic => { @@ -412,10 +412,11 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { // is also constant, then we can produce a warning. if const_cond == Some(!expected) { if let Some(err) = const_err { - let err = ConstEvalErr{ span: span, kind: err }; let mut diag = bx.tcx().sess.struct_span_warn( - span, "this expression will panic at run-time"); - err.note(bx.tcx(), span, "expression", &mut diag); + span, &format!( + "this expression will panic at run-time with {:?}", + err, + )); diag.emit(); } } @@ -529,10 +530,13 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { span_bug!(span, "shuffle indices must be constant"); } mir::Operand::Constant(ref constant) => { - let val = self.trans_constant(&bx, constant); + let (llval, ty) = self.remove_me_shuffle_indices( + &bx, + constant, + ); return OperandRef { - val: Immediate(val.llval), - layout: bx.cx.layout_of(val.ty) + val: Immediate(llval), + layout: bx.cx.layout_of(ty) }; } } diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 71ce0aa3da96b..be5cb311b9cc2 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -9,953 +9,28 @@ // 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_const_math::{ConstInt, ConstMathErr}; +use rustc::middle::const_val::{ConstVal, ConstEvalErr}; +use rustc_mir::interpret::{read_target_uint, const_val_field}; use rustc::hir::def_id::DefId; -use rustc::infer::TransNormalize; use rustc::traits; use rustc::mir; -use rustc::mir::tcx::PlaceTy; -use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; -use rustc::ty::layout::{self, LayoutOf, Size}; -use rustc::ty::cast::{CastTy, IntTy}; -use rustc::ty::subst::{Kind, Substs}; -use rustc_apfloat::{ieee, Float, Status}; -use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use rustc_data_structures::indexed_vec::Idx; +use rustc::mir::interpret::{Allocation, GlobalId, MemoryPointer, PrimVal, Value as MiriValue}; +use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::layout::{self, HasDataLayout, LayoutOf, Scalar}; use base; -use abi::{self, Abi}; -use callee; use builder::Builder; -use common::{self, CodegenCx, const_get_elt, val_ty}; -use common::{C_array, C_bool, C_bytes, C_int, C_uint, C_uint_big, C_u32, C_u64}; -use common::{C_null, C_struct, C_str_slice, C_undef, C_usize, C_vector, C_fat_ptr}; +use common::{CodegenCx}; +use common::{C_bytes, C_struct, C_uint_big, C_undef, C_usize}; use common::const_to_opt_u128; use consts; use type_of::LayoutLlvmExt; use type_::Type; -use value::Value; -use syntax_pos::Span; -use syntax::ast; - -use 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) - } - } - _ => { - 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) -} - -/// An place as seen from a constant. -#[derive(Copy, Clone)] -struct ConstPlace<'tcx> { - base: Base, - llextra: ValueRef, - ty: Ty<'tcx> -} - -impl<'tcx> ConstPlace<'tcx> { - fn to_const(&self, span: Span) -> Const<'tcx> { - match self.base { - Base::Value(val) => Const::new(val, self.ty), - Base::Str(ptr) => { - span_bug!(span, "loading from `str` ({:?}) in constant", - Value(ptr)) - } - Base::Static(val) => { - span_bug!(span, "loading from `static` ({:?}) in constant", - Value(val)) - } - } - } - - pub fn len<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> ValueRef { - match self.ty.sty { - ty::TyArray(_, n) => { - C_usize(cx, n.val.to_const_int().unwrap().to_u64().unwrap()) - } - ty::TySlice(_) | ty::TyStr => { - assert!(self.llextra != ptr::null_mut()); - self.llextra - } - _ => bug!("unexpected type `{}` in ConstPlace::len", self.ty) - } - } -} - -/// Machinery for translating a constant's MIR to LLVM values. -/// FIXME(eddyb) use miri and lower its allocations to LLVM. -struct MirConstContext<'a, 'tcx: 'a> { - cx: &'a CodegenCx<'a, 'tcx>, - mir: &'a mir::Mir<'tcx>, - - /// Type parameters for const fn and associated constants. - substs: &'tcx Substs<'tcx>, - - /// Values of locals in a constant or const fn. - locals: IndexVec, ConstEvalErr<'tcx>>>> -} - -fn add_err<'tcx, U, V>(failure: &mut Result>, - value: &Result>) -{ - if let &Err(ref err) = value { - if failure.is_ok() { - *failure = Err(err.clone()); - } - } -} - -impl<'a, 'tcx> MirConstContext<'a, 'tcx> { - fn new(cx: &'a CodegenCx<'a, 'tcx>, - mir: &'a mir::Mir<'tcx>, - substs: &'tcx Substs<'tcx>, - args: IndexVec, ConstEvalErr<'tcx>>>) - -> MirConstContext<'a, 'tcx> { - let mut context = MirConstContext { - cx, - mir, - substs, - locals: (0..mir.local_decls.len()).map(|_| None).collect(), - }; - for (i, arg) in args.into_iter().enumerate() { - // Locals after local 0 are the function arguments - let index = mir::Local::new(i + 1); - context.locals[index] = Some(arg); - } - context - } - - fn trans_def(cx: &'a CodegenCx<'a, 'tcx>, - def_id: DefId, - substs: &'tcx Substs<'tcx>, - args: IndexVec, ConstEvalErr<'tcx>>>) - -> Result, ConstEvalErr<'tcx>> { - let instance = ty::Instance::resolve(cx.tcx, - ty::ParamEnv::empty(traits::Reveal::All), - def_id, - substs).unwrap(); - let mir = cx.tcx.instance_mir(instance.def); - MirConstContext::new(cx, &mir, instance.substs, args).trans() - } - - fn monomorphize(&self, value: &T) -> T - where T: TransNormalize<'tcx> - { - self.cx.tcx.trans_apply_param_substs(self.substs, value) - } - - fn trans(&mut self) -> Result, ConstEvalErr<'tcx>> { - let tcx = self.cx.tcx; - let mut bb = mir::START_BLOCK; - - // Make sure to evaluate all statemenets to - // report as many errors as we possibly can. - let mut failure = Ok(()); - - loop { - let data = &self.mir[bb]; - for statement in &data.statements { - let span = statement.source_info.span; - match statement.kind { - mir::StatementKind::Assign(ref dest, ref rvalue) => { - let ty = dest.ty(self.mir, tcx); - let ty = self.monomorphize(&ty).to_ty(tcx); - let value = self.const_rvalue(rvalue, ty, span); - add_err(&mut failure, &value); - self.store(dest, value, span); - } - mir::StatementKind::StorageLive(_) | - mir::StatementKind::StorageDead(_) | - mir::StatementKind::Validate(..) | - mir::StatementKind::EndRegion(_) | - mir::StatementKind::Nop => {} - mir::StatementKind::InlineAsm { .. } | - mir::StatementKind::SetDiscriminant{ .. } => { - span_bug!(span, "{:?} should not appear in constants?", statement.kind); - } - } - } - - let terminator = data.terminator(); - let span = terminator.source_info.span; - bb = match terminator.kind { - mir::TerminatorKind::Drop { target, .. } | // No dropping. - mir::TerminatorKind::Goto { target } => target, - mir::TerminatorKind::Return => { - failure?; - return self.locals[mir::RETURN_PLACE].clone().unwrap_or_else(|| { - span_bug!(span, "no returned value in constant"); - }); - } - - mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, .. } => { - let cond = self.const_operand(cond, span)?; - let cond_bool = common::const_to_uint(cond.llval) != 0; - if cond_bool != expected { - let err = match *msg { - mir::AssertMessage::BoundsCheck { ref len, ref index } => { - let len = self.const_operand(len, span)?; - let index = self.const_operand(index, span)?; - ErrKind::IndexOutOfBounds { - len: common::const_to_uint(len.llval), - index: common::const_to_uint(index.llval) - } - } - mir::AssertMessage::Math(ref err) => { - ErrKind::Math(err.clone()) - } - mir::AssertMessage::GeneratorResumedAfterReturn | - mir::AssertMessage::GeneratorResumedAfterPanic => - span_bug!(span, "{:?} should not appear in constants?", msg), - }; - - let err = ConstEvalErr { span: span, kind: err }; - err.report(tcx, span, "expression"); - failure = Err(err); - } - target - } - - mir::TerminatorKind::Call { ref func, ref args, ref destination, .. } => { - let fn_ty = func.ty(self.mir, tcx); - let fn_ty = self.monomorphize(&fn_ty); - let (def_id, substs) = match fn_ty.sty { - ty::TyFnDef(def_id, substs) => (def_id, substs), - _ => span_bug!(span, "calling {:?} (of type {}) in constant", - func, fn_ty) - }; - - let mut arg_vals = IndexVec::with_capacity(args.len()); - for arg in args { - let arg_val = self.const_operand(arg, span); - add_err(&mut failure, &arg_val); - arg_vals.push(arg_val); - } - if let Some((ref dest, target)) = *destination { - let result = if fn_ty.fn_sig(tcx).abi() == Abi::RustIntrinsic { - match &tcx.item_name(def_id)[..] { - "size_of" => { - let llval = C_usize(self.cx, - self.cx.size_of(substs.type_at(0)).bytes()); - Ok(Const::new(llval, tcx.types.usize)) - } - "min_align_of" => { - let llval = C_usize(self.cx, - self.cx.align_of(substs.type_at(0)).abi()); - Ok(Const::new(llval, tcx.types.usize)) - } - _ => 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 - } 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, ConstEvalErr<'tcx>>, - span: Span) { - if let mir::Place::Local(index) = *dest { - self.locals[index] = Some(value); - } else { - span_bug!(span, "assignment to {:?} in constant", dest); - } - } - - fn const_place(&self, place: &mir::Place<'tcx>, span: Span) - -> Result, ConstEvalErr<'tcx>> { - let tcx = self.cx.tcx; - - if let mir::Place::Local(index) = *place { - return self.locals[index].clone().unwrap_or_else(|| { - span_bug!(span, "{:?} not initialized", place) - }).map(|v| v.as_place()); - } - - let place = match *place { - mir::Place::Local(_) => bug!(), // handled above - mir::Place::Static(box mir::Static { def_id, ty }) => { - ConstPlace { - base: Base::Static(consts::get_static(self.cx, def_id)), - llextra: ptr::null_mut(), - ty: self.monomorphize(&ty), - } - } - mir::Place::Projection(ref projection) => { - let tr_base = self.const_place(&projection.base, span)?; - let projected_ty = PlaceTy::Ty { ty: tr_base.ty } - .projection_ty(tcx, &projection.elem); - let base = tr_base.to_const(span); - let projected_ty = self.monomorphize(&projected_ty).to_ty(tcx); - let has_metadata = self.cx.type_has_metadata(projected_ty); - - let (projected, llextra) = match projection.elem { - mir::ProjectionElem::Deref => { - let (base, extra) = if !has_metadata { - (base.llval, ptr::null_mut()) - } else { - base.get_fat_ptr(self.cx) - }; - if self.cx.statics.borrow().contains_key(&base) { - (Base::Static(base), extra) - } else if let ty::TyStr = projected_ty.sty { - (Base::Str(base), extra) - } else { - let v = base; - let v = self.cx.const_unsized.borrow().get(&v).map_or(v, |&v| v); - let mut val = unsafe { llvm::LLVMGetInitializer(v) }; - if val.is_null() { - span_bug!(span, "dereference of non-constant pointer `{:?}`", - Value(base)); - } - let layout = self.cx.layout_of(projected_ty); - if let layout::Abi::Scalar(ref scalar) = layout.abi { - let i1_type = Type::i1(self.cx); - if scalar.is_bool() && val_ty(val) != i1_type { - unsafe { - val = llvm::LLVMConstTrunc(val, i1_type.to_ref()); - } - } - } - (Base::Value(val), extra) - } - } - mir::ProjectionElem::Field(ref field, _) => { - let llprojected = base.get_field(self.cx, field.index()); - let llextra = if !has_metadata { - ptr::null_mut() - } else { - tr_base.llextra - }; - (Base::Value(llprojected), llextra) - } - mir::ProjectionElem::Index(index) => { - let index = &mir::Operand::Copy(mir::Place::Local(index)); - let llindex = self.const_operand(index, span)?.llval; - - let iv = if let Some(iv) = common::const_to_opt_u128(llindex, false) { - iv - } else { - span_bug!(span, "index is not an integer-constant expression") - }; - - // Produce an undef instead of a LLVM assertion on OOB. - let len = common::const_to_uint(tr_base.len(self.cx)); - let llelem = if iv < len as u128 { - const_get_elt(base.llval, iv as u64) - } else { - C_undef(self.cx.layout_of(projected_ty).llvm_type(self.cx)) - }; - - (Base::Value(llelem), ptr::null_mut()) - } - _ => span_bug!(span, "{:?} in constant", projection.elem) - }; - ConstPlace { - base: projected, - llextra, - ty: projected_ty - } - } - }; - Ok(place) - } - - fn const_operand(&self, operand: &mir::Operand<'tcx>, span: Span) - -> Result, ConstEvalErr<'tcx>> { - debug!("const_operand({:?} @ {:?})", operand, span); - let result = match *operand { - mir::Operand::Copy(ref place) | - mir::Operand::Move(ref place) => { - Ok(self.const_place(place, span)?.to_const(span)) - } - - mir::Operand::Constant(ref constant) => { - let ty = self.monomorphize(&constant.ty); - match constant.literal.clone() { - mir::Literal::Promoted { index } => { - let mir = &self.mir.promoted[index]; - MirConstContext::new(self.cx, mir, self.substs, IndexVec::new()).trans() - } - mir::Literal::Value { value } => { - if let ConstVal::Unevaluated(def_id, substs) = value.val { - let substs = self.monomorphize(&substs); - MirConstContext::trans_def(self.cx, def_id, substs, IndexVec::new()) - } else { - Ok(Const::from_constval(self.cx, &value.val, ty)) - } - } - } - } - }; - debug!("const_operand({:?} @ {:?}) = {:?}", operand, span, - result.as_ref().ok()); - result - } - - fn const_array(&self, array_ty: Ty<'tcx>, fields: &[ValueRef]) - -> Const<'tcx> - { - let elem_ty = array_ty.builtin_index().unwrap_or_else(|| { - bug!("bad array type {:?}", array_ty) - }); - let llunitty = self.cx.layout_of(elem_ty).llvm_type(self.cx); - // If the array contains enums, an LLVM array won't work. - let val = if fields.iter().all(|&f| val_ty(f) == llunitty) { - C_array(llunitty, fields) - } else { - C_struct(self.cx, fields, false) - }; - Const::new(val, array_ty) - } - - fn const_rvalue(&self, rvalue: &mir::Rvalue<'tcx>, - dest_ty: Ty<'tcx>, span: Span) - -> Result, ConstEvalErr<'tcx>> { - let tcx = self.cx.tcx; - debug!("const_rvalue({:?}: {:?} @ {:?})", rvalue, dest_ty, span); - let val = match *rvalue { - mir::Rvalue::Use(ref operand) => self.const_operand(operand, span)?, - - mir::Rvalue::Repeat(ref elem, count) => { - let elem = self.const_operand(elem, span)?; - let size = count.as_u64(); - assert_eq!(size as usize as u64, size); - let fields = vec![elem.llval; size as usize]; - self.const_array(dest_ty, &fields) - } - - mir::Rvalue::Aggregate(box mir::AggregateKind::Array(_), ref operands) => { - // Make sure to evaluate all operands to - // report as many errors as we possibly can. - let mut fields = Vec::with_capacity(operands.len()); - let mut failure = Ok(()); - for operand in operands { - match self.const_operand(operand, span) { - Ok(val) => fields.push(val.llval), - Err(err) => if failure.is_ok() { failure = Err(err); } - } - } - failure?; - - self.const_array(dest_ty, &fields) - } - - mir::Rvalue::Aggregate(ref kind, ref operands) => { - // Make sure to evaluate all operands to - // report as many errors as we possibly can. - let mut fields = Vec::with_capacity(operands.len()); - let mut failure = Ok(()); - for operand in operands { - match self.const_operand(operand, span) { - Ok(val) => fields.push(val), - Err(err) => if failure.is_ok() { failure = Err(err); } - } - } - failure?; - - trans_const_adt(self.cx, dest_ty, kind, &fields) - } - - mir::Rvalue::Cast(ref kind, ref source, cast_ty) => { - let operand = self.const_operand(source, span)?; - let cast_ty = self.monomorphize(&cast_ty); - - let val = match *kind { - mir::CastKind::ReifyFnPointer => { - match operand.ty.sty { - ty::TyFnDef(def_id, substs) => { - callee::resolve_and_get_fn(self.cx, def_id, substs) - } - _ => { - span_bug!(span, "{} cannot be reified to a fn ptr", - operand.ty) - } - } - } - mir::CastKind::ClosureFnPointer => { - match operand.ty.sty { - ty::TyClosure(def_id, substs) => { - // Get the def_id for FnOnce::call_once - let fn_once = tcx.lang_items().fn_once_trait().unwrap(); - let call_once = tcx - .global_tcx().associated_items(fn_once) - .find(|it| it.kind == ty::AssociatedKind::Method) - .unwrap().def_id; - // Now create its substs [Closure, Tuple] - let input = substs.closure_sig(def_id, tcx).input(0); - let input = tcx.erase_late_bound_regions_and_normalize(&input); - let substs = tcx.mk_substs([operand.ty, input] - .iter().cloned().map(Kind::from)); - callee::resolve_and_get_fn(self.cx, call_once, substs) - } - _ => { - bug!("{} cannot be cast to a fn ptr", operand.ty) - } - } - } - mir::CastKind::UnsafeFnPointer => { - // this is a no-op at the LLVM level - operand.llval - } - mir::CastKind::Unsize => { - let pointee_ty = operand.ty.builtin_deref(true, ty::NoPreference) - .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, ty::NoPreference) - .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 bk == mir::BorrowKind::Mut { - consts::addr_of_mut(self.cx, llval, align, "ref_mut") - } else { - consts::addr_of(self.cx, llval, align, "ref") - } - } - Base::Str(llval) | - Base::Static(llval) => llval - }; - - let ptr = if self.cx.type_is_sized(ty) { - base - } else { - C_fat_ptr(self.cx, base, tr_place.llextra) - }; - Const::new(ptr, ref_ty) - } - - mir::Rvalue::Len(ref place) => { - let tr_place = self.const_place(place, span)?; - Const::new(tr_place.len(self.cx), tcx.types.usize) - } - - mir::Rvalue::BinaryOp(op, ref lhs, ref rhs) => { - let lhs = self.const_operand(lhs, span)?; - let rhs = self.const_operand(rhs, span)?; - let ty = lhs.ty; - let binop_ty = op.ty(tcx, lhs.ty, rhs.ty); - let (lhs, rhs) = (lhs.llval, rhs.llval); - Const::new(const_scalar_binop(op, lhs, rhs, ty), binop_ty) - } - - mir::Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) => { - let lhs = self.const_operand(lhs, span)?; - let rhs = self.const_operand(rhs, span)?; - let ty = lhs.ty; - let val_ty = op.ty(tcx, lhs.ty, rhs.ty); - let binop_ty = tcx.intern_tup(&[val_ty, tcx.types.bool], false); - let (lhs, rhs) = (lhs.llval, rhs.llval); - assert!(!ty.is_fp()); - - match const_scalar_checked_binop(tcx, op, lhs, rhs, ty) { - Some((llval, of)) => { - trans_const_adt(self.cx, binop_ty, &mir::AggregateKind::Tuple, &[ - Const::new(llval, val_ty), - Const::new(C_bool(self.cx, of), tcx.types.bool) - ]) - } - None => { - span_bug!(span, "{:?} got non-integer operands: {:?} and {:?}", - rvalue, Value(lhs), Value(rhs)); - } - } - } - - mir::Rvalue::UnaryOp(op, ref operand) => { - let operand = self.const_operand(operand, span)?; - let lloperand = operand.llval; - let llval = match op { - mir::UnOp::Not => { - unsafe { - llvm::LLVMConstNot(lloperand) - } - } - mir::UnOp::Neg => { - let is_float = operand.ty.is_fp(); - unsafe { - if is_float { - llvm::LLVMConstFNeg(lloperand) - } else { - llvm::LLVMConstNeg(lloperand) - } - } - } - }; - Const::new(llval, operand.ty) - } - - mir::Rvalue::NullaryOp(mir::NullOp::SizeOf, ty) => { - assert!(self.cx.type_is_sized(ty)); - let llval = C_usize(self.cx, self.cx.size_of(ty).bytes()); - Const::new(llval, tcx.types.usize) - } - - _ => span_bug!(span, "{:?} in constant", rvalue) - }; - - debug!("const_rvalue({:?}: {:?} @ {:?}) = {:?}", rvalue, dest_ty, span, val); - - Ok(val) - } - -} - fn to_const_int(value: ValueRef, t: Ty, tcx: TyCtxt) -> Option { match t.sty { ty::TyInt(int_type) => const_to_opt_u128(value, true) @@ -1060,278 +135,239 @@ pub fn const_scalar_checked_binop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } -unsafe fn cast_const_float_to_int(cx: &CodegenCx, - operand: &Const, - signed: bool, - int_ty: Type, - span: Span) -> ValueRef { - let llval = operand.llval; - let float_bits = match operand.ty.sty { - ty::TyFloat(fty) => fty.bit_width(), - _ => bug!("cast_const_float_to_int: operand not a float"), - }; - // Note: this breaks if llval is a complex constant expression rather than a simple constant. - // One way that might happen would be if addresses could be turned into integers in constant - // expressions, but that doesn't appear to be possible? - // In any case, an ICE is better than producing undef. - let llval_bits = consts::bitcast(llval, Type::ix(cx, float_bits as u64)); - let bits = const_to_opt_u128(llval_bits, false).unwrap_or_else(|| { - panic!("could not get bits of constant float {:?}", - Value(llval)); - }); - let int_width = int_ty.int_width() as usize; - // Try to convert, but report an error for overflow and NaN. This matches HIR const eval. - let cast_result = match float_bits { - 32 if signed => ieee::Single::from_bits(bits).to_i128(int_width).map(|v| v as u128), - 64 if signed => ieee::Double::from_bits(bits).to_i128(int_width).map(|v| v as u128), - 32 => ieee::Single::from_bits(bits).to_u128(int_width), - 64 => ieee::Double::from_bits(bits).to_u128(int_width), - n => bug!("unsupported float width {}", n), - }; - if cast_result.status.contains(Status::INVALID_OP) { - let err = ConstEvalErr { span: span, kind: ErrKind::CannotCast }; - err.report(cx.tcx, span, "expression"); - } - C_uint_big(int_ty, cast_result.value) -} - -unsafe fn cast_const_int_to_float(cx: &CodegenCx, - llval: ValueRef, - signed: bool, - float_ty: Type) -> ValueRef { - // Note: this breaks if llval is a complex constant expression rather than a simple constant. - // One way that might happen would be if addresses could be turned into integers in constant - // expressions, but that doesn't appear to be possible? - // In any case, an ICE is better than producing undef. - let value = const_to_opt_u128(llval, signed).unwrap_or_else(|| { - panic!("could not get z128 value of constant integer {:?}", - Value(llval)); - }); - if signed { - llvm::LLVMConstSIToFP(llval, float_ty.to_ref()) - } else if float_ty.float_width() == 32 && value >= MAX_F32_PLUS_HALF_ULP { - // We're casting to f32 and the value is > f32::MAX + 0.5 ULP -> round up to infinity. - let infinity_bits = C_u32(cx, ieee::Single::INFINITY.to_bits() as u32); - consts::bitcast(infinity_bits, float_ty) - } else { - llvm::LLVMConstUIToFP(llval, float_ty.to_ref()) - } -} - -impl<'a, 'tcx> FunctionCx<'a, 'tcx> { - pub fn trans_constant(&mut self, - bx: &Builder<'a, 'tcx>, - constant: &mir::Constant<'tcx>) - -> Const<'tcx> - { - debug!("trans_constant({:?})", constant); - let ty = self.monomorphize(&constant.ty); - let result = match constant.literal.clone() { - mir::Literal::Promoted { index } => { - let mir = &self.mir.promoted[index]; - MirConstContext::new(bx.cx, mir, self.param_substs, IndexVec::new()).trans() +pub fn primval_to_llvm(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) } - mir::Literal::Value { value } => { - if let ConstVal::Unevaluated(def_id, substs) = value.val { - let substs = self.monomorphize(&substs); - MirConstContext::trans_def(bx.cx, def_id, substs, IndexVec::new()) + }, + PrimVal::Ptr(ptr) => { + let interpret_interner = cx.tcx.interpret_interner.borrow(); + if let Some(fn_instance) = interpret_interner.get_fn(ptr.alloc_id) { + callee::get_fn(cx, fn_instance) + } else { + let static_ = interpret_interner.get_corresponding_static_def_id(ptr.alloc_id); + let base_addr = if let Some(def_id) = static_ { + assert!(cx.tcx.is_static(def_id).is_some()); + consts::get_static(cx, def_id) + } else if let Some(alloc) = interpret_interner.get_alloc(ptr.alloc_id) { + let init = global_initializer(cx, alloc); + if alloc.mutable { + consts::addr_of_mut(cx, init, alloc.align, "byte_str") + } else { + consts::addr_of(cx, init, alloc.align, "byte_str") + } } else { - Ok(Const::from_constval(bx.cx, &value.val, ty)) + bug!("missing allocation {:?}", ptr.alloc_id); + }; + + let llval = unsafe { llvm::LLVMConstInBoundsGEP( + consts::bitcast(base_addr, Type::i8p(cx)), + &C_usize(cx, ptr.offset), + 1, + ) }; + if scalar.value != layout::Pointer { + unsafe { llvm::LLVMConstPtrToInt(llval, llty.to_ref()) } + } else { + consts::bitcast(llval, llty) } } - }; - - let result = result.unwrap_or_else(|_| { - // We've errored, so we don't have to produce working code. - let llty = bx.cx.layout_of(ty).llvm_type(bx.cx); - Const::new(C_undef(llty), ty) - }); - - debug!("trans_constant({:?}) = {:?}", constant, result); - result + } } } +pub fn global_initializer(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 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; + } + if alloc.bytes.len() >= next_offset { + llvals.push(C_bytes(cx, &alloc.bytes[next_offset ..])); + } + + C_struct(cx, &llvals, true) +} pub fn trans_static_initializer<'a, 'tcx>( cx: &CodegenCx<'a, 'tcx>, def_id: DefId) -> Result> { - MirConstContext::trans_def(cx, def_id, Substs::empty(), IndexVec::new()) - .map(|c| c.llval) -} - -/// Construct a constant value, suitable for initializing a -/// GlobalVariable, given a case and constant values for its fields. -/// Note that this may have a different LLVM type (and different -/// alignment!) from the representation's `type_of`, so it needs a -/// pointer cast before use. -/// -/// The LLVM type system does not directly support unions, and only -/// pointers can be bitcast, so a constant (and, by extension, the -/// GlobalVariable initialized by it) will have a type that can vary -/// depending on which case of an enum it is. -/// -/// To understand the alignment situation, consider `enum E { V64(u64), -/// V32(u32, u32) }` on Windows. The type has 8-byte alignment to -/// accommodate the u64, but `V32(x, y)` would have LLVM type `{i32, -/// i32, i32}`, which is 4-byte aligned. -/// -/// Currently the returned value has the same size as the type, but -/// this could be changed in the future to avoid allocating unnecessary -/// space after values of shorter-than-maximum cases. -fn trans_const_adt<'a, 'tcx>( - cx: &CodegenCx<'a, 'tcx>, - t: Ty<'tcx>, - kind: &mir::AggregateKind, - vals: &[Const<'tcx>] -) -> Const<'tcx> { - let l = cx.layout_of(t); - let variant_index = match *kind { - mir::AggregateKind::Adt(_, index, _, _) => index, - _ => 0, + let instance = ty::Instance::mono(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))?; + + let alloc_id = cx + .tcx + .interpret_interner + .borrow() + .get_cached(def_id) + .expect("global not cached"); + + let alloc = cx + .tcx + .interpret_interner + .borrow() + .get_alloc(alloc_id) + .expect("miri allocation never successfully created"); + Ok(global_initializer(cx, alloc)) +} - if let layout::Abi::Uninhabited = l.abi { - return Const::new(C_undef(l.llvm_type(cx)), t); - } - - match l.variants { - layout::Variants::Single { index } => { - assert_eq!(variant_index, index); - if let layout::FieldPlacement::Union(_) = l.fields { - assert_eq!(variant_index, 0); - assert_eq!(vals.len(), 1); - let (field_size, field_align) = cx.size_and_align_of(vals[0].ty); - let contents = [ - vals[0].llval, - padding(cx, l.size - field_size) - ]; - - let packed = l.align.abi() < field_align.abi(); - Const::new(C_struct(cx, &contents, packed), t) - } else { - if let layout::Abi::Vector { .. } = l.abi { - if let layout::FieldPlacement::Array { .. } = l.fields { - return Const::new(C_vector(&vals.iter().map(|x| x.llval) - .collect::>()), t); - } - } - build_const_struct(cx, l, vals, None) - } - } - layout::Variants::Tagged { .. } => { - let discr = match *kind { - mir::AggregateKind::Adt(adt_def, _, _, _) => { - adt_def.discriminant_for_variant(cx.tcx, variant_index) - .to_u128_unchecked() as u64 - }, - _ => 0, - }; - let discr_field = l.field(cx, 0); - let discr = C_int(discr_field.llvm_type(cx), discr as i64); - if let layout::Abi::Scalar(_) = l.abi { - Const::new(discr, t) - } else { - let discr = Const::new(discr, discr_field.ty); - build_const_struct(cx, l.for_variant(cx, variant_index), vals, Some(discr)) - } - } - layout::Variants::NicheFilling { - dataful_variant, - ref niche_variants, - niche_start, - .. - } => { - if variant_index == dataful_variant { - build_const_struct(cx, l.for_variant(cx, dataful_variant), vals, None) - } else { - let niche = l.field(cx, 0); - let niche_llty = niche.llvm_type(cx); - let niche_value = ((variant_index - niche_variants.start) as u128) - .wrapping_add(niche_start); - // FIXME(eddyb) Check the actual primitive type here. - let niche_llval = if niche_value == 0 { - // HACK(eddyb) Using `C_null` as it works on all types. - C_null(niche_llty) - } else { - C_uint_big(niche_llty, niche_value) +impl<'a, 'tcx> FunctionCx<'a, 'tcx> { + fn const_to_miri_value( + &mut self, + bx: &Builder<'a, 'tcx>, + constant: &'tcx ty::Const<'tcx>, + ) -> Result> { + match constant.val { + ConstVal::Unevaluated(def_id, ref substs) => { + let tcx = bx.tcx(); + let param_env = ty::ParamEnv::empty(traits::Reveal::All); + let instance = ty::Instance::resolve(tcx, param_env, def_id, substs).unwrap(); + let cid = GlobalId { + instance, + promoted: None, }; - build_const_struct(cx, l, &[Const::new(niche_llval, niche.ty)], None) - } + let c = tcx.const_eval(param_env.and(cid))?; + self.const_to_miri_value(bx, c) + }, + ConstVal::Value(miri_val) => Ok(miri_val), } } -} - -/// Building structs is a little complicated, because we might need to -/// insert padding if a field's value is less aligned than its type. -/// -/// Continuing the example from `trans_const_adt`, a value of type `(u32, -/// E)` should have the `E` at offset 8, but if that field's -/// initializer is 4-byte aligned then simply translating the tuple as -/// a two-element struct will locate it at offset 4, and accesses to it -/// will read the wrong memory. -fn build_const_struct<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - layout: layout::TyLayout<'tcx>, - vals: &[Const<'tcx>], - discr: Option>) - -> Const<'tcx> { - assert_eq!(vals.len(), layout.fields.count()); - match layout.abi { - layout::Abi::Scalar(_) | - layout::Abi::ScalarPair(..) | - layout::Abi::Vector { .. } if discr.is_none() => { - let mut non_zst_fields = vals.iter().enumerate().map(|(i, f)| { - (f, layout.fields.offset(i)) - }).filter(|&(f, _)| !cx.layout_of(f.ty).is_zst()); - match (non_zst_fields.next(), non_zst_fields.next()) { - (Some((x, offset)), None) if offset.bytes() == 0 => { - return Const::new(x.llval, layout.ty); - } - (Some((a, a_offset)), Some((b, _))) if a_offset.bytes() == 0 => { - return Const::new(C_struct(cx, &[a.llval, b.llval], false), layout.ty); - } - (Some((a, _)), Some((b, b_offset))) if b_offset.bytes() == 0 => { - return Const::new(C_struct(cx, &[b.llval, a.llval], false), layout.ty); - } - _ => {} + pub fn mir_constant_to_miri_value( + &mut self, + bx: &Builder<'a, 'tcx>, + constant: &mir::Constant<'tcx>, + ) -> Result> { + match constant.literal { + mir::Literal::Promoted { index } => { + let param_env = ty::ParamEnv::empty(traits::Reveal::All); + let cid = mir::interpret::GlobalId { + instance: self.instance, + promoted: Some(index), + }; + bx.tcx().const_eval(param_env.and(cid)) } - } - _ => {} - } - - // offset of current value - let mut packed = false; - let mut offset = Size::from_bytes(0); - let mut cfields = Vec::new(); - cfields.reserve(discr.is_some() as usize + 1 + layout.fields.count() * 2); - - if let Some(discr) = discr { - let (field_size, field_align) = cx.size_and_align_of(discr.ty); - packed |= layout.align.abi() < field_align.abi(); - cfields.push(discr.llval); - offset = field_size; + mir::Literal::Value { value } => { + Ok(self.monomorphize(&value)) + } + }.and_then(|c| self.const_to_miri_value(bx, c)) } - let parts = layout.fields.index_by_increasing_offset().map(|i| { - (vals[i], layout.fields.offset(i)) - }); - for (val, target_offset) in parts { - let (field_size, field_align) = cx.size_and_align_of(val.ty); - packed |= layout.align.abi() < field_align.abi(); - cfields.push(padding(cx, target_offset - offset)); - cfields.push(val.llval); - offset = target_offset + field_size; + // Old version of trans_constant now used just for SIMD shuffle + pub fn remove_me_shuffle_indices(&mut self, + bx: &Builder<'a, 'tcx>, + constant: &mir::Constant<'tcx>) + -> (ValueRef, Ty<'tcx>) + { + let layout = bx.cx.layout_of(constant.ty); + self.mir_constant_to_miri_value(bx, constant) + .and_then(|c| { + let llval = match c { + MiriValue::ByVal(val) => { + let scalar = match layout.abi { + layout::Abi::Scalar(ref x) => x, + _ => bug!("from_const: invalid ByVal layout: {:#?}", layout) + }; + primval_to_llvm(bx.cx, val, scalar, layout.immediate_llvm_type(bx.cx)) + }, + MiriValue::ByValPair(a_val, b_val) => { + let (a_scalar, b_scalar) = match layout.abi { + layout::Abi::ScalarPair(ref a, ref b) => (a, b), + _ => bug!("from_const: invalid ByValPair layout: {:#?}", layout) + }; + let a_llval = primval_to_llvm( + bx.cx, + a_val, + a_scalar, + layout.scalar_pair_element_llvm_type(bx.cx, 0), + ); + let b_llval = primval_to_llvm( + bx.cx, + b_val, + b_scalar, + layout.scalar_pair_element_llvm_type(bx.cx, 1), + ); + C_struct(bx.cx, &[a_llval, b_llval], false) + }, + MiriValue::ByRef(..) => { + let field_ty = constant.ty.builtin_index().unwrap(); + let fields = match constant.ty.sty { + ty::TyArray(_, n) => n.val.unwrap_u64(), + ref other => bug!("invalid simd shuffle type: {}", other), + }; + let values: Result, _> = (0..fields).map(|field| { + let field = const_val_field( + bx.tcx(), + ty::ParamEnv::empty(traits::Reveal::All), + self.instance, + None, + mir::Field::new(field as usize), + c, + constant.ty, + )?; + match field.val { + ConstVal::Value(MiriValue::ByVal(prim)) => { + let layout = bx.cx.layout_of(field_ty); + let scalar = match layout.abi { + layout::Abi::Scalar(ref x) => x, + _ => bug!("from_const: invalid ByVal layout: {:#?}", layout) + }; + Ok(primval_to_llvm( + bx.cx, prim, scalar, + layout.immediate_llvm_type(bx.cx), + )) + }, + other => bug!("simd shuffle field {:?}, {}", other, constant.ty), + } + }).collect(); + C_struct(bx.cx, &values?, false) + }, + }; + 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 ddd78f268fadb..237b3932ee3a1 100644 --- a/src/librustc_trans/mir/mod.rs +++ b/src/librustc_trans/mir/mod.rs @@ -42,6 +42,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, @@ -225,6 +227,7 @@ pub fn trans_mir<'a, 'tcx: 'a>( let (landing_pads, funclets) = create_funclets(&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 25db9f9b4c8a8..1c321416aaa52 100644 --- a/src/librustc_trans/mir/operand.rs +++ b/src/librustc_trans/mir/operand.rs @@ -9,13 +9,15 @@ // except according to those terms. use llvm::ValueRef; +use rustc::middle::const_val::ConstEvalErr; +use rustc::mir; +use rustc::mir::interpret::Value as MiriValue; use rustc::ty; use rustc::ty::layout::{self, Align, LayoutOf, TyLayout}; -use rustc::mir; 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; @@ -25,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 @@ -90,6 +93,70 @@ impl<'a, 'tcx> OperandRef<'tcx> { } } + pub fn from_const(bx: &Builder<'a, 'tcx>, + miri_val: MiriValue, + ty: ty::Ty<'tcx>) + -> Result, ConstEvalErr<'tcx>> { + let layout = bx.cx.layout_of(ty); + + if layout.is_zst() { + return Ok(OperandRef::new_zst(bx.cx, layout)); + } + + let val = match miri_val { + MiriValue::ByVal(x) => { + let scalar = match layout.abi { + layout::Abi::Scalar(ref x) => x, + _ => bug!("from_const: invalid ByVal layout: {:#?}", layout) + }; + let llval = primval_to_llvm( + bx.cx, + x, + scalar, + layout.immediate_llvm_type(bx.cx), + ); + OperandValue::Immediate(llval) + }, + MiriValue::ByValPair(a, b) => { + let (a_scalar, b_scalar) = match layout.abi { + layout::Abi::ScalarPair(ref a, ref b) => (a, b), + _ => bug!("from_const: invalid ByValPair layout: {:#?}", layout) + }; + let a_llval = primval_to_llvm( + bx.cx, + a, + a_scalar, + layout.scalar_pair_element_llvm_type(bx.cx, 0), + ); + let b_llval = primval_to_llvm( + bx.cx, + b, + b_scalar, + layout.scalar_pair_element_llvm_type(bx.cx, 1), + ); + OperandValue::Pair(a_llval, b_llval) + }, + MiriValue::ByRef(ptr, align) => { + let scalar = layout::Scalar { + value: layout::Primitive::Pointer, + valid_range: 0..=!0 + }; + let ptr = primval_to_llvm( + bx.cx, + ptr.into_inner_primval(), + &scalar, + layout.llvm_type(bx.cx).ptr_to(), + ); + return Ok(PlaceRef::new_sized(ptr, layout, align).load(bx)); + }, + }; + + Ok(OperandRef { + val, + layout + }) + } + /// Asserts that this operand refers to a scalar and returns /// a reference to its value. pub fn immediate(self) -> ValueRef { @@ -328,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/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index d1bc4fe90014c..38df7ce30fdd3 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -493,7 +493,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { if let mir::Place::Local(index) = *place { if let LocalRef::Operand(Some(op)) = self.locals[index] { if let ty::TyArray(_, n) = op.layout.ty.sty { - let n = n.val.to_const_int().unwrap().to_u64().unwrap(); + let n = n.val.unwrap_u64(); return common::C_usize(bx.cx, n); } } diff --git a/src/librustc_trans/trans_item.rs b/src/librustc_trans/trans_item.rs index cc270ab5f2d25..e2f623650457b 100644 --- a/src/librustc_trans/trans_item.rs +++ b/src/librustc_trans/trans_item.rs @@ -49,12 +49,7 @@ pub trait MonoItemExt<'a, 'tcx>: fmt::Debug + BaseMonoItemExt<'a, 'tcx> { let tcx = cx.tcx; let item = tcx.hir.expect_item(node_id); if let hir::ItemStatic(_, m, _) = item.node { - match consts::trans_static(&cx, m, item.id, &item.attrs) { - Ok(_) => { /* Cool, everything's alright. */ }, - Err(err) => { - err.report(tcx, item.span, "static"); - } - }; + consts::trans_static(&cx, m, item.id, &item.attrs); } else { span_bug!(item.span, "Mismatch between hir::Item type and TransItem type") } diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index d5e4aa69c5b4e..5e4786bf333f0 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -384,7 +384,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 69236d77ed328..f4a50c44663ed 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -93,6 +93,7 @@ use rustc::infer::{self, InferCtxt, InferOk, RegionVariableOrigin}; use rustc::infer::anon_types::AnonTypeDecl; use rustc::infer::type_variable::{TypeVariableOrigin}; use rustc::middle::region; +use rustc::mir::interpret::{GlobalId}; use rustc::ty::subst::{Kind, Subst, Substs}; use rustc::traits::{self, FulfillmentContext, ObligationCause, ObligationCauseCode}; use rustc::ty::{ParamTy, LvaluePreference, NoPreference, PreferMutLvalue}; @@ -3887,10 +3888,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 { @@ -3916,9 +3927,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }; if let Ok(count) = count { - let zero_or_one = count.val.to_const_int().and_then(|count| { - count.to_u64().map(|count| count <= 1) - }).unwrap_or(false); + let zero_or_one = count.val.to_u128().map_or(false, |count| count <= 1); if !zero_or_one { // For [foo, ..n] where n > 1, `foo` must have // Copy type: diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index e099d1c0c2531..79e4379756c06 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -412,10 +412,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 5485045b70438..f5a09e88eb498 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -36,6 +36,7 @@ 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::mir::interpret::{GlobalId, Value, PrimVal}; use util::nodemap::FxHashMap; use rustc_const_math::ConstInt; @@ -527,16 +528,32 @@ fn convert_enum_variant_types<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, prev_discr = Some(if let Some(e) = variant.node.disr_expr { let expr_did = tcx.hir.local_def_id(e.node_id); let substs = Substs::identity_for_item(tcx, expr_did); - let result = tcx.at(variant.span).const_eval(param_env.and((expr_did, substs))); + let instance = ty::Instance::new(expr_did, substs); + let global_id = GlobalId { + instance, + promoted: None + }; + let result = tcx.at(variant.span).const_eval(param_env.and(global_id)); // enum variant evaluation happens before the global constant check // so we need to report the real error if let Err(ref err) = result { err.report(tcx, variant.span, "enum discriminant"); - } +} match result { - Ok(&ty::Const { val: ConstVal::Integral(x), .. }) => Some(x), + Ok(&ty::Const { + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))), + .. + }) => { + use syntax::attr::IntType; + Some(match repr_type { + IntType::SignedInt(int_type) => ConstInt::new_signed( + b as i128, int_type, tcx.sess.target.isize_ty).unwrap(), + IntType::UnsignedInt(uint_type) => ConstInt::new_unsigned( + b, uint_type, tcx.sess.target.usize_ty).unwrap(), + }) + } _ => None } } else if let Some(discr) = repr_type.disr_incr(tcx, prev_discr) { diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 1dc98bc7a0072..281e4c55ee68a 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -84,6 +84,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/librustc_typeck/variance/constraints.rs b/src/librustc_typeck/variance/constraints.rs index df42d5eaa0a3d..9687cfe1357f8 100644 --- a/src/librustc_typeck/variance/constraints.rs +++ b/src/librustc_typeck/variance/constraints.rs @@ -133,7 +133,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for ConstraintContext<'a, 'tcx> { } impl<'a, 'tcx> StableHashingContextProvider for ConstraintContext<'a, 'tcx> { - type ContextType = StableHashingContext<'tcx>; + type ContextType = StableHashingContext<'a>; fn create_stable_hashing_context(&self) -> Self::ContextType { self.terms_cx.tcx.create_stable_hashing_context() diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index cc75664cacbcc..d05ef425dac7b 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -31,6 +31,7 @@ use rustc::middle::const_val::ConstVal; use rustc::middle::privacy::AccessLevels; use rustc::middle::resolve_lifetime as rl; use rustc::middle::lang_items; +use rustc::mir::interpret::GlobalId; use rustc::hir::def::{Def, CtorKind}; use rustc::hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc::ty::subst::Substs; @@ -41,7 +42,6 @@ use rustc_typeck::hir_ty_to_ty; use rustc::hir; -use rustc_const_math::ConstInt; use std::default::Default; use std::{mem, slice, vec}; use std::iter::FromIterator; @@ -2061,17 +2061,29 @@ impl Clean for hir::Ty { let def_id = cx.tcx.hir.body_owner_def_id(n); let param_env = cx.tcx.param_env(def_id); let substs = Substs::identity_for_item(cx.tcx, def_id); - let n = cx.tcx.const_eval(param_env.and((def_id, substs))).unwrap(); - 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 cid = GlobalId { + instance: ty::Instance::new(def_id, substs), + promoted: None + }; + let n = cx.tcx.const_eval(param_env.and(cid)).unwrap(); + let n = match n.val { + ConstVal::Unevaluated(def_id, _) => { + if let Some(node_id) = cx.tcx.hir.as_local_node_id(def_id) { + print_const_expr(cx, cx.tcx.hir.body_owned_by(node_id)) + } else { + inline::print_inlined_const(cx, def_id) + } + }, + ConstVal::Value(val) => { + let mut s = String::new(); + ::rustc::mir::print_miri_value(val, n.ty, &mut s).unwrap(); + // array lengths are obviously usize + if s.ends_with("usize") { + let n = s.len() - "usize".len(); + s.truncate(n); + } + s + }, }; Array(box ty.clean(cx), n) }, @@ -2191,18 +2203,30 @@ impl<'tcx> Clean for Ty<'tcx> { let mut n = cx.tcx.lift(&n).unwrap(); if let ConstVal::Unevaluated(def_id, substs) = n.val { let param_env = cx.tcx.param_env(def_id); - n = cx.tcx.const_eval(param_env.and((def_id, substs))).unwrap() + let cid = GlobalId { + instance: ty::Instance::new(def_id, substs), + promoted: None + }; + n = cx.tcx.const_eval(param_env.and(cid)).unwrap() }; - 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 = match n.val { + ConstVal::Unevaluated(def_id, _) => { + if let Some(node_id) = cx.tcx.hir.as_local_node_id(def_id) { + print_const_expr(cx, cx.tcx.hir.body_owned_by(node_id)) + } else { + inline::print_inlined_const(cx, def_id) + } + }, + ConstVal::Value(val) => { + let mut s = String::new(); + ::rustc::mir::print_miri_value(val, n.ty, &mut s).unwrap(); + // array lengths are obviously usize + if s.ends_with("usize") { + let n = s.len() - "usize".len(); + s.truncate(n); + } + s + }, }; Array(box ty.clean(cx), n) } diff --git a/src/test/codegen/consts.rs b/src/test/codegen/consts.rs index a75b8f3992d07..13c037e96cbe3 100644 --- a/src/test/codegen/consts.rs +++ b/src/test/codegen/consts.rs @@ -9,6 +9,7 @@ // except according to those terms. // compile-flags: -C no-prepopulate-passes +// ignore-tidy-linelength #![crate_type = "lib"] @@ -19,12 +20,11 @@ // CHECK: @STATIC = {{.*}}, align 4 // This checks the constants from inline_enum_const -// CHECK: @ref.{{[0-9]+}} = {{.*}}, align 2 +// CHECK: @byte_str.{{[0-9]+}} = {{.*}}, align 2 // This checks the constants from {low,high}_align_const, they share the same // constant, but the alignment differs, so the higher one should be used -// CHECK: [[LOW_HIGH:@ref.[0-9]+]] = {{.*}}, align 4 -// CHECK: [[LOW_HIGH_REF:@const.[0-9]+]] = {{.*}} [[LOW_HIGH]] +// CHECK: [[LOW_HIGH:@byte_str.[0-9]+]] = {{.*}}, align 4 #[derive(Copy, Clone)] @@ -54,7 +54,7 @@ pub fn inline_enum_const() -> E { #[no_mangle] pub fn low_align_const() -> E { // Check that low_align_const and high_align_const use the same constant -// CHECK: load {{.*}} bitcast ({ i16, [0 x i8], i16, [4 x i8] }** [[LOW_HIGH_REF]] +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %1, i8* getelementptr inbounds (<{ [8 x i8] }>, <{ [8 x i8] }>* [[LOW_HIGH]], i32 0, i32 0, i32 0), i64 8, i32 2, i1 false) *&E::A(0) } @@ -62,6 +62,6 @@ pub fn low_align_const() -> E { #[no_mangle] pub fn high_align_const() -> E { // Check that low_align_const and high_align_const use the same constant -// CHECK: load {{.*}} bitcast ({ i16, [0 x i8], i16, [4 x i8] }** [[LOW_HIGH_REF]] +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %1, i8* getelementptr inbounds (<{ [8 x i8] }>, <{ [8 x i8] }>* [[LOW_HIGH]], i32 0, i32 0, i32 0), i64 8, i32 4, i1 false) *&E::A(0) } diff --git a/src/test/codegen/link_section.rs b/src/test/codegen/link_section.rs index 1879002e7f3d7..9c56a316b341c 100644 --- a/src/test/codegen/link_section.rs +++ b/src/test/codegen/link_section.rs @@ -12,7 +12,7 @@ #![crate_type = "lib"] -// CHECK: @VAR1 = constant i32 1, section ".test_one" +// CHECK: @VAR1 = constant <{ [4 x i8] }> <{ [4 x i8] c"\01\00\00\00" }>, section ".test_one" #[no_mangle] #[link_section = ".test_one"] pub static VAR1: u32 = 1; diff --git a/src/test/codegen/remap_path_prefix/main.rs b/src/test/codegen/remap_path_prefix/main.rs index ea0c9ad2b8324..c5aa00e7b8673 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/E0030.rs b/src/test/compile-fail/E0030.rs index ef3bded4beffb..896dd2599bf53 100644 --- a/src/test/compile-fail/E0030.rs +++ b/src/test/compile-fail/E0030.rs @@ -10,7 +10,7 @@ fn main() { - match 5u32 { + match 5u32 { //~ ERROR non-exhaustive patterns 1000 ... 5 => {} //~^ ERROR lower range bound must be less than or equal to upper } diff --git a/src/test/compile-fail/E0080.rs b/src/test/compile-fail/E0080.rs index 2f199c48e46e7..6e525d4ea9ec7 100644 --- a/src/test/compile-fail/E0080.rs +++ b/src/test/compile-fail/E0080.rs @@ -10,9 +10,10 @@ enum Enum { X = (1 << 500), //~ ERROR E0080 - //~| WARNING shift left with overflow + //~| shift left with overflow Y = (1 / 0) //~ ERROR E0080 - //~| WARNING divide by zero + //~| const_err + //~| divide by zero } fn main() { diff --git a/src/test/compile-fail/const-call.rs b/src/test/compile-fail/const-call.rs index 18476494300b2..02264228a6bf6 100644 --- a/src/test/compile-fail/const-call.rs +++ b/src/test/compile-fail/const-call.rs @@ -15,4 +15,5 @@ fn f(x: usize) -> usize { fn main() { let _ = [0; f(2)]; //~^ ERROR calls in constants are limited to constant functions + //~| E0080 } diff --git a/src/test/compile-fail/const-err-early.rs b/src/test/compile-fail/const-err-early.rs index 42fb40394fb29..8c2e6233abfe2 100644 --- a/src/test/compile-fail/const-err-early.rs +++ b/src/test/compile-fail/const-err-early.rs @@ -11,14 +11,15 @@ #![feature(const_indexing)] #![deny(const_err)] -pub const A: i8 = -std::i8::MIN; //~ ERROR attempt to negate with overflow -pub const B: u8 = 200u8 + 200u8; //~ ERROR attempt to add with overflow -pub const C: u8 = 200u8 * 4; //~ ERROR attempt to multiply with overflow -pub const D: u8 = 42u8 - (42u8 + 1); //~ ERROR attempt to subtract with overflow +pub const A: i8 = -std::i8::MIN; //~ ERROR E0080 +//~^ ERROR attempt to negate with overflow +//~| ERROR const_err +pub const B: u8 = 200u8 + 200u8; //~ ERROR E0080 +pub const C: u8 = 200u8 * 4; //~ ERROR E0080 +pub const D: u8 = 42u8 - (42u8 + 1); //~ ERROR E0080 pub const E: u8 = [5u8][1]; -//~^ ERROR index out of bounds: the len is 1 but the index is 1 +//~^ ERROR E0080 fn main() { let _e = [6u8][1]; - //~^ ERROR index out of bounds: the len is 1 but the index is 1 } diff --git a/src/test/compile-fail/const-err-multi.rs b/src/test/compile-fail/const-err-multi.rs index d4f9c0fe56dae..668f95f2c8d73 100644 --- a/src/test/compile-fail/const-err-multi.rs +++ b/src/test/compile-fail/const-err-multi.rs @@ -11,9 +11,14 @@ #![deny(const_err)] pub const A: i8 = -std::i8::MIN; //~ ERROR attempt to negate with overflow +//~^ ERROR E0080 +//~| ERROR const_err pub const B: i8 = A; +//~^ ERROR E0080 pub const C: u8 = A as u8; +//~^ ERROR E0080 pub const D: i8 = 50 - A; +//~^ ERROR E0080 fn main() { } diff --git a/src/test/compile-fail/const-err.rs b/src/test/compile-fail/const-err.rs index e65194ab56f98..8bd759b6d3735 100644 --- a/src/test/compile-fail/const-err.rs +++ b/src/test/compile-fail/const-err.rs @@ -26,26 +26,5 @@ const FOO: u8 = [5u8][1]; //~| index out of bounds: the len is 1 but the index is 1 fn main() { - let a = -std::i8::MIN; - //~^ WARN this expression will panic at run-time - //~| attempt to negate with overflow - let b = 200u8 + 200u8 + 200u8; - //~^ WARN this expression will panic at run-time - //~^^ WARN this expression will panic at run-time - //~| attempt to add with overflow - let c = 200u8 * 4; - //~^ WARN this expression will panic at run-time - //~| attempt to multiply with overflow - let d = 42u8 - (42u8 + 1); - //~^ WARN this expression will panic at run-time - //~| attempt to subtract with overflow - let _e = [5u8][1]; - //~^ WARN this expression will panic at run-time - //~| index out of bounds: the len is 1 but the index is 1 - black_box(a); - black_box(b); - black_box(c); - black_box(d); - black_box((FOO, FOO)); } diff --git a/src/test/compile-fail/const-err2.rs b/src/test/compile-fail/const-err2.rs index 9889ca1392ac1..09983f179266d 100644 --- a/src/test/compile-fail/const-err2.rs +++ b/src/test/compile-fail/const-err2.rs @@ -18,13 +18,13 @@ fn black_box(_: T) { fn main() { let a = -std::i8::MIN; - //~^ ERROR attempt to negate with overflow + //~^ ERROR const_err let b = 200u8 + 200u8 + 200u8; - //~^ ERROR attempt to add with overflow + //~^ ERROR const_err let c = 200u8 * 4; - //~^ ERROR attempt to multiply with overflow + //~^ ERROR const_err let d = 42u8 - (42u8 + 1); - //~^ ERROR attempt to subtract with overflow + //~^ ERROR const_err let _e = [5u8][1]; black_box(a); black_box(b); diff --git a/src/test/compile-fail/const-err3.rs b/src/test/compile-fail/const-err3.rs new file mode 100644 index 0000000000000..9656af6002442 --- /dev/null +++ b/src/test/compile-fail/const-err3.rs @@ -0,0 +1,29 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_attrs)] +#![deny(const_err)] + +fn black_box(_: T) { + unimplemented!() +} + +fn main() { + let b = 200u8 + 200u8 + 200u8; + //~^ ERROR const_err + let c = 200u8 * 4; + //~^ ERROR const_err + let d = 42u8 - (42u8 + 1); + //~^ ERROR const_err + let _e = [5u8][1]; + black_box(b); + black_box(c); + black_box(d); +} diff --git a/src/test/compile-fail/const-eval-overflow.rs b/src/test/compile-fail/const-eval-overflow.rs deleted file mode 100644 index 058a8d0a1bd4f..0000000000000 --- a/src/test/compile-fail/const-eval-overflow.rs +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(unused_imports)] - -// Note: the relevant lint pass here runs before some of the constant -// evaluation below (e.g. that performed by trans and llvm), so if you -// change this warn to a deny, then the compiler will exit before -// those errors are detected. - -#![warn(const_err)] - -use std::fmt; -use std::{i8, i16, i32, i64, isize}; -use std::{u8, u16, u32, u64, usize}; - -const VALS_I8: (i8, i8, i8, i8) = - (-i8::MIN, - //~^ ERROR constant evaluation error - //~| attempt to negate with overflow - i8::MIN - 1, - //~^ ERROR constant evaluation error - //~| attempt to subtract with overflow - i8::MAX + 1, - //~^ ERROR constant evaluation error - //~| attempt to add with overflow - i8::MIN * 2, - //~^ ERROR constant evaluation error - //~| attempt to multiply with overflow - ); - -const VALS_I16: (i16, i16, i16, i16) = - (-i16::MIN, - //~^ ERROR constant evaluation error - //~| attempt to negate with overflow - i16::MIN - 1, - //~^ ERROR constant evaluation error - //~| attempt to subtract with overflow - i16::MAX + 1, - //~^ ERROR constant evaluation error - //~| attempt to add with overflow - i16::MIN * 2, - //~^ ERROR constant evaluation error - //~| attempt to multiply with overflow - ); - -const VALS_I32: (i32, i32, i32, i32) = - (-i32::MIN, - //~^ ERROR constant evaluation error - //~| attempt to negate with overflow - i32::MIN - 1, - //~^ ERROR constant evaluation error - //~| attempt to subtract with overflow - i32::MAX + 1, - //~^ ERROR constant evaluation error - //~| attempt to add with overflow - i32::MIN * 2, - //~^ ERROR constant evaluation error - //~| attempt to multiply with overflow - ); - -const VALS_I64: (i64, i64, i64, i64) = - (-i64::MIN, - //~^ ERROR constant evaluation error - //~| attempt to negate with overflow - i64::MIN - 1, - //~^ ERROR constant evaluation error - //~| attempt to subtract with overflow - i64::MAX + 1, - //~^ ERROR constant evaluation error - //~| attempt to add with overflow - i64::MAX * 2, - //~^ ERROR constant evaluation error - //~| attempt to multiply with overflow - ); - -const VALS_U8: (u8, u8, u8, u8) = - ( //~ WARN constant evaluation error: attempt to subtract with overflow - -(u8::MIN as i8) as u8, - u8::MIN - 1, - //~^ ERROR constant evaluation error - //~| attempt to subtract with overflow - u8::MAX + 1, - //~^ ERROR constant evaluation error - //~| attempt to add with overflow - u8::MAX * 2, - //~^ ERROR constant evaluation error - //~| attempt to multiply with overflow - ); - -const VALS_U16: (u16, u16, u16, u16) = - ( //~ WARN constant evaluation error: attempt to subtract with overflow - -(u16::MIN as i16) as u16, - u16::MIN - 1, - //~^ ERROR constant evaluation error - //~| attempt to subtract with overflow - u16::MAX + 1, - //~^ ERROR constant evaluation error - //~| attempt to add with overflow - u16::MAX * 2, - //~^ ERROR constant evaluation error - //~| attempt to multiply with overflow - ); - -const VALS_U32: (u32, u32, u32, u32) = - ( //~ WARN constant evaluation error: attempt to subtract with overflow - -(u32::MIN as i32) as u32, - u32::MIN - 1, - //~^ ERROR constant evaluation error - //~| attempt to subtract with overflow - u32::MAX + 1, - //~^ ERROR constant evaluation error - //~| attempt to add with overflow - u32::MAX * 2, - //~^ ERROR constant evaluation error - //~| attempt to multiply with overflow - ); - -const VALS_U64: (u64, u64, u64, u64) = - ( //~ WARN constant evaluation error: attempt to subtract with overflow - -(u64::MIN as i64) as u64, - u64::MIN - 1, - //~^ ERROR constant evaluation error - //~| attempt to subtract with overflow - u64::MAX + 1, - //~^ ERROR constant evaluation error - //~| attempt to add with overflow - u64::MAX * 2, - //~^ ERROR constant evaluation error - //~| attempt to multiply with overflow - ); - -fn main() { - foo(VALS_I8); - foo(VALS_I16); - foo(VALS_I32); - foo(VALS_I64); - - foo(VALS_U8); - foo(VALS_U16); - foo(VALS_U32); - foo(VALS_U64); -} - -fn foo(x: T) { - println!("{:?}", x); -} diff --git a/src/test/compile-fail/const-eval-overflow2.rs b/src/test/compile-fail/const-eval-overflow2.rs new file mode 100644 index 0000000000000..61a653589fff9 --- /dev/null +++ b/src/test/compile-fail/const-eval-overflow2.rs @@ -0,0 +1,91 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_imports)] + +// Note: the relevant lint pass here runs before some of the constant +// evaluation below (e.g. that performed by trans and llvm), so if you +// change this warn to a deny, then the compiler will exit before +// those errors are detected. + +#![deny(const_err)] + +use std::fmt; +use std::{i8, i16, i32, i64, isize}; +use std::{u8, u16, u32, u64, usize}; + +const VALS_I8: (i8,) = + ( + i8::MIN - 1, + //~^ ERROR constant evaluation error + //~| attempt to subtract with overflow + ); + +const VALS_I16: (i16,) = + ( + i16::MIN - 1, + //~^ ERROR constant evaluation error + //~| attempt to subtract with overflow + ); + +const VALS_I32: (i32,) = + ( + i32::MIN - 1, + //~^ ERROR constant evaluation error + //~| attempt to subtract with overflow + ); + +const VALS_I64: (i64,) = + ( + i64::MIN - 1, + //~^ ERROR constant evaluation error + //~| attempt to subtract with overflow + ); + +const VALS_U8: (u8,) = + ( + u8::MIN - 1, + //~^ ERROR constant evaluation error + //~| attempt to subtract with overflow + ); + +const VALS_U16: (u16,) = ( + u16::MIN - 1, + //~^ ERROR constant evaluation error + //~| attempt to subtract with overflow + ); + +const VALS_U32: (u32,) = ( + u32::MIN - 1, + //~^ ERROR constant evaluation error + //~| attempt to subtract with overflow + ); + +const VALS_U64: (u64,) = + ( + u64::MIN - 1, + //~^ ERROR constant evaluation error + //~| attempt to subtract with overflow + ); + +fn main() { + foo(VALS_I8); + foo(VALS_I16); + foo(VALS_I32); + foo(VALS_I64); + + foo(VALS_U8); + foo(VALS_U16); + foo(VALS_U32); + foo(VALS_U64); +} + +fn foo(_: T) { +} diff --git a/src/test/compile-fail/const-eval-overflow2b.rs b/src/test/compile-fail/const-eval-overflow2b.rs new file mode 100644 index 0000000000000..b2e84486101bc --- /dev/null +++ b/src/test/compile-fail/const-eval-overflow2b.rs @@ -0,0 +1,91 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_imports)] + +// Note: the relevant lint pass here runs before some of the constant +// evaluation below (e.g. that performed by trans and llvm), so if you +// change this warn to a deny, then the compiler will exit before +// those errors are detected. + +#![deny(const_err)] + +use std::fmt; +use std::{i8, i16, i32, i64, isize}; +use std::{u8, u16, u32, u64, usize}; + +const VALS_I8: (i8,) = + ( + i8::MAX + 1, + //~^ ERROR constant evaluation error + //~| attempt to add with overflow + ); + +const VALS_I16: (i16,) = + ( + i16::MAX + 1, + //~^ ERROR constant evaluation error + //~| attempt to add with overflow + ); + +const VALS_I32: (i32,) = + ( + i32::MAX + 1, + //~^ ERROR constant evaluation error + //~| attempt to add with overflow + ); + +const VALS_I64: (i64,) = + ( + i64::MAX + 1, + //~^ ERROR constant evaluation error + //~| attempt to add with overflow + ); + +const VALS_U8: (u8,) = + ( + u8::MAX + 1, + //~^ ERROR constant evaluation error + //~| attempt to add with overflow + ); + +const VALS_U16: (u16,) = ( + u16::MAX + 1, + //~^ ERROR constant evaluation error + //~| attempt to add with overflow + ); + +const VALS_U32: (u32,) = ( + u32::MAX + 1, + //~^ ERROR constant evaluation error + //~| attempt to add with overflow + ); + +const VALS_U64: (u64,) = + ( + u64::MAX + 1, + //~^ ERROR constant evaluation error + //~| attempt to add with overflow + ); + +fn main() { + foo(VALS_I8); + foo(VALS_I16); + foo(VALS_I32); + foo(VALS_I64); + + foo(VALS_U8); + foo(VALS_U16); + foo(VALS_U32); + foo(VALS_U64); +} + +fn foo(_: T) { +} diff --git a/src/test/compile-fail/const-eval-overflow2c.rs b/src/test/compile-fail/const-eval-overflow2c.rs new file mode 100644 index 0000000000000..c4a8beaf0f057 --- /dev/null +++ b/src/test/compile-fail/const-eval-overflow2c.rs @@ -0,0 +1,91 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_imports)] + +// Note: the relevant lint pass here runs before some of the constant +// evaluation below (e.g. that performed by trans and llvm), so if you +// change this warn to a deny, then the compiler will exit before +// those errors are detected. + +#![deny(const_err)] + +use std::fmt; +use std::{i8, i16, i32, i64, isize}; +use std::{u8, u16, u32, u64, usize}; + +const VALS_I8: (i8,) = + ( + i8::MIN * 2, + //~^ ERROR constant evaluation error + //~| attempt to multiply with overflow + ); + +const VALS_I16: (i16,) = + ( + i16::MIN * 2, + //~^ ERROR constant evaluation error + //~| attempt to multiply with overflow + ); + +const VALS_I32: (i32,) = + ( + i32::MIN * 2, + //~^ ERROR constant evaluation error + //~| attempt to multiply with overflow + ); + +const VALS_I64: (i64,) = + ( + i64::MIN * 2, + //~^ ERROR constant evaluation error + //~| attempt to multiply with overflow + ); + +const VALS_U8: (u8,) = + ( + u8::MAX * 2, + //~^ ERROR constant evaluation error + //~| attempt to multiply with overflow + ); + +const VALS_U16: (u16,) = ( + u16::MAX * 2, + //~^ ERROR constant evaluation error + //~| attempt to multiply with overflow + ); + +const VALS_U32: (u32,) = ( + u32::MAX * 2, + //~^ ERROR constant evaluation error + //~| attempt to multiply with overflow + ); + +const VALS_U64: (u64,) = + ( + u64::MAX * 2, + //~^ ERROR constant evaluation error + //~| attempt to multiply with overflow + ); + +fn main() { + foo(VALS_I8); + foo(VALS_I16); + foo(VALS_I32); + foo(VALS_I64); + + foo(VALS_U8); + foo(VALS_U16); + foo(VALS_U32); + foo(VALS_U64); +} + +fn foo(_: T) { +} diff --git a/src/test/compile-fail/const-integer-bool-ops.rs b/src/test/compile-fail/const-integer-bool-ops.rs index 29bc665a22e7b..3065122af6a94 100644 --- a/src/test/compile-fail/const-integer-bool-ops.rs +++ b/src/test/compile-fail/const-integer-bool-ops.rs @@ -16,6 +16,7 @@ const X: usize = 42 && 39; //~| ERROR mismatched types //~| expected usize, found bool const ARR: [i32; X] = [99; 34]; +//~^ ERROR constant evaluation error const X1: usize = 42 || 39; //~^ ERROR mismatched types @@ -25,6 +26,7 @@ const X1: usize = 42 || 39; //~| ERROR mismatched types //~| expected usize, found bool const ARR1: [i32; X1] = [99; 47]; +//~^ ERROR constant evaluation error const X2: usize = -42 || -39; //~^ ERROR mismatched types @@ -34,6 +36,7 @@ const X2: usize = -42 || -39; //~| ERROR mismatched types //~| expected usize, found bool const ARR2: [i32; X2] = [99; 18446744073709551607]; +//~^ ERROR constant evaluation error const X3: usize = -42 && -39; //~^ ERROR mismatched types @@ -43,36 +46,43 @@ const X3: usize = -42 && -39; //~| ERROR mismatched types //~| expected usize, found bool const ARR3: [i32; X3] = [99; 6]; +//~^ ERROR constant evaluation error const Y: usize = 42.0 == 42.0; //~^ ERROR mismatched types //~| expected usize, found bool const ARRR: [i32; Y] = [99; 1]; +//~^ ERROR constant evaluation error const Y1: usize = 42.0 >= 42.0; //~^ ERROR mismatched types //~| expected usize, found bool const ARRR1: [i32; Y1] = [99; 1]; +//~^ ERROR constant evaluation error const Y2: usize = 42.0 <= 42.0; //~^ ERROR mismatched types //~| expected usize, found bool const ARRR2: [i32; Y2] = [99; 1]; +//~^ ERROR constant evaluation error const Y3: usize = 42.0 > 42.0; //~^ ERROR mismatched types //~| expected usize, found bool const ARRR3: [i32; Y3] = [99; 0]; +//~^ ERROR constant evaluation error const Y4: usize = 42.0 < 42.0; //~^ ERROR mismatched types //~| expected usize, found bool const ARRR4: [i32; Y4] = [99; 0]; +//~^ ERROR constant evaluation error const Y5: usize = 42.0 != 42.0; //~^ ERROR mismatched types //~| expected usize, found bool const ARRR5: [i32; Y5] = [99; 0]; +//~^ ERROR constant evaluation error fn main() { let _ = ARR; diff --git a/src/test/compile-fail/const-len-underflow-subspans.rs b/src/test/compile-fail/const-len-underflow-subspans.rs index 7f2229b5a6534..85cc893aa133c 100644 --- a/src/test/compile-fail/const-len-underflow-subspans.rs +++ b/src/test/compile-fail/const-len-underflow-subspans.rs @@ -16,6 +16,6 @@ const TWO: usize = 2; fn main() { let a: [i8; ONE - TWO] = unimplemented!(); - //~^ ERROR constant evaluation error [E0080] + //~^ ERROR constant evaluation error //~| attempt to subtract with overflow } diff --git a/src/test/compile-fail/const-size_of-cycle.rs b/src/test/compile-fail/const-size_of-cycle.rs index cbeafdfe6acc9..9720d3809120c 100644 --- a/src/test/compile-fail/const-size_of-cycle.rs +++ b/src/test/compile-fail/const-size_of-cycle.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern: unsupported cyclic reference between types/traits detected +//error-pattern: cyclic reference #![feature(const_fn)] diff --git a/src/test/compile-fail/const-slice-oob.rs b/src/test/compile-fail/const-slice-oob.rs index b1b4bfe2d1c39..179ea9e853f3a 100644 --- a/src/test/compile-fail/const-slice-oob.rs +++ b/src/test/compile-fail/const-slice-oob.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[deny(const_err)] + const FOO: &'static[u32] = &[1, 2, 3]; const BAR: u32 = FOO[5]; //~^ ERROR constant evaluation error [E0080] diff --git a/src/test/compile-fail/const-tup-index-span.rs b/src/test/compile-fail/const-tup-index-span.rs index b42c440f87d74..7596881ef9b9a 100644 --- a/src/test/compile-fail/const-tup-index-span.rs +++ b/src/test/compile-fail/const-tup-index-span.rs @@ -14,6 +14,7 @@ const TUP: (usize,) = 5usize << 64; //~^ ERROR mismatched types //~| expected tuple, found usize const ARR: [i32; TUP.0] = []; +//~^ ERROR constant evaluation error fn main() { } diff --git a/src/test/compile-fail/eval-enum.rs b/src/test/compile-fail/eval-enum.rs index 86cc2c144ac07..49f76c532df54 100644 --- a/src/test/compile-fail/eval-enum.rs +++ b/src/test/compile-fail/eval-enum.rs @@ -8,12 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -enum test { - div_zero = 1/0, //~ ERROR E0080 - //~| attempt to divide by zero - rem_zero = 1%0, - //~^ ERROR E0080 - //~| attempt to calculate the remainder with a divisor of zero +enum Test { + DivZero = 1/0, + //~^ attempt to divide by zero + //~| ERROR constant evaluation error + //~| WARN constant evaluation error + RemZero = 1%0, + //~^ attempt to calculate the remainder with a divisor of zero + //~| ERROR constant evaluation error + //~| WARN constant evaluation error } fn main() {} diff --git a/src/test/compile-fail/float-int-invalid-const-cast.rs b/src/test/compile-fail/float-int-invalid-const-cast.rs deleted file mode 100644 index 1f07422e21be4..0000000000000 --- a/src/test/compile-fail/float-int-invalid-const-cast.rs +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![feature(i128_type)] -#![allow(const_err)] // this test is only about hard errors - -use std::{f32, f64}; - -// Forces evaluation of constants, triggering hard error -fn force(_: T) {} - -fn main() { - { const X: u16 = -1. as u16; force(X); } //~ ERROR constant evaluation error - { const X: u128 = -100. as u128; force(X); } //~ ERROR constant evaluation error - - { const X: i8 = f32::NAN as i8; force(X); } //~ ERROR constant evaluation error - { const X: i32 = f32::NAN as i32; force(X); } //~ ERROR constant evaluation error - { const X: u64 = f32::NAN as u64; force(X); } //~ ERROR constant evaluation error - { const X: u128 = f32::NAN as u128; force(X); } //~ ERROR constant evaluation error - - { const X: i8 = f32::INFINITY as i8; force(X); } //~ ERROR constant evaluation error - { const X: u32 = f32::INFINITY as u32; force(X); } //~ ERROR constant evaluation error - { const X: i128 = f32::INFINITY as i128; force(X); } //~ ERROR constant evaluation error - { const X: u128 = f32::INFINITY as u128; force(X); } //~ ERROR constant evaluation error - - { const X: u8 = f32::NEG_INFINITY as u8; force(X); } //~ ERROR constant evaluation error - { const X: u16 = f32::NEG_INFINITY as u16; force(X); } //~ ERROR constant evaluation error - { const X: i64 = f32::NEG_INFINITY as i64; force(X); } //~ ERROR constant evaluation error - { const X: i128 = f32::NEG_INFINITY as i128; force(X); } //~ ERROR constant evaluation error - - { const X: i8 = f64::NAN as i8; force(X); } //~ ERROR constant evaluation error - { const X: i32 = f64::NAN as i32; force(X); } //~ ERROR constant evaluation error - { const X: u64 = f64::NAN as u64; force(X); } //~ ERROR constant evaluation error - { const X: u128 = f64::NAN as u128; force(X); } //~ ERROR constant evaluation error - - { const X: i8 = f64::INFINITY as i8; force(X); } //~ ERROR constant evaluation error - { const X: u32 = f64::INFINITY as u32; force(X); } //~ ERROR constant evaluation error - { const X: i128 = f64::INFINITY as i128; force(X); } //~ ERROR constant evaluation error - { const X: u128 = f64::INFINITY as u128; force(X); } //~ ERROR constant evaluation error - - { const X: u8 = f64::NEG_INFINITY as u8; force(X); } //~ ERROR constant evaluation error - { const X: u16 = f64::NEG_INFINITY as u16; force(X); } //~ ERROR constant evaluation error - { const X: i64 = f64::NEG_INFINITY as i64; force(X); } //~ ERROR constant evaluation error - { const X: i128 = f64::NEG_INFINITY as i128; force(X); } //~ ERROR constant evaluation error - - { const X: u8 = 256. as u8; force(X); } //~ ERROR constant evaluation error - { const X: i8 = -129. as i8; force(X); } //~ ERROR constant evaluation error - { const X: i8 = 128. as i8; force(X); } //~ ERROR constant evaluation error - { const X: i32 = 2147483648. as i32; force(X); } //~ ERROR constant evaluation error - { const X: i32 = -2147483904. as i32; force(X); } //~ ERROR constant evaluation error - { const X: u32 = 4294967296. as u32; force(X); } //~ ERROR constant evaluation error - { const X: u128 = 1e40 as u128; force(X); } //~ ERROR constant evaluation error - { const X: i128 = 1e40 as i128; force(X); } //~ ERROR constant evaluation error -} diff --git a/src/test/compile-fail/issue-27895.rs b/src/test/compile-fail/issue-27895.rs index ca8d5a1f70473..be76796c5c465 100644 --- a/src/test/compile-fail/issue-27895.rs +++ b/src/test/compile-fail/issue-27895.rs @@ -14,8 +14,7 @@ fn main() { match i { 0...index => println!("winner"), - //~^ ERROR constant evaluation error - //~| non-constant path in constant expression + //~^ ERROR runtime values cannot be referenced in patterns _ => println!("hello"), } } diff --git a/src/test/compile-fail/issue-31109.rs b/src/test/compile-fail/issue-31109.rs index e3548d740717f..74121e3a420f9 100644 --- a/src/test/compile-fail/issue-31109.rs +++ b/src/test/compile-fail/issue-31109.rs @@ -12,6 +12,5 @@ fn main() { // FIXME(#31407) this error should go away, but in the meantime we test that it // is accompanied by a somewhat useful error message. let _: f64 = 1234567890123456789012345678901234567890e-340; - //~^ ERROR constant evaluation error - //~| unimplemented constant expression: could not evaluate float literal + //~^ ERROR could not evaluate float literal (see issue #31407) } diff --git a/src/test/compile-fail/issue-39559-2.rs b/src/test/compile-fail/issue-39559-2.rs index aa0750230649d..f01fd1fd8f144 100644 --- a/src/test/compile-fail/issue-39559-2.rs +++ b/src/test/compile-fail/issue-39559-2.rs @@ -22,7 +22,9 @@ impl Dim for Dim3 { fn main() { let array: [usize; Dim3::dim()] - //~^ ERROR calls in constants are limited to constant functions + //~^ ERROR E0015 + //~| ERROR E0080 = [0; Dim3::dim()]; - //~^ ERROR calls in constants are limited to constant functions + //~^ ERROR E0015 + //~| ERROR E0080 } diff --git a/src/test/compile-fail/issue-41255.rs b/src/test/compile-fail/issue-41255.rs index 191b867e7a8b5..ac85e43cf4f05 100644 --- a/src/test/compile-fail/issue-41255.rs +++ b/src/test/compile-fail/issue-41255.rs @@ -18,33 +18,33 @@ fn main() { let x = 42.0; match x { - 5.0 => {}, //~ ERROR floating-point 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 fb419d751b4e8..c0af3b4b9e695 100644 --- a/src/test/compile-fail/issue-43105.rs +++ b/src/test/compile-fail/issue-43105.rs @@ -12,6 +12,7 @@ fn xyz() -> u8 { 42 } const NUM: u8 = xyz(); //~^ ERROR calls in constants are limited to constant functions, struct and enum constructors +//~| ERROR constant evaluation error fn main() { match 1 { diff --git a/src/test/compile-fail/issue-44578.rs b/src/test/compile-fail/issue-44578.rs index a6ae21c3b5497..9efe3d90a93c5 100644 --- a/src/test/compile-fail/issue-44578.rs +++ b/src/test/compile-fail/issue-44578.rs @@ -18,7 +18,7 @@ enum Bar { } impl Foo for Bar { - const AMT: usize = [A::AMT][(A::AMT > B::AMT) as usize]; //~ ERROR constant evaluation + const AMT: usize = [A::AMT][(A::AMT > B::AMT) as usize]; //~ E0080 } impl Foo for u8 { @@ -30,5 +30,5 @@ impl Foo for u16 { } fn main() { - println!("{}", as Foo>::AMT); + println!("{}", as Foo>::AMT); //~ E0080 } diff --git a/src/test/compile-fail/issue-6804.rs b/src/test/compile-fail/issue-6804.rs index 2a97945f26648..9191dfa155c65 100644 --- a/src/test/compile-fail/issue-6804.rs +++ b/src/test/compile-fail/issue-6804.rs @@ -12,18 +12,21 @@ #![feature(slice_patterns)] #![allow(unused)] +#![deny(illegal_floating_point_literal_pattern)] use std::f64::NAN; fn main() { let x = NAN; match x { - NAN => {}, //~ ERROR floating point constants cannot be used + NAN => {}, //~ ERROR floating-point types cannot be used + //~^ WARN this was previously accepted by the compiler but is being phased out _ => {}, }; match [x, 1.0] { - [NAN, _] => {}, //~ ERROR floating point constants cannot be used + [NAN, _] => {}, //~ ERROR floating-point types cannot be used + //~^ WARN this was previously accepted by the compiler but is being phased out _ => {}, }; } diff --git a/src/test/compile-fail/issue-8460-const.rs b/src/test/compile-fail/issue-8460-const.rs index d8ab48d1ec3e6..1d59e75a0f0f0 100644 --- a/src/test/compile-fail/issue-8460-const.rs +++ b/src/test/compile-fail/issue-8460-const.rs @@ -16,42 +16,62 @@ use std::thread; fn main() { assert!(thread::spawn(move|| { isize::MIN / -1; }).join().is_err()); //~^ ERROR attempt to divide with overflow + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i8::MIN / -1; }).join().is_err()); //~^ ERROR attempt to divide with overflow + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i16::MIN / -1; }).join().is_err()); //~^ ERROR attempt to divide with overflow + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i32::MIN / -1; }).join().is_err()); //~^ ERROR attempt to divide with overflow + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i64::MIN / -1; }).join().is_err()); //~^ ERROR attempt to divide with overflow + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1isize / 0; }).join().is_err()); //~^ ERROR attempt to divide by zero + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i8 / 0; }).join().is_err()); //~^ ERROR attempt to divide by zero + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i16 / 0; }).join().is_err()); //~^ ERROR attempt to divide by zero + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i32 / 0; }).join().is_err()); //~^ ERROR attempt to divide by zero + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i64 / 0; }).join().is_err()); //~^ ERROR attempt to divide by zero + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with overflow + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with overflow + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with overflow + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with overflow + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with overflow + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1isize % 0; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with a divisor of zero + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i8 % 0; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with a divisor of zero + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i16 % 0; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with a divisor of zero + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i32 % 0; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with a divisor of zero + //~| ERROR constant evaluation error assert!(thread::spawn(move|| { 1i64 % 0; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with a divisor of zero + //~| ERROR constant evaluation error } diff --git a/src/test/compile-fail/lint-exceeding-bitshifts.rs b/src/test/compile-fail/lint-exceeding-bitshifts.rs index 3e51550d1fa07..2634157c80671 100644 --- a/src/test/compile-fail/lint-exceeding-bitshifts.rs +++ b/src/test/compile-fail/lint-exceeding-bitshifts.rs @@ -10,7 +10,7 @@ #![deny(exceeding_bitshifts)] #![allow(unused_variables)] -#![allow(dead_code)] +#![allow(dead_code, const_err)] #![feature(const_indexing)] fn main() { @@ -53,22 +53,6 @@ fn main() { let n = n << 8; //~ ERROR: bitshift exceeds the type's number of bits let n = 1u8 << -8; //~ ERROR: bitshift exceeds the type's number of bits - //~^ WARN: attempt to shift by a negative amount - - let n = 1u8 << (4+3); - let n = 1u8 << (4+4); //~ ERROR: bitshift exceeds the type's number of bits - - #[cfg(target_pointer_width = "32")] - const BITS: usize = 32; - #[cfg(target_pointer_width = "64")] - const BITS: usize = 64; - - let n = 1_isize << BITS; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1_usize << BITS; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1i8<<(1isize+-1); - - let n = 1i64 >> [63][0]; - let n = 1i64 >> [64][0]; //~ ERROR: bitshift exceeds the type's number of bits } diff --git a/src/test/compile-fail/lint-exceeding-bitshifts2.rs b/src/test/compile-fail/lint-exceeding-bitshifts2.rs new file mode 100644 index 0000000000000..d95e1b370aafd --- /dev/null +++ b/src/test/compile-fail/lint-exceeding-bitshifts2.rs @@ -0,0 +1,27 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![deny(exceeding_bitshifts)] +#![allow(unused_variables, const_err)] +#![allow(dead_code)] + +fn main() { + let n = 1u8 << (4+3); + let n = 1u8 << (4+4); //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i64 >> [63][0]; + let n = 1i64 >> [64][0]; // should be linting, needs to wait for const propagation + + #[cfg(target_pointer_width = "32")] + const BITS: usize = 32; + #[cfg(target_pointer_width = "64")] + const BITS: usize = 64; + let n = 1_isize << BITS; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1_usize << BITS; //~ ERROR: bitshift exceeds the type's number of bits +} diff --git a/src/test/compile-fail/lint-type-overflow2.rs b/src/test/compile-fail/lint-type-overflow2.rs index 5593499758346..9178d8d2f6ed2 100644 --- a/src/test/compile-fail/lint-type-overflow2.rs +++ b/src/test/compile-fail/lint-type-overflow2.rs @@ -17,7 +17,7 @@ #[rustc_error] fn main() { //~ ERROR: compilation successful let x2: i8 = --128; //~ warn: literal out of range for i8 - //~^ warn: attempt to negate with overflow + //~^ WARN constant evaluation error let x = -3.40282357e+38_f32; //~ warn: literal out of range for f32 let x = 3.40282357e+38_f32; //~ warn: literal out of range for f32 diff --git a/src/test/compile-fail/non-constant-in-const-path.rs b/src/test/compile-fail/non-constant-in-const-path.rs index 737f80372debf..fe88ab4e11798 100644 --- a/src/test/compile-fail/non-constant-in-const-path.rs +++ b/src/test/compile-fail/non-constant-in-const-path.rs @@ -12,7 +12,6 @@ fn main() { let x = 0; match 1 { 0 ... x => {} - //~^ ERROR constant evaluation error - //~| non-constant path in constant expression + //~^ ERROR runtime values cannot be referenced in patterns }; } diff --git a/src/test/compile-fail/rfc1445/match-forbidden-without-eq.rs b/src/test/compile-fail/rfc1445/match-forbidden-without-eq.rs index 679be9ce219fe..ca9af78dd9936 100644 --- a/src/test/compile-fail/rfc1445/match-forbidden-without-eq.rs +++ b/src/test/compile-fail/rfc1445/match-forbidden-without-eq.rs @@ -28,7 +28,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 720e15991c059..dc220bd1cc94f 100644 --- a/src/test/compile-fail/thread-local-in-ctfe.rs +++ b/src/test/compile-fail/thread-local-in-ctfe.rs @@ -16,7 +16,6 @@ static A: u32 = 1; static B: u32 = A; //~^ ERROR thread-local statics cannot be accessed at compile-time //~| ERROR cannot refer to other statics by value -//~| WARN non-constant path in constant expression static C: &u32 = &A; //~^ ERROR thread-local statics cannot be accessed at compile-time @@ -24,7 +23,6 @@ static C: &u32 = &A; const D: u32 = A; //~^ ERROR thread-local statics cannot be accessed at compile-time //~| ERROR cannot refer to statics by value -//~| WARN non-constant path in constant expression const E: &u32 = &A; //~^ ERROR thread-local statics cannot be accessed at compile-time diff --git a/src/test/mir-opt/end_region_2.rs b/src/test/mir-opt/end_region_2.rs index 56c3e2a38a0ed..c184a1e9a287f 100644 --- a/src/test/mir-opt/end_region_2.rs +++ b/src/test/mir-opt/end_region_2.rs @@ -46,7 +46,7 @@ fn main() { // _3 = &'23_1rs _2; // StorageLive(_5); // _5 = _2; -// switchInt(move _5) -> [0u8: bb3, otherwise: bb2]; +// switchInt(move _5) -> [false: bb3, otherwise: bb2]; // } // bb2: { // _0 = (); diff --git a/src/test/mir-opt/end_region_3.rs b/src/test/mir-opt/end_region_3.rs index 8c0d56eba7828..8e336a16c130b 100644 --- a/src/test/mir-opt/end_region_3.rs +++ b/src/test/mir-opt/end_region_3.rs @@ -48,7 +48,7 @@ fn main() { // _3 = &'26_1rs _1; // StorageLive(_5); // _5 = _1; -// switchInt(move _5) -> [0u8: bb3, otherwise: bb2]; +// switchInt(move _5) -> [false: bb3, otherwise: bb2]; // } // bb2: { // _0 = (); diff --git a/src/test/mir-opt/end_region_9.rs b/src/test/mir-opt/end_region_9.rs index b313e296ac99c..b8532f91f2e96 100644 --- a/src/test/mir-opt/end_region_9.rs +++ b/src/test/mir-opt/end_region_9.rs @@ -64,7 +64,7 @@ fn main() { // bb1: { // StorageLive(_7); // _7 = _1; -// switchInt(move _7) -> [0u8: bb3, otherwise: bb2]; +// switchInt(move _7) -> [false: bb3, otherwise: bb2]; // } // bb2: { // _0 = (); diff --git a/src/test/mir-opt/end_region_cyclic.rs b/src/test/mir-opt/end_region_cyclic.rs index 37a6229febabb..4743b6a711553 100644 --- a/src/test/mir-opt/end_region_cyclic.rs +++ b/src/test/mir-opt/end_region_cyclic.rs @@ -100,7 +100,7 @@ fn query() -> bool { true } // _11 = const query() -> [return: bb5, unwind: bb2]; // } // bb5: { -// switchInt(move _11) -> [0u8: bb7, otherwise: bb6]; +// switchInt(move _11) -> [false: bb7, otherwise: bb6]; // } // bb6: { // _0 = (); diff --git a/src/test/mir-opt/issue-38669.rs b/src/test/mir-opt/issue-38669.rs index b5c188cf834a9..59916f4b60e01 100644 --- a/src/test/mir-opt/issue-38669.rs +++ b/src/test/mir-opt/issue-38669.rs @@ -31,7 +31,7 @@ fn main() { // bb1: { // StorageLive(_4); // _4 = _1; -// switchInt(move _4) -> [0u8: bb3, otherwise: bb2]; +// switchInt(move _4) -> [false: bb3, otherwise: bb2]; // } // // bb2: { diff --git a/src/test/mir-opt/match_false_edges.rs b/src/test/mir-opt/match_false_edges.rs index 1f892b0f9587a..2c732376712bf 100644 --- a/src/test/mir-opt/match_false_edges.rs +++ b/src/test/mir-opt/match_false_edges.rs @@ -92,7 +92,7 @@ fn main() { // _6 = const guard() -> [return: bb10, unwind: bb1]; // } // bb10: { // end of guard -// switchInt(move _6) -> [0u8: bb11, otherwise: bb2]; +// switchInt(move _6) -> [false: bb11, otherwise: bb2]; // } // bb11: { // to pre_binding2 // falseEdges -> [real: bb5, imaginary: bb5]; @@ -155,7 +155,7 @@ fn main() { // _6 = const guard() -> [return: bb10, unwind: bb1]; // } // bb10: { // end of guard -// switchInt(move _6) -> [0u8: bb11, otherwise: bb2]; +// switchInt(move _6) -> [false: bb11, otherwise: bb2]; // } // bb11: { // to pre_binding2 // falseEdges -> [real: bb6, imaginary: bb5]; @@ -216,7 +216,7 @@ fn main() { // _8 = const guard() -> [return: bb10, unwind: bb1]; // } // bb10: { //end of guard -// switchInt(move _8) -> [0u8: bb11, otherwise: bb2]; +// switchInt(move _8) -> [false: bb11, otherwise: bb2]; // } // bb11: { // to pre_binding2 // falseEdges -> [real: bb5, imaginary: bb5]; @@ -237,7 +237,7 @@ fn main() { // } // bb14: { // end of guard2 // StorageDead(_11); -// switchInt(move _10) -> [0u8: bb15, otherwise: bb3]; +// switchInt(move _10) -> [false: bb15, otherwise: bb3]; // } // bb15: { // to pre_binding4 // falseEdges -> [real: bb7, imaginary: bb7]; diff --git a/src/test/mir-opt/nll/region-liveness-basic.rs b/src/test/mir-opt/nll/region-liveness-basic.rs index e9834305550c3..19d733d4f6b6a 100644 --- a/src/test/mir-opt/nll/region-liveness-basic.rs +++ b/src/test/mir-opt/nll/region-liveness-basic.rs @@ -41,7 +41,7 @@ fn main() { // | Live variables on entry to bb2[0]: [_1, _3] // _2 = &'_#2r _1[_3]; // | Live variables on entry to bb2[1]: [_2] -// switchInt(const true) -> [0u8: bb4, otherwise: bb3]; +// switchInt(const true) -> [false: bb4, otherwise: bb3]; // } // END rustc.main.nll.0.mir // START rustc.main.nll.0.mir diff --git a/src/test/mir-opt/simplify_if.rs b/src/test/mir-opt/simplify_if.rs index 35786643648eb..52d5892e6560b 100644 --- a/src/test/mir-opt/simplify_if.rs +++ b/src/test/mir-opt/simplify_if.rs @@ -17,7 +17,7 @@ fn main() { // END RUST SOURCE // START rustc.main.SimplifyBranches-initial.before.mir // bb0: { -// switchInt(const false) -> [0u8: bb3, otherwise: bb2]; +// switchInt(const false) -> [false: bb3, otherwise: bb2]; // } // END rustc.main.SimplifyBranches-initial.before.mir // START rustc.main.SimplifyBranches-initial.after.mir diff --git a/src/test/compile-fail/const-index-feature-gate.rs b/src/test/run-pass/const-index-feature-gate.rs similarity index 83% rename from src/test/compile-fail/const-index-feature-gate.rs rename to src/test/run-pass/const-index-feature-gate.rs index 4f92770df289c..2e60634d15eed 100644 --- a/src/test/compile-fail/const-index-feature-gate.rs +++ b/src/test/run-pass/const-index-feature-gate.rs @@ -9,8 +9,7 @@ // except according to those terms. const ARR: [usize; 1] = [2]; -const ARR2: [i32; ARR[0]] = [5, 6]; //~ ERROR E0080 - //~| unstable +const ARR2: [i32; ARR[0]] = [5, 6]; fn main() { } diff --git a/src/test/run-pass/ctfe/chained-constants-stackoverflow.rs b/src/test/run-pass/ctfe/chained-constants-stackoverflow.rs new file mode 100644 index 0000000000000..813dd5fbb9941 --- /dev/null +++ b/src/test/run-pass/ctfe/chained-constants-stackoverflow.rs @@ -0,0 +1,364 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// https://github.com/rust-lang/rust/issues/34997 + +pub const CST_1: u32 = 0; +pub const CST_2: u32 = CST_1+1; +pub const CST_3: u32 = CST_2+1; +pub const CST_4: u32 = CST_3+1; +pub const CST_5: u32 = CST_4+1; +pub const CST_6: u32 = CST_5+1; +pub const CST_7: u32 = CST_6+1; +pub const CST_8: u32 = CST_7+1; +pub const CST_9: u32 = CST_8+1; +pub const CST_10: u32 = CST_9+1; +pub const CST_11: u32 = CST_10+1; +pub const CST_12: u32 = CST_11+1; +pub const CST_13: u32 = CST_12+1; +pub const CST_14: u32 = CST_13+1; +pub const CST_15: u32 = CST_14+1; +pub const CST_16: u32 = CST_15+1; +pub const CST_17: u32 = CST_16+1; +pub const CST_18: u32 = CST_17+1; +pub const CST_19: u32 = CST_18+1; +pub const CST_20: u32 = CST_19+1; +pub const CST_21: u32 = CST_20+1; +pub const CST_22: u32 = CST_21+1; +pub const CST_23: u32 = CST_22+1; +pub const CST_24: u32 = CST_23+1; +pub const CST_25: u32 = CST_24+1; +pub const CST_26: u32 = CST_25+1; +pub const CST_27: u32 = CST_26+1; +pub const CST_28: u32 = CST_27+1; +pub const CST_29: u32 = CST_28+1; +pub const CST_30: u32 = CST_29+1; +pub const CST_31: u32 = CST_30+1; +pub const CST_32: u32 = CST_31+1; +pub const CST_33: u32 = CST_32+1; +pub const CST_34: u32 = CST_33+1; +pub const CST_35: u32 = CST_34+1; +pub const CST_36: u32 = CST_35+1; +pub const CST_37: u32 = CST_36+1; +pub const CST_38: u32 = CST_37+1; +pub const CST_39: u32 = CST_38+1; +pub const CST_40: u32 = CST_39+1; +pub const CST_41: u32 = CST_40+1; +pub const CST_42: u32 = CST_41+1; +pub const CST_43: u32 = CST_42+1; +pub const CST_44: u32 = CST_43+1; +pub const CST_45: u32 = CST_44+1; +pub const CST_46: u32 = CST_45+1; +pub const CST_47: u32 = CST_46+1; +pub const CST_48: u32 = CST_47+1; +pub const CST_49: u32 = CST_48+1; +pub const CST_50: u32 = CST_49+1; +pub const CST_51: u32 = CST_50+1; +pub const CST_52: u32 = CST_51+1; +pub const CST_53: u32 = CST_52+1; +pub const CST_54: u32 = CST_53+1; +pub const CST_55: u32 = CST_54+1; +pub const CST_56: u32 = CST_55+1; +pub const CST_57: u32 = CST_56+1; +pub const CST_58: u32 = CST_57+1; +pub const CST_59: u32 = CST_58+1; +pub const CST_60: u32 = CST_59+1; +pub const CST_61: u32 = CST_60+1; +pub const CST_62: u32 = CST_61+1; +pub const CST_63: u32 = CST_62+1; +pub const CST_64: u32 = CST_63+1; +pub const CST_65: u32 = CST_64+1; +pub const CST_66: u32 = CST_65+1; +pub const CST_67: u32 = CST_66+1; +pub const CST_68: u32 = CST_67+1; +pub const CST_69: u32 = CST_68+1; +pub const CST_70: u32 = CST_69+1; +pub const CST_71: u32 = CST_70+1; +pub const CST_72: u32 = CST_71+1; +pub const CST_73: u32 = CST_72+1; +pub const CST_74: u32 = CST_73+1; +pub const CST_75: u32 = CST_74+1; +pub const CST_76: u32 = CST_75+1; +pub const CST_77: u32 = CST_76+1; +pub const CST_78: u32 = CST_77+1; +pub const CST_79: u32 = CST_78+1; +pub const CST_80: u32 = CST_79+1; +pub const CST_81: u32 = CST_80+1; +pub const CST_82: u32 = CST_81+1; +pub const CST_83: u32 = CST_82+1; +pub const CST_84: u32 = CST_83+1; +pub const CST_85: u32 = CST_84+1; +pub const CST_86: u32 = CST_85+1; +pub const CST_87: u32 = CST_86+1; +pub const CST_88: u32 = CST_87+1; +pub const CST_89: u32 = CST_88+1; +pub const CST_90: u32 = CST_89+1; +pub const CST_91: u32 = CST_90+1; +pub const CST_92: u32 = CST_91+1; +pub const CST_93: u32 = CST_92+1; +pub const CST_94: u32 = CST_93+1; +pub const CST_95: u32 = CST_94+1; +pub const CST_96: u32 = CST_95+1; +pub const CST_97: u32 = CST_96+1; +pub const CST_98: u32 = CST_97+1; +pub const CST_99: u32 = CST_98+1; +pub const CST_100: u32 = CST_99+1; +pub const CST_101: u32 = CST_100+1; +pub const CST_102: u32 = CST_101+1; +pub const CST_103: u32 = CST_102+1; +pub const CST_104: u32 = CST_103+1; +pub const CST_105: u32 = CST_104+1; +pub const CST_106: u32 = CST_105+1; +pub const CST_107: u32 = CST_106+1; +pub const CST_108: u32 = CST_107+1; +pub const CST_109: u32 = CST_108+1; +pub const CST_110: u32 = CST_109+1; +pub const CST_111: u32 = CST_110+1; +pub const CST_112: u32 = CST_111+1; +pub const CST_113: u32 = CST_112+1; +pub const CST_114: u32 = CST_113+1; +pub const CST_115: u32 = CST_114+1; +pub const CST_116: u32 = CST_115+1; +pub const CST_117: u32 = CST_116+1; +pub const CST_118: u32 = CST_117+1; +pub const CST_119: u32 = CST_118+1; +pub const CST_120: u32 = CST_119+1; +pub const CST_121: u32 = CST_120+1; +pub const CST_122: u32 = CST_121+1; +pub const CST_123: u32 = CST_122+1; +pub const CST_124: u32 = CST_123+1; +pub const CST_125: u32 = CST_124+1; +pub const CST_126: u32 = CST_125+1; +pub const CST_127: u32 = CST_126+1; +pub const CST_128: u32 = CST_127+1; +pub const CST_129: u32 = CST_128+1; +pub const CST_130: u32 = CST_129+1; +pub const CST_131: u32 = CST_130+1; +pub const CST_132: u32 = CST_131+1; +pub const CST_133: u32 = CST_132+1; +pub const CST_134: u32 = CST_133+1; +pub const CST_135: u32 = CST_134+1; +pub const CST_136: u32 = CST_135+1; +pub const CST_137: u32 = CST_136+1; +pub const CST_138: u32 = CST_137+1; +pub const CST_139: u32 = CST_138+1; +pub const CST_140: u32 = CST_139+1; +pub const CST_141: u32 = CST_140+1; +pub const CST_142: u32 = CST_141+1; +pub const CST_143: u32 = CST_142+1; +pub const CST_144: u32 = CST_143+1; +pub const CST_145: u32 = CST_144+1; +pub const CST_146: u32 = CST_145+1; +pub const CST_147: u32 = CST_146+1; +pub const CST_148: u32 = CST_147+1; +pub const CST_149: u32 = CST_148+1; +pub const CST_150: u32 = CST_149+1; +pub const CST_151: u32 = CST_150+1; +pub const CST_152: u32 = CST_151+1; +pub const CST_153: u32 = CST_152+1; +pub const CST_154: u32 = CST_153+1; +pub const CST_155: u32 = CST_154+1; +pub const CST_156: u32 = CST_155+1; +pub const CST_157: u32 = CST_156+1; +pub const CST_158: u32 = CST_157+1; +pub const CST_159: u32 = CST_158+1; +pub const CST_160: u32 = CST_159+1; +pub const CST_161: u32 = CST_160+1; +pub const CST_162: u32 = CST_161+1; +pub const CST_163: u32 = CST_162+1; +pub const CST_164: u32 = CST_163+1; +pub const CST_165: u32 = CST_164+1; +pub const CST_166: u32 = CST_165+1; +pub const CST_167: u32 = CST_166+1; +pub const CST_168: u32 = CST_167+1; +pub const CST_169: u32 = CST_168+1; +pub const CST_170: u32 = CST_169+1; +pub const CST_171: u32 = CST_170+1; +pub const CST_172: u32 = CST_171+1; +pub const CST_173: u32 = CST_172+1; +pub const CST_174: u32 = CST_173+1; +pub const CST_175: u32 = CST_174+1; +pub const CST_176: u32 = CST_175+1; +pub const CST_177: u32 = CST_176+1; +pub const CST_178: u32 = CST_177+1; +pub const CST_179: u32 = CST_178+1; +pub const CST_180: u32 = CST_179+1; +pub const CST_181: u32 = CST_180+1; +pub const CST_182: u32 = CST_181+1; +pub const CST_183: u32 = CST_182+1; +pub const CST_184: u32 = CST_183+1; +pub const CST_185: u32 = CST_184+1; +pub const CST_186: u32 = CST_185+1; +pub const CST_187: u32 = CST_186+1; +pub const CST_188: u32 = CST_187+1; +pub const CST_189: u32 = CST_188+1; +pub const CST_190: u32 = CST_189+1; +pub const CST_191: u32 = CST_190+1; +pub const CST_192: u32 = CST_191+1; +pub const CST_193: u32 = CST_192+1; +pub const CST_194: u32 = CST_193+1; +pub const CST_195: u32 = CST_194+1; +pub const CST_196: u32 = CST_195+1; +pub const CST_197: u32 = CST_196+1; +pub const CST_198: u32 = CST_197+1; +pub const CST_199: u32 = CST_198+1; +pub const CST_200: u32 = CST_199+1; +pub const CST_201: u32 = CST_200+1; +pub const CST_202: u32 = CST_201+1; +pub const CST_203: u32 = CST_202+1; +pub const CST_204: u32 = CST_203+1; +pub const CST_205: u32 = CST_204+1; +pub const CST_206: u32 = CST_205+1; +pub const CST_207: u32 = CST_206+1; +pub const CST_208: u32 = CST_207+1; +pub const CST_209: u32 = CST_208+1; +pub const CST_210: u32 = CST_209+1; +pub const CST_211: u32 = CST_210+1; +pub const CST_212: u32 = CST_211+1; +pub const CST_213: u32 = CST_212+1; +pub const CST_214: u32 = CST_213+1; +pub const CST_215: u32 = CST_214+1; +pub const CST_216: u32 = CST_215+1; +pub const CST_217: u32 = CST_216+1; +pub const CST_218: u32 = CST_217+1; +pub const CST_219: u32 = CST_218+1; +pub const CST_220: u32 = CST_219+1; +pub const CST_221: u32 = CST_220+1; +pub const CST_222: u32 = CST_221+1; +pub const CST_223: u32 = CST_222+1; +pub const CST_224: u32 = CST_223+1; +pub const CST_225: u32 = CST_224+1; +pub const CST_226: u32 = CST_225+1; +pub const CST_227: u32 = CST_226+1; +pub const CST_228: u32 = CST_227+1; +pub const CST_229: u32 = CST_228+1; +pub const CST_230: u32 = CST_229+1; +pub const CST_231: u32 = CST_230+1; +pub const CST_232: u32 = CST_231+1; +pub const CST_233: u32 = CST_232+1; +pub const CST_234: u32 = CST_233+1; +pub const CST_235: u32 = CST_234+1; +pub const CST_236: u32 = CST_235+1; +pub const CST_237: u32 = CST_236+1; +pub const CST_238: u32 = CST_237+1; +pub const CST_239: u32 = CST_238+1; +pub const CST_240: u32 = CST_239+1; +pub const CST_241: u32 = CST_240+1; +pub const CST_242: u32 = CST_241+1; +pub const CST_243: u32 = CST_242+1; +pub const CST_244: u32 = CST_243+1; +pub const CST_245: u32 = CST_244+1; +pub const CST_246: u32 = CST_245+1; +pub const CST_247: u32 = CST_246+1; +pub const CST_248: u32 = CST_247+1; +pub const CST_249: u32 = CST_248+1; +pub const CST_250: u32 = CST_249+1; +pub const CST_251: u32 = CST_250+1; +pub const CST_252: u32 = CST_251+1; +pub const CST_253: u32 = CST_252+1; +pub const CST_254: u32 = CST_253+1; +pub const CST_255: u32 = CST_254+1; +pub const CST_256: u32 = CST_255+1; +pub const CST_257: u32 = CST_256+1; +pub const CST_258: u32 = CST_257+1; +pub const CST_259: u32 = CST_258+1; +pub const CST_260: u32 = CST_259+1; +pub const CST_261: u32 = CST_260+1; +pub const CST_262: u32 = CST_261+1; +pub const CST_263: u32 = CST_262+1; +pub const CST_264: u32 = CST_263+1; +pub const CST_265: u32 = CST_264+1; +pub const CST_266: u32 = CST_265+1; +pub const CST_267: u32 = CST_266+1; +pub const CST_268: u32 = CST_267+1; +pub const CST_269: u32 = CST_268+1; +pub const CST_270: u32 = CST_269+1; +pub const CST_271: u32 = CST_270+1; +pub const CST_272: u32 = CST_271+1; +pub const CST_273: u32 = CST_272+1; +pub const CST_274: u32 = CST_273+1; +pub const CST_275: u32 = CST_274+1; +pub const CST_276: u32 = CST_275+1; +pub const CST_277: u32 = CST_276+1; +pub const CST_278: u32 = CST_277+1; +pub const CST_279: u32 = CST_278+1; +pub const CST_280: u32 = CST_279+1; +pub const CST_281: u32 = CST_280+1; +pub const CST_282: u32 = CST_281+1; +pub const CST_283: u32 = CST_282+1; +pub const CST_284: u32 = CST_283+1; +pub const CST_285: u32 = CST_284+1; +pub const CST_286: u32 = CST_285+1; +pub const CST_287: u32 = CST_286+1; +pub const CST_288: u32 = CST_287+1; +pub const CST_289: u32 = CST_288+1; +pub const CST_290: u32 = CST_289+1; +pub const CST_291: u32 = CST_290+1; +pub const CST_292: u32 = CST_291+1; +pub const CST_293: u32 = CST_292+1; +pub const CST_294: u32 = CST_293+1; +pub const CST_295: u32 = CST_294+1; +pub const CST_296: u32 = CST_295+1; +pub const CST_297: u32 = CST_296+1; +pub const CST_298: u32 = CST_297+1; +pub const CST_299: u32 = CST_298+1; +pub const CST_300: u32 = CST_299+1; +pub const CST_301: u32 = CST_300+1; +pub const CST_302: u32 = CST_301+1; +pub const CST_303: u32 = CST_302+1; +pub const CST_304: u32 = CST_303+1; +pub const CST_305: u32 = CST_304+1; +pub const CST_306: u32 = CST_305+1; +pub const CST_307: u32 = CST_306+1; +pub const CST_308: u32 = CST_307+1; +pub const CST_309: u32 = CST_308+1; +pub const CST_310: u32 = CST_309+1; +pub const CST_311: u32 = CST_310+1; +pub const CST_312: u32 = CST_311+1; +pub const CST_313: u32 = CST_312+1; +pub const CST_314: u32 = CST_313+1; +pub const CST_315: u32 = CST_314+1; +pub const CST_316: u32 = CST_315+1; +pub const CST_317: u32 = CST_316+1; +pub const CST_318: u32 = CST_317+1; +pub const CST_319: u32 = CST_318+1; +pub const CST_320: u32 = CST_319+1; +pub const CST_321: u32 = CST_320+1; +pub const CST_322: u32 = CST_321+1; +pub const CST_323: u32 = CST_322+1; +pub const CST_324: u32 = CST_323+1; +pub const CST_325: u32 = CST_324+1; +pub const CST_326: u32 = CST_325+1; +pub const CST_327: u32 = CST_326+1; +pub const CST_328: u32 = CST_327+1; +pub const CST_329: u32 = CST_328+1; +pub const CST_330: u32 = CST_329+1; +pub const CST_331: u32 = CST_330+1; +pub const CST_332: u32 = CST_331+1; +pub const CST_333: u32 = CST_332+1; +pub const CST_334: u32 = CST_333+1; +pub const CST_335: u32 = CST_334+1; +pub const CST_336: u32 = CST_335+1; +pub const CST_337: u32 = CST_336+1; +pub const CST_338: u32 = CST_337+1; +pub const CST_339: u32 = CST_338+1; +pub const CST_340: u32 = CST_339+1; +pub const CST_341: u32 = CST_340+1; +pub const CST_342: u32 = CST_341+1; +pub const CST_343: u32 = CST_342+1; +pub const CST_344: u32 = CST_343+1; +pub const CST_345: u32 = CST_344+1; +pub const CST_346: u32 = CST_345+1; +pub const CST_347: u32 = CST_346+1; +pub const CST_348: u32 = CST_347+1; +pub const CST_349: u32 = CST_348+1; +pub const CST_350: u32 = CST_349+1; + +fn main() {} diff --git a/src/test/run-pass/ctfe/deref_in_pattern.rs b/src/test/run-pass/ctfe/deref_in_pattern.rs new file mode 100644 index 0000000000000..4ccfa0338f393 --- /dev/null +++ b/src/test/run-pass/ctfe/deref_in_pattern.rs @@ -0,0 +1,20 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// https://github.com/rust-lang/rust/issues/25574 + +const A: [u8; 4] = *b"fooo"; + +fn main() { + match *b"xxxx" { + A => {}, + _ => {} + } +} diff --git a/src/test/run-pass/ctfe/issue-broken-mir.rs b/src/test/run-pass/ctfe/issue-broken-mir.rs new file mode 100644 index 0000000000000..6ed0c7c0d5dc4 --- /dev/null +++ b/src/test/run-pass/ctfe/issue-broken-mir.rs @@ -0,0 +1,18 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// https://github.com/rust-lang/rust/issues/27918 + +fn main() { + match b" " { + b"1234" => {}, + _ => {}, + } +} diff --git a/src/test/run-pass/ctfe/match-const-fn-structs.rs b/src/test/run-pass/ctfe/match-const-fn-structs.rs new file mode 100644 index 0000000000000..0bb253d1a6455 --- /dev/null +++ b/src/test/run-pass/ctfe/match-const-fn-structs.rs @@ -0,0 +1,31 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// https://github.com/rust-lang/rust/issues/46114 + +#![feature(const_fn)] + +#[derive(Eq, PartialEq)] +struct A { value: u32 } + +const fn new(value: u32) -> A { + A { value } +} + +const A_1: A = new(1); +const A_2: A = new(2); + +fn main() { + let a_str = match new(42) { + A_1 => "A 1", + A_2 => "A 2", + _ => "Unknown A", + }; +} diff --git a/src/test/run-pass/ctfe/non-scalar-cast.rs b/src/test/run-pass/ctfe/non-scalar-cast.rs new file mode 100644 index 0000000000000..ff4474f47c9f3 --- /dev/null +++ b/src/test/run-pass/ctfe/non-scalar-cast.rs @@ -0,0 +1,17 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// https://github.com/rust-lang/rust/issues/37448 + +fn main() { + struct A; + const FOO: &A = &(A as A); + let _x = FOO; +} diff --git a/src/test/run-pass/ctfe/references.rs b/src/test/run-pass/ctfe/references.rs new file mode 100644 index 0000000000000..ad7dbeb79c721 --- /dev/null +++ b/src/test/run-pass/ctfe/references.rs @@ -0,0 +1,34 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +const FOO: &[u8] = b"foo"; +const BAR: &[u8] = &[1, 2, 3]; + +const BOO: &i32 = &42; + +fn main() { + match &[1u8, 2, 3] as &[u8] { + FOO => panic!("a"), + BAR => println!("b"), + _ => panic!("c"), + } + + match b"foo" as &[u8] { + FOO => println!("a"), + BAR => panic!("b"), + _ => panic!("c"), + } + + match &43 { + &42 => panic!(), + BOO => panic!(), + _ => println!("d"), + } +} diff --git a/src/test/run-pass/ctfe/return-in-const-fn.rs b/src/test/run-pass/ctfe/return-in-const-fn.rs new file mode 100644 index 0000000000000..d57d3bcb49aa8 --- /dev/null +++ b/src/test/run-pass/ctfe/return-in-const-fn.rs @@ -0,0 +1,19 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// https://github.com/rust-lang/rust/issues/43754 + +#![feature(const_fn)] +const fn foo(x: usize) -> usize { + return x; +} +fn main() { + [0; foo(2)]; +} diff --git a/src/test/run-pass/ctfe/tuple-struct-constructors.rs b/src/test/run-pass/ctfe/tuple-struct-constructors.rs new file mode 100644 index 0000000000000..ecc5d37663679 --- /dev/null +++ b/src/test/run-pass/ctfe/tuple-struct-constructors.rs @@ -0,0 +1,20 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// https://github.com/rust-lang/rust/issues/41898 + +#![feature(nonzero, const_fn)] +extern crate core; +use core::nonzero::NonZero; + +fn main() { + const FOO: NonZero = unsafe { NonZero::new_unchecked(2) }; + if let FOO = FOO {} +} diff --git a/src/test/run-pass/float-int-invalid-const-cast.rs b/src/test/run-pass/float-int-invalid-const-cast.rs new file mode 100644 index 0000000000000..80ab12482cbb7 --- /dev/null +++ b/src/test/run-pass/float-int-invalid-const-cast.rs @@ -0,0 +1,61 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(i128_type)] +#![deny(const_err)] + +use std::{f32, f64}; + +// Forces evaluation of constants, triggering hard error +fn force(_: T) {} + +fn main() { + { const X: u16 = -1. as u16; force(X); } + { const X: u128 = -100. as u128; force(X); } + + { const X: i8 = f32::NAN as i8; force(X); } + { const X: i32 = f32::NAN as i32; force(X); } + { const X: u64 = f32::NAN as u64; force(X); } + { const X: u128 = f32::NAN as u128; force(X); } + + { const X: i8 = f32::INFINITY as i8; force(X); } + { const X: u32 = f32::INFINITY as u32; force(X); } + { const X: i128 = f32::INFINITY as i128; force(X); } + { const X: u128 = f32::INFINITY as u128; force(X); } + + { const X: u8 = f32::NEG_INFINITY as u8; force(X); } + { const X: u16 = f32::NEG_INFINITY as u16; force(X); } + { const X: i64 = f32::NEG_INFINITY as i64; force(X); } + { const X: i128 = f32::NEG_INFINITY as i128; force(X); } + + { const X: i8 = f64::NAN as i8; force(X); } + { const X: i32 = f64::NAN as i32; force(X); } + { const X: u64 = f64::NAN as u64; force(X); } + { const X: u128 = f64::NAN as u128; force(X); } + + { const X: i8 = f64::INFINITY as i8; force(X); } + { const X: u32 = f64::INFINITY as u32; force(X); } + { const X: i128 = f64::INFINITY as i128; force(X); } + { const X: u128 = f64::INFINITY as u128; force(X); } + + { const X: u8 = f64::NEG_INFINITY as u8; force(X); } + { const X: u16 = f64::NEG_INFINITY as u16; force(X); } + { const X: i64 = f64::NEG_INFINITY as i64; force(X); } + { const X: i128 = f64::NEG_INFINITY as i128; force(X); } + + { const X: u8 = 256. as u8; force(X); } + { const X: i8 = -129. as i8; force(X); } + { const X: i8 = 128. as i8; force(X); } + { const X: i32 = 2147483648. as i32; force(X); } + { const X: i32 = -2147483904. as i32; force(X); } + { const X: u32 = 4294967296. as u32; force(X); } + { const X: u128 = 1e40 as u128; force(X); } + { const X: i128 = 1e40 as i128; force(X); } +} diff --git a/src/test/ui/const-eval-overflow-2.rs b/src/test/ui/const-eval-overflow-2.rs index 6b7f631ff4c8d..885edb55ed86c 100644 --- a/src/test/ui/const-eval-overflow-2.rs +++ b/src/test/ui/const-eval-overflow-2.rs @@ -19,8 +19,7 @@ use std::{u8, u16, u32, u64, usize}; const NEG_128: i8 = -128; const NEG_NEG_128: i8 = -NEG_128; -//~^ ERROR constant evaluation error -//~| attempt to negate with overflow +//~^ ERROR E0080 fn main() { match -128i8 { diff --git a/src/test/ui/const-eval-overflow-2.stderr b/src/test/ui/const-eval-overflow-2.stderr index a9d29d0107192..e92754fc13582 100644 --- a/src/test/ui/const-eval-overflow-2.stderr +++ b/src/test/ui/const-eval-overflow-2.stderr @@ -1,13 +1,13 @@ error[E0080]: constant evaluation error - --> $DIR/const-eval-overflow-2.rs:21:25 + --> $DIR/const-eval-overflow-2.rs:21:1 | 21 | const NEG_NEG_128: i8 = -NEG_128; - | ^^^^^^^^ attempt to negate with overflow + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ miri failed: 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 | -27 | NEG_NEG_128 => println!("A"), +26 | NEG_NEG_128 => println!("A"), | ^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/const-eval-overflow-4.rs b/src/test/ui/const-eval-overflow-4.rs index 4423fdec33a85..a1b90f623cdae 100644 --- a/src/test/ui/const-eval-overflow-4.rs +++ b/src/test/ui/const-eval-overflow-4.rs @@ -21,8 +21,7 @@ use std::{u8, u16, u32, u64, usize}; const A_I8_T : [u32; (i8::MAX as i8 + 1i8) as usize] - //~^ ERROR constant evaluation error - //~| WARNING constant evaluation error + //~^ ERROR E0080 = [0; (i8::MAX as usize) + 1]; fn main() { diff --git a/src/test/ui/const-eval-overflow-4.stderr b/src/test/ui/const-eval-overflow-4.stderr index 98c6ae1b9bcac..2f8bf78758a0f 100644 --- a/src/test/ui/const-eval-overflow-4.stderr +++ b/src/test/ui/const-eval-overflow-4.stderr @@ -1,16 +1,8 @@ -warning: constant evaluation error: attempt to add with overflow - --> $DIR/const-eval-overflow-4.rs:23:13 - | -23 | : [u32; (i8::MAX as i8 + 1i8) as usize] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: #[warn(const_err)] on by default - error[E0080]: constant evaluation error --> $DIR/const-eval-overflow-4.rs:23:13 | 23 | : [u32; (i8::MAX as i8 + 1i8) as usize] - | ^^^^^^^^^^^^^^^^^^^^^ attempt to add with overflow + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ miri failed: attempt to add with overflow error: aborting due to previous error diff --git a/src/test/ui/const-eval/issue-43197.rs b/src/test/ui/const-eval/issue-43197.rs index 85ab2a0052164..e87b2caa49f90 100644 --- a/src/test/ui/const-eval/issue-43197.rs +++ b/src/test/ui/const-eval/issue-43197.rs @@ -16,8 +16,8 @@ const fn foo(x: u32) -> u32 { fn main() { const X: u32 = 0-1; //~ ERROR constant evaluation error - //~^ WARN constant evaluation error const Y: u32 = foo(0-1); //~ ERROR constant evaluation error - //~^ WARN constant evaluation error println!("{} {}", X, Y); + //~^ 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 82baab620ffab..9ba9c43462c53 100644 --- a/src/test/ui/const-eval/issue-43197.stderr +++ b/src/test/ui/const-eval/issue-43197.stderr @@ -1,28 +1,50 @@ -warning: constant evaluation error: attempt to subtract with overflow - --> $DIR/issue-43197.rs:18:20 +error[E0080]: constant evaluation error + --> $DIR/issue-43197.rs:20:23 | -18 | const X: u32 = 0-1; //~ ERROR constant evaluation error - | ^^^ +20 | println!("{} {}", X, Y); + | ^ attempt to subtract with overflow + | +note: inside call to main + --> $DIR/issue-43197.rs:20:23 + | +20 | println!("{} {}", X, Y); + | ^ + +error[E0080]: constant evaluation error + --> $DIR/issue-43197.rs:20:26 + | +20 | println!("{} {}", X, Y); + | ^ attempt to subtract with overflow + | +note: inside call to main + --> $DIR/issue-43197.rs:20:26 | - = note: #[warn(const_err)] on by default +20 | println!("{} {}", X, Y); + | ^ -warning: constant evaluation error: attempt to subtract with overflow - --> $DIR/issue-43197.rs:20:20 +error[E0080]: constant evaluation error + --> $DIR/issue-43197.rs:19:24 + | +19 | const Y: u32 = foo(0-1); //~ ERROR constant evaluation error + | ^^^ attempt to subtract with overflow | -20 | const Y: u32 = foo(0-1); //~ ERROR constant evaluation error - | ^^^^^^^^ +note: inside call to main::Y + --> $DIR/issue-43197.rs:19:5 + | +19 | const Y: u32 = foo(0-1); //~ ERROR constant evaluation error + | ^^^^^^^^^^^^^^^^^^^^^^^^ error[E0080]: constant evaluation error --> $DIR/issue-43197.rs:18:20 | 18 | const X: u32 = 0-1; //~ ERROR constant evaluation error | ^^^ attempt to subtract with overflow - -error[E0080]: constant evaluation error - --> $DIR/issue-43197.rs:20:24 | -20 | const Y: u32 = foo(0-1); //~ ERROR constant evaluation error - | ^^^ attempt to subtract with overflow +note: inside call to main::X + --> $DIR/issue-43197.rs:18:5 + | +18 | const X: u32 = 0-1; //~ ERROR constant evaluation error + | ^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors diff --git a/src/test/ui/const-expr-addr-operator.rs b/src/test/ui/const-expr-addr-operator.rs index 24d4457f01d70..bfd6a4090649b 100644 --- a/src/test/ui/const-expr-addr-operator.rs +++ b/src/test/ui/const-expr-addr-operator.rs @@ -9,10 +9,11 @@ // except according to those terms. // Encountered while testing #44614. +// must-compile-successfully pub fn main() { // Constant of generic type (int) - const X: &'static u32 = &22; //~ ERROR constant evaluation error + const X: &'static u32 = &22; assert_eq!(0, match &22 { X => 0, _ => 1, diff --git a/src/test/ui/const-expr-addr-operator.stderr b/src/test/ui/const-expr-addr-operator.stderr index f6587c703bd7f..e69de29bb2d1d 100644 --- a/src/test/ui/const-expr-addr-operator.stderr +++ b/src/test/ui/const-expr-addr-operator.stderr @@ -1,14 +0,0 @@ -error[E0080]: constant evaluation error - --> $DIR/const-expr-addr-operator.rs:15:29 - | -15 | 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 - | -17 | X => 0, - | ^ - -error: aborting due to previous error - diff --git a/src/test/ui/const-fn-error.rs b/src/test/ui/const-fn-error.rs index ac1c2fe5432de..dc1526a7079d4 100644 --- a/src/test/ui/const-fn-error.rs +++ b/src/test/ui/const-fn-error.rs @@ -13,17 +13,18 @@ const X : usize = 2; const fn f(x: usize) -> usize { - let mut sum = 0; //~ ERROR blocks in constant functions are limited - for i in 0..x { //~ ERROR calls in constant functions - //~| ERROR constant function contains unimplemented + let mut sum = 0; + //~^ ERROR E0016 + for i in 0..x { + //~^ ERROR E0015 + //~| ERROR E0019 sum += i; } - sum //~ ERROR E0080 - //~| non-constant path in constant + sum } #[allow(unused_variables)] fn main() { let a : [i32; f(X)]; - //~^ WARNING constant evaluation error: non-constant path + //~^ ERROR E0080 } diff --git a/src/test/ui/const-fn-error.stderr b/src/test/ui/const-fn-error.stderr index 0e275e78fc68c..2d078a2ebfec0 100644 --- a/src/test/ui/const-fn-error.stderr +++ b/src/test/ui/const-fn-error.stderr @@ -1,46 +1,34 @@ -warning: constant evaluation error: non-constant path in constant expression - --> $DIR/const-fn-error.rs:27:19 - | -27 | 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 | -16 | let mut sum = 0; //~ ERROR blocks in constant functions are limited +16 | 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:5 + --> $DIR/const-fn-error.rs:18:5 | -17 | / for i in 0..x { //~ ERROR calls in constant functions -18 | | //~| ERROR constant function contains unimplemented -19 | | sum += i; -20 | | } +18 | / for i in 0..x { +19 | | //~^ ERROR E0015 +20 | | //~| ERROR E0019 +21 | | sum += i; +22 | | } | |_____^ error[E0019]: constant function contains unimplemented expression type - --> $DIR/const-fn-error.rs:17:5 + --> $DIR/const-fn-error.rs:18:5 | -17 | / for i in 0..x { //~ ERROR calls in constant functions -18 | | //~| ERROR constant function contains unimplemented -19 | | sum += i; -20 | | } +18 | / for i in 0..x { +19 | | //~^ ERROR E0015 +20 | | //~| ERROR E0019 +21 | | sum += i; +22 | | } | |_____^ error[E0080]: constant evaluation error - --> $DIR/const-fn-error.rs:21:5 - | -21 | sum //~ ERROR E0080 - | ^^^ non-constant path in constant expression - | -note: for constant expression here - --> $DIR/const-fn-error.rs:27:13 + --> $DIR/const-fn-error.rs:28:19 | -27 | let a : [i32; f(X)]; - | ^^^^^^^^^^^ +28 | let a : [i32; f(X)]; + | ^^^^ miri failed: machine error: Cannot evaluate within constants: "calling non-const fn `>::into_iter`" error: aborting due to 4 previous errors diff --git a/src/test/ui/const-len-underflow-separate-spans.rs b/src/test/ui/const-len-underflow-separate-spans.rs index 823cc988947cb..35e3ba9ce0945 100644 --- a/src/test/ui/const-len-underflow-separate-spans.rs +++ b/src/test/ui/const-len-underflow-separate-spans.rs @@ -15,9 +15,9 @@ const ONE: usize = 1; const TWO: usize = 2; const LEN: usize = ONE - TWO; -//~^ ERROR constant evaluation error [E0080] -//~| WARN attempt to subtract with overflow +//~^ ERROR E0080 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 6e6c2130e1ccd..6b2789c145444 100644 --- a/src/test/ui/const-len-underflow-separate-spans.stderr +++ b/src/test/ui/const-len-underflow-separate-spans.stderr @@ -1,22 +1,20 @@ -warning: constant evaluation error: attempt to subtract with overflow - --> $DIR/const-len-underflow-separate-spans.rs:17:20 - | -17 | const LEN: usize = ONE - TWO; - | ^^^^^^^^^ - | - = note: #[warn(const_err)] on by default - error[E0080]: constant evaluation error --> $DIR/const-len-underflow-separate-spans.rs:17:20 | 17 | const LEN: usize = ONE - TWO; | ^^^^^^^^^ attempt to subtract with overflow | -note: for constant expression here - --> $DIR/const-len-underflow-separate-spans.rs:22:12 +note: inside call to LEN + --> $DIR/const-len-underflow-separate-spans.rs:17:1 + | +17 | const LEN: usize = ONE - TWO; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0080]: constant evaluation error + --> $DIR/const-len-underflow-separate-spans.rs:21:17 | -22 | let a: [i8; LEN] = unimplemented!(); - | ^^^^^^^^^ +21 | let a: [i8; LEN] = unimplemented!(); + | ^^^ miri failed: attempt to subtract with overflow -error: aborting due to previous error +error: aborting due to 2 previous errors diff --git a/src/test/ui/const-pattern-not-const-evaluable.rs b/src/test/ui/const-pattern-not-const-evaluable.rs index 263c0bdc64c6f..09b24d1ffa208 100644 --- a/src/test/ui/const-pattern-not-const-evaluable.rs +++ b/src/test/ui/const-pattern-not-const-evaluable.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// must-compile-successfully + #![feature(const_fn)] #[derive(PartialEq, Eq)] @@ -20,8 +22,6 @@ use Cake::*; struct Pair(A, B); const BOO: Pair = Pair(Marmor, BlackForest); -//~^ ERROR: constant evaluation error [E0080] -//~| unimplemented constant expression: tuple struct constructors const FOO: Cake = BOO.1; const fn foo() -> Cake { diff --git a/src/test/ui/const-pattern-not-const-evaluable.stderr b/src/test/ui/const-pattern-not-const-evaluable.stderr index 5441937e4dd60..e69de29bb2d1d 100644 --- a/src/test/ui/const-pattern-not-const-evaluable.stderr +++ b/src/test/ui/const-pattern-not-const-evaluable.stderr @@ -1,14 +0,0 @@ -error[E0080]: constant evaluation error - --> $DIR/const-pattern-not-const-evaluable.rs:22:31 - | -22 | const BOO: Pair = Pair(Marmor, BlackForest); - | ^^^^ unimplemented constant expression: tuple struct constructors - | -note: for pattern here - --> $DIR/const-pattern-not-const-evaluable.rs:37:9 - | -37 | FOO => println!("hi"), - | ^^^ - -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 0d61878cd8073..eb5f746774cf1 100644 --- a/src/test/ui/feature-gate-const-indexing.rs +++ b/src/test/ui/feature-gate-const-indexing.rs @@ -8,10 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// must-compile-successfully fn main() { const ARR: [i32; 6] = [42, 43, 44, 45, 46, 47]; const IDX: usize = 3; const VAL: i32 = ARR[IDX]; - const BLUB: [i32; (ARR[0] - 41) as usize] = [5]; //~ ERROR constant evaluation error + const BLUB: [i32; (ARR[0] - 41) as usize] = [5]; } diff --git a/src/test/ui/feature-gate-const-indexing.stderr b/src/test/ui/feature-gate-const-indexing.stderr index bc4b687800d75..e69de29bb2d1d 100644 --- a/src/test/ui/feature-gate-const-indexing.stderr +++ b/src/test/ui/feature-gate-const-indexing.stderr @@ -1,8 +0,0 @@ -error[E0080]: constant evaluation error - --> $DIR/feature-gate-const-indexing.rs:16:24 - | -16 | 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 - diff --git a/src/test/ui/infinite-recursion-const-fn.rs b/src/test/ui/infinite-recursion-const-fn.rs new file mode 100644 index 0000000000000..05e40abdc0f2f --- /dev/null +++ b/src/test/ui/infinite-recursion-const-fn.rs @@ -0,0 +1,18 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//https://github.com/rust-lang/rust/issues/31364 + +#![feature(const_fn)] +const fn a() -> usize { b() } +const fn b() -> usize { a() } +const ARR: [i32; a()] = [5; 6]; //~ ERROR constant evaluation error + +fn main(){} diff --git a/src/test/ui/infinite-recursion-const-fn.stderr b/src/test/ui/infinite-recursion-const-fn.stderr new file mode 100644 index 0000000000000..ce1b35aac7ecc --- /dev/null +++ b/src/test/ui/infinite-recursion-const-fn.stderr @@ -0,0 +1,8 @@ +error[E0080]: constant evaluation error + --> $DIR/infinite-recursion-const-fn.rs:16:18 + | +16 | const ARR: [i32; a()] = [5; 6]; //~ ERROR constant evaluation error + | ^^^ miri failed: reached the configured maximum number of stack frames + +error: aborting due to previous error + diff --git a/src/test/ui/issue-38875/issue_38875.rs b/src/test/ui/issue-38875/issue_38875.rs index 42e3c05a38c7e..24cd20a84a9fe 100644 --- a/src/test/ui/issue-38875/issue_38875.rs +++ b/src/test/ui/issue-38875/issue_38875.rs @@ -9,6 +9,7 @@ // except according to those terms. // aux-build:issue_38875_b.rs +// must-compile-successfully extern crate issue_38875_b; diff --git a/src/test/ui/issue-38875/issue_38875.stderr b/src/test/ui/issue-38875/issue_38875.stderr index d49741f25b9f5..e69de29bb2d1d 100644 --- a/src/test/ui/issue-38875/issue_38875.stderr +++ b/src/test/ui/issue-38875/issue_38875.stderr @@ -1,14 +0,0 @@ -error[E0080]: constant evaluation error - --> $DIR/auxiliary/issue_38875_b.rs:11:24 - | -11 | pub const FOO: usize = *&0; - | ^^^ unimplemented constant expression: deref operation - | -note: for constant expression here - --> $DIR/issue_38875.rs:16:22 - | -16 | let test_x = [0; issue_38875_b::FOO]; - | ^^^^^^^^^^^^^^^^^^ - -error: aborting due to previous error - diff --git a/src/test/ui/union/union-const-eval.rs b/src/test/ui/union/union-const-eval.rs index a4c969ba20c46..aeafb45e6a552 100644 --- a/src/test/ui/union/union-const-eval.rs +++ b/src/test/ui/union/union-const-eval.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// must-compile-successfully + union U { a: usize, b: usize, @@ -16,9 +18,6 @@ union U { const C: U = U { a: 10 }; fn main() { - unsafe { - let a: [u8; C.a]; // OK - let b: [u8; C.b]; //~ ERROR constant evaluation error - //~| WARNING constant evaluation error - } + let a: [u8; unsafe { C.a }]; + let b: [u8; unsafe { C.b }]; } diff --git a/src/test/ui/union/union-const-eval.stderr b/src/test/ui/union/union-const-eval.stderr index 3c98b5cdc6cdc..e69de29bb2d1d 100644 --- a/src/test/ui/union/union-const-eval.stderr +++ b/src/test/ui/union/union-const-eval.stderr @@ -1,16 +0,0 @@ -warning: constant evaluation error: nonexistent struct field - --> $DIR/union-const-eval.rs:21:21 - | -21 | 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 - | -21 | let b: [u8; C.b]; //~ ERROR constant evaluation error - | ^^^ nonexistent struct field - -error: aborting due to previous error -